diff --git a/.changeset/config.json b/.changeset/config.json index 334610b5f..ed771077f 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -11,7 +11,12 @@ "sample-action-client", "vscode-using-workspace-api" ], - ["yeoman-ui", "yeoman-ui-frontend"] + ["yeoman-ui", "yeoman-ui-frontend"], + [ + "@vscode-logging/logger", + "@vscode-logging/types", + "@vscode-logging/wrapper" + ] ], "linked": [], "access": "restricted", diff --git a/.eslintrc.js b/.eslintrc.js index c1f6afe97..94c29c203 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -85,6 +85,17 @@ module.exports = { "eslint-comments/require-description": "off", }, }, + { + // vscode-logging was integrated with these rules relaxed for the migrated code. + // TODO: clean up violations and tighten incrementally. + files: ["projects/vscode-logging/**"], + rules: { + "eslint-comments/require-description": "off", + // base rule for .js sources/tests (the @typescript-eslint variant is + // handled in the projects/vscode-logging/**/*.ts override below) + "no-unused-vars": "off", + }, + }, { // Use this package's tsconfig so type-aware linting sees its newer (@types/node) typings. files: ["packages/vsix-zst/**/*.ts"], @@ -178,6 +189,19 @@ module.exports = { "@typescript-eslint/no-misused-promises": "off", }, }, + { + // Additional TypeScript rules for vscode-logging packages. + // TODO: clean up violations and tighten incrementally. + files: ["projects/vscode-logging/**/*.ts"], + rules: { + // public API surface (api.d.ts) intentionally exposes `any` + "@typescript-eslint/no-explicit-any": "off", + // chai assertions (e.g. expect(x).to.be.true) read as unused expressions + "@typescript-eslint/no-unused-expressions": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-unsafe-argument": "off", + }, + }, { // For Vue frontend sub-packages. files: ["*.vue"], diff --git a/.gitignore b/.gitignore index a67ac791f..8c34a9f35 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,9 @@ coverage # nyc test coverage .nyc_output +# logger test log output +.log-out + # Dependency directories node_modules/ diff --git a/package.json b/package.json index 39adc7623..a8a2e02d6 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "packages/*", "projects/*/packages/*", "projects/*", + "projects/*/examples/*", "examples/*" ] }, @@ -42,7 +43,7 @@ "@types/deep-equal-in-any-order": "1.0.4", "@types/lodash": "4.14.177", "@types/mocha": "10.0.7", - "@types/node": "16.11.10", + "@types/node": "22.20.1", "@types/sinon": "17.0.4", "@types/vscode": "1.75.0", "@typescript-eslint/eslint-plugin": "8.60.1", diff --git a/packages/app-studio-toolkit/src/devspace-manager/landscape/landscape.ts b/packages/app-studio-toolkit/src/devspace-manager/landscape/landscape.ts index 8ab469229..0a23f2982 100644 --- a/packages/app-studio-toolkit/src/devspace-manager/landscape/landscape.ts +++ b/packages/app-studio-toolkit/src/devspace-manager/landscape/landscape.ts @@ -20,7 +20,7 @@ const LBL_ADD_LANDSCAPE = "Add another landscape"; const DEFAULT_REFRESH_RATE = 10 * 1000; // 10 sec default const DEFAULT_TIMEOUT = 2 * 60 * 1000; // 2 min default -const autoRefreshTimerArray: NodeJS.Timer[] = []; +const autoRefreshTimerArray: NodeJS.Timeout[] = []; export function clearAutoRefreshTimers(): void { autoRefreshTimerArray.forEach((interval) => { @@ -34,7 +34,7 @@ export function autoRefresh( timeOut = DEFAULT_TIMEOUT ): void { let refreshedTime = 0; - const refreshInterval: NodeJS.Timer = setInterval(() => { + const refreshInterval: NodeJS.Timeout = setInterval(() => { getLandscapes() .then((landscapes) => { if (refreshedTime < timeOut && !isEmpty(landscapes)) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 555e45833..12959a65c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,10 +16,10 @@ importers: devDependencies: '@changesets/cli': specifier: 2.31.1 - version: 2.31.1(@types/node@16.11.10) + version: 2.31.1(@types/node@22.20.1) '@commitlint/cli': specifier: 19.5.0 - version: 19.5.0(@types/node@16.11.10)(typescript@5.7.3) + version: 19.5.0(@types/node@22.20.1)(typescript@5.7.3) '@commitlint/config-conventional': specifier: 19.5.0 version: 19.5.0 @@ -42,8 +42,8 @@ importers: specifier: 10.0.7 version: 10.0.7 '@types/node': - specifier: 16.11.10 - version: 16.11.10 + specifier: 22.20.1 + version: 22.20.1 '@types/sinon': specifier: 17.0.4 version: 17.0.4 @@ -378,7 +378,7 @@ importers: version: 2.0.8 jest-mock-vscode: specifier: 0.1.6 - version: 0.1.6(jest@29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))) + version: 0.1.6(jest@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))) proxyquire: specifier: 2.1.3 version: 2.1.3 @@ -568,7 +568,109 @@ importers: version: 2.25.3(@typescript-eslint/parser@8.60.1(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1) jest: specifier: 27.5.1 - version: 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10) + version: 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10) + + projects/vscode-logging/examples/extension: + dependencies: + '@vscode-logging/library-example': + specifier: workspace:* + version: link:../library + '@vscode-logging/logger': + specifier: workspace:* + version: link:../../packages/logger + devDependencies: + '@types/vscode': + specifier: ^1.32.0 + version: 1.75.0 + + projects/vscode-logging/examples/extension-wrapper: + dependencies: + '@vscode-logging/wrapper': + specifier: workspace:* + version: link:../../packages/wrapper + devDependencies: + '@types/vscode': + specifier: ^1.52.0 + version: 1.75.0 + '@vscode-logging/types': + specifier: workspace:* + version: link:../../packages/types + + projects/vscode-logging/examples/library: + devDependencies: + '@vscode-logging/types': + specifier: workspace:* + version: link:../../packages/types + + projects/vscode-logging/packages/logger: + dependencies: + '@vscode-logging/types': + specifier: workspace:* + version: link:../types + fast-safe-stringify: + specifier: 2.1.1 + version: 2.1.1 + fs-extra: + specifier: 11.2.0 + version: 11.2.0 + lodash: + specifier: 4.18.1 + version: 4.18.1 + stacktrace-js: + specifier: 2.0.2 + version: 2.0.2 + streamroller: + specifier: 3.1.5 + version: 3.1.5 + triple-beam: + specifier: 1.4.1 + version: 1.4.1 + winston: + specifier: 3.11.0 + version: 3.11.0 + winston-transport: + specifier: 4.6.0 + version: 4.6.0 + devDependencies: + '@types/fs-extra': + specifier: 11.0.4 + version: 11.0.4 + '@types/vscode': + specifier: 1.51.0 + version: 1.51.0 + chai-exclude: + specifier: 2.1.0 + version: 2.1.0(chai@4.5.0) + proxyquire: + specifier: 2.1.3 + version: 2.1.3 + sinon: + specifier: 17.0.1 + version: 17.0.1 + sinon-chai: + specifier: 3.7.0 + version: 3.7.0(chai@4.5.0)(sinon@17.0.1) + + projects/vscode-logging/packages/types: {} + + projects/vscode-logging/packages/wrapper: + dependencies: + '@vscode-logging/logger': + specifier: workspace:* + version: link:../logger + '@vscode-logging/types': + specifier: workspace:* + version: link:../types + devDependencies: + '@types/lodash': + specifier: 4.14.202 + version: 4.14.202 + '@types/vscode': + specifier: 1.52.0 + version: 1.52.0 + lodash: + specifier: 4.18.1 + version: 4.18.1 projects/vscode-mta-tools: dependencies: @@ -582,8 +684,8 @@ importers: specifier: 1.2.15 version: 1.2.15 '@vscode-logging/logger': - specifier: 1.2.3 - version: 1.2.3 + specifier: workspace:* + version: link:../vscode-logging/packages/logger comment-json: specifier: 4.1.1 version: 4.1.1 @@ -807,7 +909,7 @@ importers: version: 3.4.13(typescript@5.7.3) '@sap-devx/inquirer-gui-auto-complete-plugin': specifier: 3.4.13 - version: 3.4.13(typescript@5.7.3)(vite@4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0)) + version: 3.4.13(typescript@5.7.3)(vite@4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0)) '@sap-devx/inquirer-gui-file-browser-plugin': specifier: 3.4.13 version: 3.4.13(typescript@5.7.3) @@ -862,7 +964,7 @@ importers: version: 7.11.5(@babel/core@7.29.0) '@vitejs/plugin-vue': specifier: 4.5.2 - version: 4.5.2(vite@4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0))(vue@3.5.34(typescript@5.7.3)) + version: 4.5.2(vite@4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0))(vue@3.5.34(typescript@5.7.3)) '@vue/compiler-sfc': specifier: 3.3.4 version: 3.3.4 @@ -871,7 +973,7 @@ importers: version: 2.4.1(@vue/server-renderer@3.5.34(vue@3.5.34(typescript@5.7.3)))(vue@3.5.34(typescript@5.7.3)) '@vue/vue3-jest': specifier: 29.2.6 - version: 29.2.6(@babel/core@7.29.0)(babel-jest@29.7.0(@babel/core@7.29.0))(jest@29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)))(typescript@5.7.3)(vue@3.5.34(typescript@5.7.3)) + version: 29.2.6(@babel/core@7.29.0)(babel-jest@29.7.0(@babel/core@7.29.0))(jest@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)))(typescript@5.7.3)(vue@3.5.34(typescript@5.7.3)) babel-jest: specifier: 29.7.0 version: 29.7.0(@babel/core@7.29.0) @@ -880,7 +982,7 @@ importers: version: 9.14.1(eslint@8.57.1) jest: specifier: 29.7.0 - version: 29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) + version: 29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -898,10 +1000,10 @@ importers: version: 1.56.1 ts-jest: specifier: ^29.1.1 - version: 29.4.11(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)))(typescript@5.7.3) + version: 29.4.11(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)))(typescript@5.7.3) vite: specifier: 4.5.11 - version: 4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0) + version: 4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0) projects/yeoman-ui/packages/generator-foodq: dependencies: @@ -919,7 +1021,7 @@ importers: version: 7.3.3 yeoman-generator: specifier: ^4.12.0 - version: 4.13.0(@types/node@25.9.1)(encoding@0.1.13)(mem-fs@4.1.4) + version: 4.13.0(@types/node@22.20.1)(encoding@0.1.13)(mem-fs@4.1.4) projects/yeoman-ui/packages/types: devDependencies: @@ -931,11 +1033,11 @@ importers: dependencies: yeoman-environment: specifier: 3.19.3 - version: 3.19.3(@types/node@22.15.35) + version: 3.19.3(@types/node@22.20.1) devDependencies: '@types/yeoman-environment': specifier: 2.10.11 - version: 2.10.11(@yeoman/types@1.11.1(@types/node@22.15.35)(@yeoman/adapter@4.0.2(@types/node@22.15.35))(mem-fs@4.1.4))(mem-fs@4.1.4) + version: 2.10.11(@yeoman/types@1.11.1(@types/node@22.20.1)(@yeoman/adapter@4.0.2(@types/node@22.20.1))(mem-fs@4.1.4))(mem-fs@4.1.4) packages: @@ -3312,24 +3414,15 @@ packages: '@types/node@15.14.9': resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} - '@types/node@16.11.10': - resolution: {integrity: sha512-3aRnHa1KlOEEhJ6+CvyHKK5vE9BcLGjtUpwvqYLRvYNQKMfabu3BwfJaA/SLW8dxe28LsNDjtHwePTuzn3gmOA==} - '@types/node@16.18.126': resolution: {integrity: sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==} - '@types/node@22.15.35': - resolution: {integrity: sha512-stV91mHxlWpDksiUiivmFfQzjy2JLlb2NUTxKipiANEbxBZsdbDU9fSrT7SHY4uoCXAxYfJZVasn3x2/hqpd3g==} - '@types/node@22.19.1': resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} '@types/node@22.20.1': resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} - '@types/node@25.9.1': - resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} - '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -3435,6 +3528,12 @@ packages: '@types/vinyl@2.0.12': resolution: {integrity: sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==} + '@types/vscode@1.51.0': + resolution: {integrity: sha512-C/jZ35OT5k/rsJyAK8mS1kM++vMcm89oSWegkzxRCvHllIq0cToZAkIDs6eCY4SKrvik3nrhELizyLcM0onbQA==} + + '@types/vscode@1.52.0': + resolution: {integrity: sha512-Kt3bvWzAvvF/WH9YEcrCICDp0Z7aHhJGhLJ1BxeyNP6yRjonWqWnAIh35/pXAjswAnWOABrYlF7SwXR9+1nnLA==} + '@types/vscode@1.75.0': resolution: {integrity: sha512-SAr0PoOhJS6FUq5LjNr8C/StBKALZwDVm3+U4pjF/3iYkt3GioJOPV/oB1Sf1l7lROe4TgrMyL5N1yaEgTWycw==} @@ -3542,18 +3641,12 @@ packages: '@vscode-elements/elements@1.11.0': resolution: {integrity: sha512-vC1QDaelqERypHinavJbe4Bl4/g66CVJWKFWFXZyp1seBvTxkgscayDTiE//H95V8b75BgLPDAS1y3pbSIKXag==} - '@vscode-logging/logger@1.2.3': - resolution: {integrity: sha512-kQgRjbZmBBUktklaEat8YtTBuddCNRJezjJTk0Y+4VIs49KWjYkfF02GoyiCYASvCSMSqw9bBTQN3ijCO2aq+A==} - '@vscode-logging/logger@2.0.9': resolution: {integrity: sha512-Kq7dmziSHej+6yADxoHCZU8Tspf7ucJeNjH+QPMAQlRfT0uaXlh1q5DpWnzSgznWIBrMc7lX82meThDKBxL+oA==} '@vscode-logging/types@0.1.3': resolution: {integrity: sha512-1mV/aMVXpm50PyJ4XYD0qPUREv6QcmPwuDtzdMcKeiXN5Tt/WmnyEHfUxENQr1Aes8Hr+EVtNK4tvrUXXco/5A==} - '@vscode-logging/types@0.1.4': - resolution: {integrity: sha512-uxuHQfpX9RbkgSj5unJFmciXRczyFSaAI2aA829MYYkE8jxlhZLRLoiJLymTNiojNVdV7fFE3CILF5Q6M+EBsA==} - '@vscode-logging/types@2.0.8': resolution: {integrity: sha512-9mc5+Iob1BqJ00QExLwdz4+w0A1odSYjvzpKYfP0lVzr7Tdf4iWGspLADyTSC7DtJVQQ+tu411cBju+cYJxE/g==} @@ -4174,10 +4267,6 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - at-least-node@1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} - engines: {node: '>= 4.0.0'} - atob@2.1.2: resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} engines: {node: '>= 4.5.0'} @@ -4575,6 +4664,11 @@ packages: peerDependencies: chai: '>= 2.1.2 < 6' + chai-exclude@2.1.0: + resolution: {integrity: sha512-IBnm50Mvl3O1YhPpTgbU8MK0Gw7NHcb18WT2TxGdPKOMtdtZVKLHmQwdvOF7mTlHVQStbXuZKFwkevFtbHjpVg==} + peerDependencies: + chai: '>= 4.0.0 < 5' + chai-string@1.6.0: resolution: {integrity: sha512-sXV7whDmpax+8H++YaZelgin7aur1LGf9ZhjZa3ojETFJ0uPVuS4XEXuIagpZ/c8uVOtsSh4MwOjy5CBLjJSXA==} peerDependencies: @@ -5123,11 +5217,6 @@ packages: resolution: {integrity: sha512-y17kh32+I82G+ED9MNWFkZiP/Cq/vO1hN9+tSZsT9C9qn3NrvcBnh7crSepg0AQPge1hXx2Ca44s1FRdv0gFWA==} engines: {node: '>= 10'} - date-format@2.1.0: - resolution: {integrity: sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==} - engines: {node: '>=4.0'} - deprecated: 2.x is no longer supported. Please upgrade to 4.x or higher. - date-format@4.0.14: resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} engines: {node: '>=4.0'} @@ -5998,9 +6087,6 @@ packages: resolution: {integrity: sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==} engines: {node: '>=10.0'} - fast-safe-stringify@2.0.7: - resolution: {integrity: sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==} - fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} @@ -6025,6 +6111,9 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fclone@1.0.11: + resolution: {integrity: sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==} + fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} @@ -6253,10 +6342,6 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} - fs-extra@9.1.0: - resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} - engines: {node: '>=10'} - fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -8538,6 +8623,9 @@ packages: nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + nise@5.1.9: + resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} + nise@6.0.0: resolution: {integrity: sha512-K8ePqo9BFvN31HXwEtTNGzgrPpmvgciDsFz8aztFjt4LqKO/JeFD8tBOeuDiCMXrIl/m1YvfH8auSpxfaD09wg==} @@ -10110,6 +10198,15 @@ packages: simple-git@3.36.0: resolution: {integrity: sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==} + sinon-chai@3.7.0: + resolution: {integrity: sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==} + peerDependencies: + chai: ^4.0.0 + sinon: '>=4.0.0' + + sinon@17.0.1: + resolution: {integrity: sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==} + sinon@18.0.1: resolution: {integrity: sha512-a2N2TDY1uGviajJ6r4D1CyRAkzE9NNVlYOV1wX5xQDuAk0ONgzgRl0EjCQuRCPxOwp13ghsMwt9Gdldujs39qw==} @@ -10348,11 +10445,6 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - streamroller@2.2.3: - resolution: {integrity: sha512-AegmvQsscTRhHVO46PhCDerjIpxi7E+d2GxgUDu+nzw/HuLnUdxHWr6WQ+mVn/4iJgMKKFFdiUwFcFRDvcjCtw==} - engines: {node: '>=8.0'} - deprecated: 2.x is no longer supported. Please upgrade to 3.x or higher. - streamroller@3.1.5: resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} engines: {node: '>=8.0'} @@ -10745,9 +10837,6 @@ packages: resolution: {integrity: sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==} engines: {node: '>=0.10.0'} - triple-beam@1.3.0: - resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==} - triple-beam@1.4.1: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} @@ -10985,9 +11074,6 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.24.6: - resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici@6.27.0: resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==} engines: {node: '>=18.17'} @@ -11536,10 +11622,6 @@ packages: peerDependencies: winston: ^3 - winston-transport@4.3.0: - resolution: {integrity: sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==} - engines: {node: '>= 6.4.0'} - winston-transport@4.6.0: resolution: {integrity: sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==} engines: {node: '>= 12.0.0'} @@ -11556,10 +11638,6 @@ packages: resolution: {integrity: sha512-RhruH2Cj0bV0WgNL+lOfoUBI4DVfdUNjVnJGVovWZmrcKtrFTTRzgXYK2O9cymSGjrERCtaAeHwMNnUWXlwZow==} engines: {node: '>= 12.0.0'} - winston@3.3.3: - resolution: {integrity: sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==} - engines: {node: '>= 6.4.0'} - with-open-file@0.1.7: resolution: {integrity: sha512-ecJS2/oHtESJ1t3ZfMI3B7KIDKyfN0O16miWxdn30zdh66Yd3LsRFebXZXq6GU4xfxLf6nVxp9kIqElb5fqczA==} engines: {node: '>=6'} @@ -12725,7 +12803,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.31.1(@types/node@16.11.10)': + '@changesets/cli@2.31.1(@types/node@22.20.1)': dependencies: '@changesets/apply-release-plan': 7.1.1 '@changesets/assemble-release-plan': 6.0.10 @@ -12741,7 +12819,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@16.11.10) + '@inquirer/external-editor': 1.0.3(@types/node@22.20.1) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 enquirer: 2.4.1 @@ -12843,11 +12921,11 @@ snapshots: '@colors/colors@1.6.0': {} - '@commitlint/cli@19.5.0(@types/node@16.11.10)(typescript@5.7.3)': + '@commitlint/cli@19.5.0(@types/node@22.20.1)(typescript@5.7.3)': dependencies: '@commitlint/format': 19.5.0 '@commitlint/lint': 19.5.0 - '@commitlint/load': 19.5.0(@types/node@16.11.10)(typescript@5.7.3) + '@commitlint/load': 19.5.0(@types/node@22.20.1)(typescript@5.7.3) '@commitlint/read': 19.5.0 '@commitlint/types': 19.5.0 tinyexec: 0.3.1 @@ -12894,7 +12972,7 @@ snapshots: '@commitlint/rules': 19.5.0 '@commitlint/types': 19.5.0 - '@commitlint/load@19.5.0(@types/node@16.11.10)(typescript@5.7.3)': + '@commitlint/load@19.5.0(@types/node@22.20.1)(typescript@5.7.3)': dependencies: '@commitlint/config-validator': 19.5.0 '@commitlint/execute-rule': 19.5.0 @@ -12902,7 +12980,7 @@ snapshots: '@commitlint/types': 19.5.0 chalk: 5.6.2 cosmiconfig: 9.0.0(typescript@5.7.3) - cosmiconfig-typescript-loader: 5.0.0(@types/node@16.11.10)(cosmiconfig@9.0.0(typescript@5.7.3))(typescript@5.7.3) + cosmiconfig-typescript-loader: 5.0.0(@types/node@22.20.1)(cosmiconfig@9.0.0(typescript@5.7.3))(typescript@5.7.3) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -13180,14 +13258,14 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/checkbox@5.2.1(@types/node@22.15.35)': + '@inquirer/checkbox@5.2.1(@types/node@22.20.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/confirm@6.1.1(@types/node@14.18.63)': @@ -13197,12 +13275,12 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/confirm@6.1.1(@types/node@22.15.35)': + '@inquirer/confirm@6.1.1(@types/node@22.20.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@22.15.35) - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/core@11.2.1(@types/node@14.18.63)': @@ -13217,17 +13295,17 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/core@11.2.1(@types/node@22.15.35)': + '@inquirer/core@11.2.1(@types/node@22.20.1)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/type': 4.0.7(@types/node@22.20.1) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/editor@5.2.2(@types/node@14.18.63)': @@ -13238,13 +13316,13 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/editor@5.2.2(@types/node@22.15.35)': + '@inquirer/editor@5.2.2(@types/node@22.20.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@22.15.35) - '@inquirer/external-editor': 3.0.3(@types/node@22.15.35) - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) + '@inquirer/external-editor': 3.0.3(@types/node@22.20.1) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/expand@5.1.1(@types/node@14.18.63)': @@ -13254,27 +13332,20 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/expand@5.1.1(@types/node@22.15.35)': + '@inquirer/expand@5.1.1(@types/node@22.20.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@22.15.35) - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true - '@inquirer/external-editor@1.0.3(@types/node@16.11.10)': + '@inquirer/external-editor@1.0.3(@types/node@22.20.1)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 16.11.10 - - '@inquirer/external-editor@1.0.3(@types/node@22.15.35)': - dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.2 - optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@inquirer/external-editor@3.0.3(@types/node@14.18.63)': dependencies: @@ -13283,12 +13354,12 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/external-editor@3.0.3(@types/node@22.15.35)': + '@inquirer/external-editor@3.0.3(@types/node@22.20.1)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/figures@2.0.7': {} @@ -13300,12 +13371,12 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/input@5.1.2(@types/node@22.15.35)': + '@inquirer/input@5.1.2(@types/node@22.20.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@22.15.35) - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/number@4.1.1(@types/node@14.18.63)': @@ -13315,12 +13386,12 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/number@4.1.1(@types/node@22.15.35)': + '@inquirer/number@4.1.1(@types/node@22.20.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@22.15.35) - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/password@5.1.1(@types/node@14.18.63)': @@ -13331,13 +13402,13 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/password@5.1.1(@types/node@22.15.35)': + '@inquirer/password@5.1.1(@types/node@22.20.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@22.15.35) - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/prompts@8.5.2(@types/node@14.18.63)': @@ -13355,20 +13426,20 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/prompts@8.5.2(@types/node@22.15.35)': - dependencies: - '@inquirer/checkbox': 5.2.1(@types/node@22.15.35) - '@inquirer/confirm': 6.1.1(@types/node@22.15.35) - '@inquirer/editor': 5.2.2(@types/node@22.15.35) - '@inquirer/expand': 5.1.1(@types/node@22.15.35) - '@inquirer/input': 5.1.2(@types/node@22.15.35) - '@inquirer/number': 4.1.1(@types/node@22.15.35) - '@inquirer/password': 5.1.1(@types/node@22.15.35) - '@inquirer/rawlist': 5.3.1(@types/node@22.15.35) - '@inquirer/search': 4.2.1(@types/node@22.15.35) - '@inquirer/select': 5.2.1(@types/node@22.15.35) + '@inquirer/prompts@8.5.2(@types/node@22.20.1)': + dependencies: + '@inquirer/checkbox': 5.2.1(@types/node@22.20.1) + '@inquirer/confirm': 6.1.1(@types/node@22.20.1) + '@inquirer/editor': 5.2.2(@types/node@22.20.1) + '@inquirer/expand': 5.1.1(@types/node@22.20.1) + '@inquirer/input': 5.1.2(@types/node@22.20.1) + '@inquirer/number': 4.1.1(@types/node@22.20.1) + '@inquirer/password': 5.1.1(@types/node@22.20.1) + '@inquirer/rawlist': 5.3.1(@types/node@22.20.1) + '@inquirer/search': 4.2.1(@types/node@22.20.1) + '@inquirer/select': 5.2.1(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/rawlist@5.3.1(@types/node@14.18.63)': @@ -13378,12 +13449,12 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/rawlist@5.3.1(@types/node@22.15.35)': + '@inquirer/rawlist@5.3.1(@types/node@22.20.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@22.15.35) - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/search@4.2.1(@types/node@14.18.63)': @@ -13394,13 +13465,13 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/search@4.2.1(@types/node@22.15.35)': + '@inquirer/search@4.2.1(@types/node@22.20.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/select@5.2.1(@types/node@14.18.63)': @@ -13412,23 +13483,23 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - '@inquirer/select@5.2.1(@types/node@22.15.35)': + '@inquirer/select@5.2.1(@types/node@22.20.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/type': 4.0.7(@types/node@22.20.1) optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@inquirer/type@4.0.7(@types/node@14.18.63)': optionalDependencies: '@types/node': 14.18.63 - '@inquirer/type@4.0.7(@types/node@22.15.35)': + '@inquirer/type@4.0.7(@types/node@22.20.1)': optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true '@isaacs/cliui@8.0.2': @@ -13459,7 +13530,7 @@ snapshots: '@jest/console@27.5.1': dependencies: '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 jest-message-util: 27.5.1 jest-util: 27.5.1 @@ -13468,27 +13539,27 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10)': + '@jest/core@27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10)': dependencies: '@jest/console': 27.5.1 '@jest/reporters': 27.5.1 '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.8.1 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 27.5.1 - jest-config: 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10) + jest-config: 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10) jest-haste-map: 27.5.1 jest-message-util: 27.5.1 jest-regex-util: 27.5.1 @@ -13511,56 +13582,21 @@ snapshots: - ts-node - utf-8-validate - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3))': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.0 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.15.35 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)) + jest-config: 29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -13585,14 +13621,14 @@ snapshots: dependencies: '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-mock: 27.5.1 '@jest/environment@29.7.0': dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -13610,7 +13646,7 @@ snapshots: dependencies: '@jest/types': 27.5.1 '@sinonjs/fake-timers': 8.1.0 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 @@ -13619,7 +13655,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -13646,7 +13682,7 @@ snapshots: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit: 0.1.2 @@ -13677,7 +13713,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit: 0.1.2 @@ -13788,7 +13824,7 @@ snapshots: dependencies: '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/yargs': 16.0.11 chalk: 4.1.2 @@ -13797,7 +13833,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -14541,10 +14577,10 @@ snapshots: dependencies: node-cache: 5.1.2 - '@sap-devx/inquirer-gui-auto-complete-plugin@3.4.13(typescript@5.7.3)(vite@4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0))': + '@sap-devx/inquirer-gui-auto-complete-plugin@3.4.13(typescript@5.7.3)(vite@4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0))': dependencies: strip-ansi: 6.0.0 - vite-plugin-css-injected-by-js: 3.5.2(vite@4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0)) + vite-plugin-css-injected-by-js: 3.5.2(vite@4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0)) vue: 3.5.34(typescript@5.7.3) transitivePeerDependencies: - typescript @@ -14997,7 +15033,7 @@ snapshots: '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.35 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/chai-as-promised@7.1.8': dependencies: @@ -15007,7 +15043,7 @@ snapshots: '@types/cheerio@0.22.35': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/comment-json@2.4.2': dependencies: @@ -15015,19 +15051,19 @@ snapshots: '@types/connect@3.4.35': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/conventional-commits-parser@5.0.0': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/cors@2.8.13': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/cors@2.8.19': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/debug@4.1.13': dependencies: @@ -15057,7 +15093,7 @@ snapshots: '@types/express-serve-static-core@4.17.35': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 '@types/send': 0.17.1 @@ -15072,34 +15108,34 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/fs-extra@9.0.12': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/fs-extra@9.0.13': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/glob@7.2.0': dependencies: '@types/minimatch': 6.0.0 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/http-cache-semantics@4.2.0': {} '@types/http-proxy@1.17.17': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/http-terminator@2.0.5': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/inquirer@7.3.3': dependencies: @@ -15123,7 +15159,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -15135,11 +15171,11 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/keyv@3.1.4': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/lodash-es@4.17.12': dependencies: @@ -15172,7 +15208,7 @@ snapshots: '@types/node-fetch@2.6.13': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 form-data: 4.0.5 '@types/node@12.20.55': {} @@ -15181,14 +15217,8 @@ snapshots: '@types/node@15.14.9': {} - '@types/node@16.11.10': {} - '@types/node@16.18.126': {} - '@types/node@22.15.35': - dependencies: - undici-types: 6.21.0 - '@types/node@22.19.1': dependencies: undici-types: 6.21.0 @@ -15197,18 +15227,13 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/node@25.9.1': - dependencies: - undici-types: 7.24.6 - optional: true - '@types/normalize-package-data@2.4.4': {} '@types/npm-package-arg@6.1.4': {} '@types/npm-registry-fetch@8.0.9': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/node-fetch': 2.6.13 '@types/npm-package-arg': 6.1.4 '@types/npmlog': 7.0.0 @@ -15216,7 +15241,7 @@ snapshots: '@types/npmlog@7.0.0': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/object-hash@2.2.1': {} @@ -15234,12 +15259,12 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/rimraf@3.0.2': dependencies: '@types/glob': 7.2.0 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/sarif@2.1.7': {} @@ -15250,12 +15275,12 @@ snapshots: '@types/send@0.17.1': dependencies: '@types/mime': 1.3.2 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/serve-static@1.15.1': dependencies: '@types/mime': 3.0.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/shimmer@1.2.0': {} @@ -15267,7 +15292,7 @@ snapshots: '@types/ssri@7.1.5': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/stack-utils@2.0.3': {} @@ -15277,13 +15302,13 @@ snapshots: '@types/tar-stream@3.1.4': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/text-table@0.2.5': {} '@types/through@0.0.33': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/tmp@0.2.6': {} @@ -15300,7 +15325,11 @@ snapshots: '@types/vinyl@2.0.12': dependencies: '@types/expect': 1.20.4 - '@types/node': 22.15.35 + '@types/node': 22.20.1 + + '@types/vscode@1.51.0': {} + + '@types/vscode@1.52.0': {} '@types/vscode@1.75.0': {} @@ -15308,11 +15337,11 @@ snapshots: '@types/websocket@1.0.5': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/ws@8.18.1': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/yargs-parser@21.0.3': {} @@ -15326,17 +15355,17 @@ snapshots: '@types/yauzl@3.4.0': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 - '@types/yeoman-environment@2.10.11(@yeoman/types@1.11.1(@types/node@22.15.35)(@yeoman/adapter@4.0.2(@types/node@22.15.35))(mem-fs@4.1.4))(mem-fs@4.1.4)': + '@types/yeoman-environment@2.10.11(@yeoman/types@1.11.1(@types/node@22.20.1)(@yeoman/adapter@4.0.2(@types/node@22.20.1))(mem-fs@4.1.4))(mem-fs@4.1.4)': dependencies: '@types/diff': 8.0.0 '@types/inquirer': 8.2.13 '@types/mem-fs': 2.2.0 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/text-table': 0.2.5 '@types/vinyl': 2.0.12 - '@types/yeoman-generator': 6.0.0(@types/node@22.15.35)(@yeoman/types@1.11.1(@types/node@22.15.35)(@yeoman/adapter@4.0.2(@types/node@22.15.35))(mem-fs@4.1.4))(mem-fs@4.1.4) + '@types/yeoman-generator': 6.0.0(@types/node@22.20.1)(@yeoman/types@1.11.1(@types/node@22.20.1)(@yeoman/adapter@4.0.2(@types/node@22.20.1))(mem-fs@4.1.4))(mem-fs@4.1.4) chalk: 4.1.2 commander: 9.5.0 execa: 5.1.1 @@ -15348,9 +15377,9 @@ snapshots: - react-native-b4a - supports-color - '@types/yeoman-generator@6.0.0(@types/node@22.15.35)(@yeoman/types@1.11.1(@types/node@22.15.35)(@yeoman/adapter@4.0.2(@types/node@22.15.35))(mem-fs@4.1.4))(mem-fs@4.1.4)': + '@types/yeoman-generator@6.0.0(@types/node@22.20.1)(@yeoman/types@1.11.1(@types/node@22.20.1)(@yeoman/adapter@4.0.2(@types/node@22.20.1))(mem-fs@4.1.4))(mem-fs@4.1.4)': dependencies: - yeoman-generator: 8.2.2(@types/node@22.15.35)(@yeoman/types@1.11.1(@types/node@22.15.35)(@yeoman/adapter@4.0.2(@types/node@22.15.35))(mem-fs@4.1.4))(mem-fs@4.1.4) + yeoman-generator: 8.2.2(@types/node@22.20.1)(@yeoman/types@1.11.1(@types/node@22.20.1)(@yeoman/adapter@4.0.2(@types/node@22.20.1))(mem-fs@4.1.4))(mem-fs@4.1.4) transitivePeerDependencies: - '@types/node' - '@yeoman/types' @@ -15460,29 +15489,15 @@ snapshots: '@ungap/structured-clone@1.3.1': {} - '@vitejs/plugin-vue@4.5.2(vite@4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0))(vue@3.5.34(typescript@5.7.3))': + '@vitejs/plugin-vue@4.5.2(vite@4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0))(vue@3.5.34(typescript@5.7.3))': dependencies: - vite: 4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0) + vite: 4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0) vue: 3.5.34(typescript@5.7.3) '@vscode-elements/elements@1.11.0': dependencies: lit: 3.3.3 - '@vscode-logging/logger@1.2.3': - dependencies: - '@vscode-logging/types': 0.1.4 - fast-safe-stringify: 2.0.7 - fs-extra: 9.1.0 - lodash: 4.18.1 - stacktrace-js: 2.0.2 - streamroller: 2.2.3 - triple-beam: 1.3.0 - winston: 3.3.3 - winston-transport: 4.3.0 - transitivePeerDependencies: - - supports-color - '@vscode-logging/logger@2.0.9': dependencies: '@vscode-logging/types': 2.0.8 @@ -15499,8 +15514,6 @@ snapshots: '@vscode-logging/types@0.1.3': {} - '@vscode-logging/types@0.1.4': {} - '@vscode-logging/types@2.0.8': {} '@vscode-logging/wrapper@2.0.9': @@ -15712,7 +15725,7 @@ snapshots: optionalDependencies: '@vue/server-renderer': 3.5.34(vue@3.5.34(typescript@5.7.3)) - '@vue/vue3-jest@29.2.6(@babel/core@7.29.0)(babel-jest@29.7.0(@babel/core@7.29.0))(jest@29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)))(typescript@5.7.3)(vue@3.5.34(typescript@5.7.3))': + '@vue/vue3-jest@29.2.6(@babel/core@7.29.0)(babel-jest@29.7.0(@babel/core@7.29.0))(jest@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)))(typescript@5.7.3)(vue@3.5.34(typescript@5.7.3))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) @@ -15720,7 +15733,7 @@ snapshots: chalk: 2.4.2 convert-source-map: 1.8.0 css-tree: 2.3.1 - jest: 29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) + jest: 29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) source-map: 0.5.6 tsconfig: 7.0.0 vue: 3.5.34(typescript@5.7.3) @@ -15852,12 +15865,12 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@yeoman/adapter@4.0.2(@types/node@22.15.35)': + '@yeoman/adapter@4.0.2(@types/node@22.20.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@22.15.35) - '@inquirer/prompts': 8.5.2(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) + '@inquirer/prompts': 8.5.2(@types/node@22.20.1) chalk: 5.6.2 - inquirer: 13.4.3(@types/node@22.15.35) + inquirer: 13.4.3(@types/node@22.20.1) log-symbols: 7.0.1 ora: 9.4.1 p-queue: 9.3.1 @@ -15904,11 +15917,11 @@ snapshots: '@yeoman/adapter': 4.0.2(@types/node@14.18.63) mem-fs: 4.1.4 - '@yeoman/types@1.11.1(@types/node@22.15.35)(@yeoman/adapter@4.0.2(@types/node@22.15.35))(mem-fs@4.1.4)': + '@yeoman/types@1.11.1(@types/node@22.20.1)(@yeoman/adapter@4.0.2(@types/node@22.20.1))(mem-fs@4.1.4)': dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optionalDependencies: - '@yeoman/adapter': 4.0.2(@types/node@22.15.35) + '@yeoman/adapter': 4.0.2(@types/node@22.20.1) mem-fs: 4.1.4 JSONStream@1.3.5: @@ -16231,8 +16244,6 @@ snapshots: asynckit@0.4.0: {} - at-least-node@1.0.0: {} - atob@2.1.2: {} available-typed-arrays@1.0.7: @@ -16769,6 +16780,11 @@ snapshots: chai: 4.5.0 check-error: 1.0.3 + chai-exclude@2.1.0(chai@4.5.0): + dependencies: + chai: 4.5.0 + fclone: 1.0.11 + chai-string@1.6.0(chai@4.4.1): dependencies: chai: 4.4.1 @@ -17188,9 +17204,9 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - cosmiconfig-typescript-loader@5.0.0(@types/node@16.11.10)(cosmiconfig@9.0.0(typescript@5.7.3))(typescript@5.7.3): + cosmiconfig-typescript-loader@5.0.0(@types/node@22.20.1)(cosmiconfig@9.0.0(typescript@5.7.3))(typescript@5.7.3): dependencies: - '@types/node': 16.11.10 + '@types/node': 22.20.1 cosmiconfig: 9.0.0(typescript@5.7.3) jiti: 1.21.6 typescript: 5.7.3 @@ -17224,28 +17240,13 @@ snapshots: dependencies: capture-stack-trace: 1.0.2 - create-jest@29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - create-jest@29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)): + create-jest@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)) + jest-config: 29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -17397,8 +17398,6 @@ snapshots: image-size: 1.0.0 mimer: 2.0.2 - date-format@2.1.0: {} - date-format@4.0.14: {} dateformat@1.0.12: @@ -17751,7 +17750,7 @@ snapshots: engine.io@6.6.7(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@types/cors': 2.8.13 - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/ws': 8.18.1 accepts: 1.3.8 base64id: 2.0.0 @@ -18460,8 +18459,6 @@ snapshots: dependencies: boolean: 3.2.0 - fast-safe-stringify@2.0.7: {} - fast-safe-stringify@2.1.1: {} fast-string-truncated-width@3.0.3: {} @@ -18486,6 +18483,8 @@ snapshots: dependencies: bser: 2.1.1 + fclone@1.0.11: {} + fd-slicer@1.1.0: dependencies: pend: 1.2.0 @@ -18747,13 +18746,6 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 - fs-extra@9.1.0: - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - fs-minipass@2.1.0: dependencies: minipass: 3.3.6 @@ -19498,17 +19490,17 @@ snapshots: optionalDependencies: '@types/node': 14.18.63 - inquirer@13.4.3(@types/node@22.15.35): + inquirer@13.4.3(@types/node@22.20.1): dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@22.15.35) - '@inquirer/prompts': 8.5.2(@types/node@22.15.35) - '@inquirer/type': 4.0.7(@types/node@22.15.35) + '@inquirer/core': 11.2.1(@types/node@22.20.1) + '@inquirer/prompts': 8.5.2(@types/node@22.20.1) + '@inquirer/type': 4.0.7(@types/node@22.20.1) mute-stream: 3.0.0 run-async: 4.0.6 rxjs: 7.8.2 optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 optional: true inquirer@7.3.3: @@ -19527,9 +19519,9 @@ snapshots: strip-ansi: 6.0.1 through: 2.3.8 - inquirer@8.2.7(@types/node@22.15.35): + inquirer@8.2.7(@types/node@22.20.1): dependencies: - '@inquirer/external-editor': 1.0.3(@types/node@22.15.35) + '@inquirer/external-editor': 1.0.3(@types/node@22.20.1) ansi-escapes: 4.3.2 chalk: 4.1.2 cli-cursor: 3.1.0 @@ -19969,7 +19961,7 @@ snapshots: '@jest/environment': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -19994,7 +19986,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.2 @@ -20014,16 +20006,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10): + jest-cli@27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10): dependencies: - '@jest/core': 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10) + '@jest/core': 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10) '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 import-local: 3.0.3 - jest-config: 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10) + jest-config: 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10) jest-util: 27.5.1 jest-validate: 27.5.1 prompts: 2.4.2 @@ -20035,16 +20027,16 @@ snapshots: - ts-node - utf-8-validate - jest-cli@29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)): + jest-cli@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) + create-jest: 29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) exit: 0.1.2 import-local: 3.0.3 - jest-config: 29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) + jest-config: 29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -20054,26 +20046,7 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)) - exit: 0.1.2 - import-local: 3.0.3 - jest-config: 29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - jest-config@27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10): + jest-config@27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10): dependencies: '@babel/core': 7.29.0 '@jest/test-sequencer': 27.5.1 @@ -20100,76 +20073,14 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - ts-node: 10.9.2(@types/node@25.9.1)(typescript@5.7.3) + ts-node: 10.9.2(@types/node@22.20.1)(typescript@5.7.3) transitivePeerDependencies: - bufferutil - canvas - supports-color - utf-8-validate - jest-config@29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)): - dependencies: - '@babel/core': 7.29.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.29.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.0 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.15.35 - ts-node: 10.9.2(@types/node@22.15.35)(typescript@5.7.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)): - dependencies: - '@babel/core': 7.29.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.29.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.0 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.15.35 - ts-node: 10.9.2(@types/node@25.9.1)(typescript@5.7.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)): + jest-config@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)): dependencies: '@babel/core': 7.29.0 '@jest/test-sequencer': 29.7.0 @@ -20194,8 +20105,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 25.9.1 - ts-node: 10.9.2(@types/node@25.9.1)(typescript@5.7.3) + '@types/node': 22.20.1 + ts-node: 10.9.2(@types/node@22.20.1)(typescript@5.7.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -20243,7 +20154,7 @@ snapshots: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-mock: 27.5.1 jest-util: 27.5.1 jsdom: 16.7.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -20259,7 +20170,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -20273,7 +20184,7 @@ snapshots: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-mock: 27.5.1 jest-util: 27.5.1 @@ -20282,7 +20193,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -20294,7 +20205,7 @@ snapshots: dependencies: '@jest/types': 27.5.1 '@types/graceful-fs': 4.1.9 - '@types/node': 22.15.35 + '@types/node': 22.20.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -20311,7 +20222,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.15.35 + '@types/node': 22.20.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -20329,7 +20240,7 @@ snapshots: '@jest/source-map': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 co: 4.6.0 expect: 27.5.1 @@ -20393,20 +20304,20 @@ snapshots: slash: 3.0.0 stack-utils: 2.0.6 - jest-mock-vscode@0.1.6(jest@29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))): + jest-mock-vscode@0.1.6(jest@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))): dependencies: - jest: 29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)) + jest: 29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) vscode-uri: 3.0.3 jest-mock@27.5.1: dependencies: '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@27.5.1): @@ -20468,7 +20379,7 @@ snapshots: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 emittery: 0.8.1 graceful-fs: 4.2.11 @@ -20497,7 +20408,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -20552,7 +20463,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 cjs-module-lexer: 1.4.3 collect-v8-coverage: 1.0.3 @@ -20576,7 +20487,7 @@ snapshots: jest-serializer@27.5.1: dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 graceful-fs: 4.2.11 jest-snapshot@27.5.1: @@ -20636,7 +20547,7 @@ snapshots: jest-util@27.5.1: dependencies: '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -20645,7 +20556,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -20673,7 +20584,7 @@ snapshots: dependencies: '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 22.15.35 + '@types/node': 22.20.1 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 27.5.1 @@ -20683,7 +20594,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.35 + '@types/node': 22.20.1 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -20692,22 +20603,22 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10): + jest@27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10): dependencies: - '@jest/core': 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10) + '@jest/core': 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10) import-local: 3.0.3 - jest-cli: 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3))(utf-8-validate@5.0.10) + jest-cli: 27.5.1(bufferutil@4.1.0)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3))(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - canvas @@ -20715,24 +20626,12 @@ snapshots: - ts-node - utf-8-validate - jest@29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) - '@jest/types': 29.6.3 - import-local: 3.0.3 - jest-cli: 29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - jest@29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)): + jest@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) '@jest/types': 29.6.3 import-local: 3.0.3 - jest-cli: 29.7.0(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3)) + jest-cli: 29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -21545,7 +21444,7 @@ snapshots: - react-native-b4a - supports-color - mem-fs-editor@12.0.4(@types/node@22.15.35)(mem-fs@4.1.4): + mem-fs-editor@12.0.4(@types/node@22.20.1)(mem-fs@4.1.4): dependencies: '@types/ejs': 3.1.5 '@types/picomatch': 4.0.3 @@ -21563,31 +21462,7 @@ snapshots: tinyglobby: 0.2.17 vinyl: 3.0.1 optionalDependencies: - '@types/node': 22.15.35 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - mem-fs-editor@12.0.4(@types/node@25.9.1)(mem-fs@4.1.4): - dependencies: - '@types/ejs': 3.1.5 - '@types/picomatch': 4.0.3 - binaryextensions: 6.11.0 - commondir: 1.0.1 - debug: 4.4.3 - deep-extend: 0.6.0 - ejs: 5.0.2 - isbinaryfile: 5.0.7 - mem-fs: 4.1.4 - minimatch: 10.2.5 - multimatch: 8.0.0 - normalize-path: 3.0.0 - textextensions: 6.11.0 - tinyglobby: 0.2.17 - vinyl: 3.0.1 - optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 22.20.1 transitivePeerDependencies: - bare-abort-controller - react-native-b4a @@ -21617,7 +21492,7 @@ snapshots: mem-fs@4.1.4: dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 '@types/vinyl': 2.0.12 vinyl: 3.0.1 vinyl-file: 5.0.0 @@ -21954,6 +21829,14 @@ snapshots: nice-try@1.0.5: {} + nise@5.1.9: + dependencies: + '@sinonjs/commons': 3.0.1 + '@sinonjs/fake-timers': 11.3.1 + '@sinonjs/text-encoding': 0.7.3 + just-extend: 6.2.0 + path-to-regexp: 6.2.2 + nise@6.0.0: dependencies: '@sinonjs/commons': 3.0.1 @@ -22595,7 +22478,7 @@ snapshots: p-transform@5.0.1: dependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 p-queue: 8.1.1 p-try@2.2.0: {} @@ -23844,6 +23727,20 @@ snapshots: transitivePeerDependencies: - supports-color + sinon-chai@3.7.0(chai@4.5.0)(sinon@17.0.1): + dependencies: + chai: 4.5.0 + sinon: 17.0.1 + + sinon@17.0.1: + dependencies: + '@sinonjs/commons': 3.0.1 + '@sinonjs/fake-timers': 11.3.1 + '@sinonjs/samsam': 8.0.0 + diff: 5.2.0 + nise: 5.1.9 + supports-color: 7.2.0 + sinon@18.0.1: dependencies: '@sinonjs/commons': 3.0.1 @@ -24133,14 +24030,6 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - streamroller@2.2.3: - dependencies: - date-format: 2.1.0 - debug: 4.4.3 - fs-extra: 8.1.0 - transitivePeerDependencies: - - supports-color - streamroller@3.1.5: dependencies: date-format: 4.0.14 @@ -24564,8 +24453,6 @@ snapshots: trim-newlines@1.0.0: {} - triple-beam@1.3.0: {} - triple-beam@1.4.1: {} ts-api-utils@2.5.0(typescript@5.7.3): @@ -24578,12 +24465,12 @@ snapshots: optionalDependencies: typescript: 5.7.3 - ts-jest@29.4.11(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)))(typescript@5.7.3): + ts-jest@29.4.11(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)))(typescript@5.7.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.9 - jest: 29.7.0(@types/node@22.15.35)(ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3)) + jest: 29.7.0(@types/node@22.20.1)(ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -24645,33 +24532,14 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - ts-node@10.9.2(@types/node@22.15.35)(typescript@5.7.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 22.15.35 - acorn: 8.16.0 - acorn-walk: 8.3.5 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.4 - make-error: 1.3.6 - typescript: 5.7.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optional: true - - ts-node@10.9.2(@types/node@25.9.1)(typescript@5.7.3): + ts-node@10.9.2(@types/node@22.20.1)(typescript@5.7.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 25.9.1 + '@types/node': 22.20.1 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -24835,9 +24703,6 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.24.6: - optional: true - undici@6.27.0: {} undici@7.25.0: {} @@ -25054,17 +24919,17 @@ snapshots: - bare-abort-controller - react-native-b4a - vite-plugin-css-injected-by-js@3.5.2(vite@4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0)): + vite-plugin-css-injected-by-js@3.5.2(vite@4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0)): dependencies: - vite: 4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0) + vite: 4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0) - vite@4.5.11(@types/node@22.15.35)(sass@1.56.1)(terser@5.48.0): + vite@4.5.11(@types/node@22.20.1)(sass@1.56.1)(terser@5.48.0): dependencies: esbuild: 0.18.20 postcss: 8.5.15 rollup: 3.30.0 optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 fsevents: 2.3.3 sass: 1.56.1 terser: 5.48.0 @@ -25483,12 +25348,6 @@ snapshots: winston: 3.15.0 winston-transport: 4.8.0 - winston-transport@4.3.0: - dependencies: - logform: 2.7.0 - readable-stream: 2.3.8 - triple-beam: 1.4.1 - winston-transport@4.6.0: dependencies: logform: 2.7.0 @@ -25513,7 +25372,7 @@ snapshots: safe-stable-stringify: 2.5.0 stack-trace: 0.0.10 triple-beam: 1.4.1 - winston-transport: 4.8.0 + winston-transport: 4.6.0 winston@3.15.0: dependencies: @@ -25529,18 +25388,6 @@ snapshots: triple-beam: 1.4.1 winston-transport: 4.8.0 - winston@3.3.3: - dependencies: - '@dabh/diagnostics': 2.0.8 - async: 3.2.6 - is-stream: 2.0.1 - logform: 2.7.0 - one-time: 1.0.0 - readable-stream: 3.6.2 - stack-trace: 0.0.10 - triple-beam: 1.4.1 - winston-transport: 4.8.0 - with-open-file@0.1.7: dependencies: p-finally: 1.0.0 @@ -25741,7 +25588,7 @@ snapshots: - react-native-b4a - supports-color - yeoman-environment@2.10.3(@types/node@25.9.1)(encoding@0.1.13): + yeoman-environment@2.10.3(@types/node@22.20.1)(encoding@0.1.13): dependencies: chalk: 2.4.2 debug: 3.2.7 @@ -25755,13 +25602,13 @@ snapshots: lodash: 4.18.1 log-symbols: 2.2.0 mem-fs: 4.1.4 - mem-fs-editor: 12.0.4(@types/node@25.9.1)(mem-fs@4.1.4) + mem-fs-editor: 12.0.4(@types/node@22.20.1)(mem-fs@4.1.4) npm-api: 1.0.1(debug@3.2.7)(encoding@0.1.13) semver: 7.6.3 strip-ansi: 4.0.0 text-table: 0.2.0 untildify: 3.0.3 - yeoman-generator: 4.13.0(@types/node@25.9.1)(encoding@0.1.13)(mem-fs@4.1.4) + yeoman-generator: 4.13.0(@types/node@22.20.1)(encoding@0.1.13)(mem-fs@4.1.4) transitivePeerDependencies: - '@types/node' - bare-abort-controller @@ -25770,7 +25617,7 @@ snapshots: - supports-color optional: true - yeoman-environment@3.19.3(@types/node@22.15.35): + yeoman-environment@3.19.3(@types/node@22.20.1): dependencies: '@npmcli/arborist': 4.3.1 are-we-there-yet: 2.0.0 @@ -25788,7 +25635,7 @@ snapshots: find-up: 5.0.0 globby: 11.1.0 grouped-queue: 2.1.0 - inquirer: 8.2.7(@types/node@22.15.35) + inquirer: 8.2.7(@types/node@22.20.1) is-scoped: 2.1.0 isbinaryfile: 4.0.10 lodash: 4.18.1 @@ -25916,7 +25763,7 @@ snapshots: - react-native-b4a - supports-color - yeoman-generator@4.13.0(@types/node@25.9.1)(encoding@0.1.13)(mem-fs@4.1.4): + yeoman-generator@4.13.0(@types/node@22.20.1)(encoding@0.1.13)(mem-fs@4.1.4): dependencies: async: 2.6.4 chalk: 2.4.2 @@ -25932,7 +25779,7 @@ snapshots: istextorbinary: 2.6.0 lodash: 4.18.1 make-dir: 3.1.0 - mem-fs-editor: 12.0.4(@types/node@25.9.1)(mem-fs@4.1.4) + mem-fs-editor: 12.0.4(@types/node@22.20.1)(mem-fs@4.1.4) minimist: 1.2.8 pretty-bytes: 5.6.0 read-chunk: 3.2.0 @@ -25945,7 +25792,7 @@ snapshots: through2: 3.0.2 optionalDependencies: grouped-queue: 1.1.0 - yeoman-environment: 2.10.3(@types/node@25.9.1)(encoding@0.1.13) + yeoman-environment: 2.10.3(@types/node@22.20.1)(encoding@0.1.13) transitivePeerDependencies: - '@types/node' - bare-abort-controller @@ -26065,19 +25912,19 @@ snapshots: - react-native-b4a - supports-color - yeoman-generator@8.2.2(@types/node@22.15.35)(@yeoman/types@1.11.1(@types/node@22.15.35)(@yeoman/adapter@4.0.2(@types/node@22.15.35))(mem-fs@4.1.4))(mem-fs@4.1.4): + yeoman-generator@8.2.2(@types/node@22.20.1)(@yeoman/types@1.11.1(@types/node@22.20.1)(@yeoman/adapter@4.0.2(@types/node@22.20.1))(mem-fs@4.1.4))(mem-fs@4.1.4): dependencies: '@types/debug': 4.1.13 '@types/lodash-es': 4.17.12 '@yeoman/namespace': 2.1.0 - '@yeoman/types': 1.11.1(@types/node@22.15.35)(@yeoman/adapter@4.0.2(@types/node@22.15.35))(mem-fs@4.1.4) + '@yeoman/types': 1.11.1(@types/node@22.20.1)(@yeoman/adapter@4.0.2(@types/node@22.20.1))(mem-fs@4.1.4) chalk: 5.6.2 debug: 4.4.3 execa: 9.6.1 latest-version: 9.0.0 lodash-es: 4.18.1 mem-fs: 4.1.4 - mem-fs-editor: 12.0.4(@types/node@22.15.35)(mem-fs@4.1.4) + mem-fs-editor: 12.0.4(@types/node@22.20.1)(mem-fs@4.1.4) minimist: 1.2.8 read-package-up: 12.0.0 semver: 7.8.1 @@ -26086,7 +25933,7 @@ snapshots: text-table: 0.2.0 type-fest: 5.8.0 optionalDependencies: - '@types/node': 22.15.35 + '@types/node': 22.20.1 transitivePeerDependencies: - bare-abort-controller - react-native-b4a diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0f99124a4..ee9eec338 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,6 +2,7 @@ packages: - 'packages/*' - 'projects/*/packages/*' - 'projects/*' + - 'projects/*/examples/*' - 'examples/*' overrides: 'mem-fs': '4.1.4' diff --git a/projects/vscode-logging/CHANGELOG.old.md b/projects/vscode-logging/CHANGELOG.old.md new file mode 100644 index 000000000..3e69ff1ef --- /dev/null +++ b/projects/vscode-logging/CHANGELOG.old.md @@ -0,0 +1,94 @@ +# Change Log + +> **Note:** This is the historic changelog from before vscode-logging was integrated into the app-studio-toolkit monorepo. +> Going forward, changes are tracked in the individual package changelogs under [`packages/`](./packages/). + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [2.0.9](https://github.com/SAP/vscode-logging/compare/v2.0.8...v2.0.9) (2026-04-16) + +**Note:** Version bump only for package root + +## [2.0.8](https://github.com/SAP/vscode-logging/compare/v2.0.7...v2.0.8) (2026-02-18) + +### Bug Fixes + +- correct repository URL case (SAP instead of sap) for npm provenance ([00a91ca](https://github.com/SAP/vscode-logging/commit/00a91ca002ba88ed27278f756b7aca8865eee1ee)) + +## [2.0.7](https://github.com/SAP/vscode-logging/compare/v2.0.6...v2.0.7) (2026-02-18) + +### Bug Fixes + +- upgrade lerna to 9.0.3 for better npm trusted publishers support ([396a9f1](https://github.com/SAP/vscode-logging/commit/396a9f1ebd27dbba01d41c6b299561d849d47a5b)) + +## [2.0.6](https://github.com/SAP/vscode-logging/compare/v2.0.5...v2.0.6) (2026-02-18) + +### Bug Fixes + +- add verbose logging to lerna publish ([7330c6e](https://github.com/SAP/vscode-logging/commit/7330c6e326446f867c22ba391e79077375190d11)) + +## [2.0.5](https://github.com/SAP/vscode-logging/compare/v2.0.4...v2.0.5) (2026-02-18) + +### Bug Fixes + +- add NPM_CONFIG_PROVENANCE for trusted publishers ([10d22fa](https://github.com/SAP/vscode-logging/commit/10d22faeef072af6fdb82f344d9496979b13279e)) + +## [2.0.4](https://github.com/SAP/vscode-logging/compare/v2.0.3...v2.0.4) (2026-02-18) + +### Bug Fixes + +- add registry-url for npm trusted publishers ([6937c19](https://github.com/SAP/vscode-logging/commit/6937c1902bfed6b02527c608fd011fab102818ee)) + +## [2.0.3](https://github.com/SAP/vscode-logging/compare/v2.0.2...v2.0.3) (2026-02-17) + +### Bug Fixes + +- pnpm.lock ([#245](https://github.com/SAP/vscode-logging/issues/245)) ([cba23f4](https://github.com/SAP/vscode-logging/commit/cba23f4c6169ac48f38ca1514b94210fc6fddf2e)) + +## [2.0.2](https://github.com/SAP/vscode-logging/compare/v2.0.1...v2.0.2) (2026-02-17) + +**Note:** Version bump only for package root + +## [2.0.1](https://github.com/SAP/vscode-logging/compare/v2.0.0...v2.0.1) (2026-02-15) + +### Bug Fixes + +- examples fix ([#242](https://github.com/SAP/vscode-logging/issues/242)) ([09afea5](https://github.com/SAP/vscode-logging/commit/09afea515f76238e2761c9cf5849bda589204794)) +- upgrade lodash to version 4.17.23 ([#241](https://github.com/SAP/vscode-logging/issues/241)) ([69fc2ce](https://github.com/SAP/vscode-logging/commit/69fc2ce233e960219efca1118027638cc115874c)) +- workspace protocol ([#243](https://github.com/SAP/vscode-logging/issues/243)) ([3091351](https://github.com/SAP/vscode-logging/commit/30913512554b29590b16bce4ad2a1eb3890b5dc9)) + +# 2.0.0 (2024-01-05) + +### Bug Fixes + +- add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/SAP/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) +- convert timestamp format according to ISO-8601 ([b4ab3e4](https://github.com/SAP/vscode-logging/commit/b4ab3e48829df42bd73c67de3f068385aabd1259)) +- source Location Tracking not affecting ChildLoggers ([72b151a](https://github.com/SAP/vscode-logging/commit/72b151a773ba2707cb131d59799389a7cfe93c85)), closes [#15](https://github.com/SAP/vscode-logging/issues/15) +- typo in the logging.level configuration description ([#181](https://github.com/SAP/vscode-logging/issues/181)) ([c05b17d](https://github.com/SAP/vscode-logging/commit/c05b17d4348e89f27a3cb86f2e20bc190cdf1afb)) +- update yarn.lock ([#144](https://github.com/SAP/vscode-logging/issues/144)) ([33ec5ac](https://github.com/SAP/vscode-logging/commit/33ec5acfda275b9717a5986012667b8e854c6c5e)) +- upgrade lodash version due to CVE-2021-23337 ([c514c16](https://github.com/SAP/vscode-logging/commit/c514c169ae5941cea9ebd5ae0dcb4eef8fc431d8)) +- **logger:** vscode-logging/types package should be a dependency ([e2ea6c7](https://github.com/SAP/vscode-logging/commit/e2ea6c7d26efed219f2b983ad7e601eeb9f4704f)) + +### Features + +- **wrapper:** remove dep to @types/vscode ([#167](https://github.com/SAP/vscode-logging/issues/167)) ([b362e5c](https://github.com/SAP/vscode-logging/commit/b362e5c3b11020ab09a5e705d7834fa53e8bd48e)) +- logger wrapper package ([#163](https://github.com/SAP/vscode-logging/issues/163)) ([fc6abc5](https://github.com/SAP/vscode-logging/commit/fc6abc5ea43403c3039edb8589c68a0a339e5ebc)) +- support console as log target ([#109](https://github.com/SAP/vscode-logging/issues/109)) ([ea16211](https://github.com/SAP/vscode-logging/commit/ea16211a5e2fbcdc86f4e96c8c60eaaf440d2431)) +- **logger:** a memory leak when creating 9 or more ChildLogger's ([#76](https://github.com/SAP/vscode-logging/issues/76)) ([482c708](https://github.com/SAP/vscode-logging/commit/482c708e9b8643849f6a14253c51650ffac70416)) +- aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/SAP/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) +- initial Commit ([ee780af](https://github.com/SAP/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) + +### Reverts + +- Revert "chore: include release number in release commit" ([d9d9c68](https://github.com/SAP/vscode-logging/commit/d9d9c68471476be517a06b3dd8712d573d3f7fa6)) + +* Make OutputChannel optional. (#55) ([b7fa564](https://github.com/SAP/vscode-logging/commit/b7fa56436693df9787f8ea720559beb3b0566612)), closes [#55](https://github.com/SAP/vscode-logging/issues/55) [#55](https://github.com/SAP/vscode-logging/issues/55) + +### BREAKING CHANGES + +- **wrapper:** `getConfigurations` and `onDidChangeConfiguration` properties + were removed from the `configureLogger` public API +- OutputChannel will not be created inside the logger as today. Alternatively, it + will be added as an optional parameter. The VS Code Extension will be able to create the + OutputChannel and send it to the logger as a parameter. diff --git a/projects/vscode-logging/README.md b/projects/vscode-logging/README.md new file mode 100644 index 000000000..1fd758fd1 --- /dev/null +++ b/projects/vscode-logging/README.md @@ -0,0 +1,28 @@ +[![REUSE status](https://api.reuse.software/badge/github.com/SAP/app-studio-toolkit)](https://api.reuse.software/info/github.com/SAP/app-studio-toolkit) + +# VSCode-Logging + +VSCode-Logging is a set of libraries for implementing logging functionality in [VSCode Extensions](https://code.visualstudio.com/api/get-started/your-first-extension). + +It contains the following packages: + +- [![npm-vscode-logging-logger][npm-vscode-logging-logger-image]][npm-vscode-logging-logger-url] [@vscode-logging/logger](./packages/logger) A logging library for VSCode Extensions. +- [![npm-vscode-logging-types][npm-vscode-logging-types-image]][npm-vscode-logging-types-url] [@vscode-logging/types](./packages/types) Common Logger Type Signatures for [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) scenarios. +- [![npm-vscode-logging-wrapper][npm-vscode-logging-wrapper-image]][npm-vscode-logging-wrapper-url] [@vscode-logging/wrapper](./packages/wrapper) An optional wrapper and utilities to reduce boilerplate when consuming @vscode-logging/logger. + +Usage examples are available in the [examples](./examples) folder. + +[npm-vscode-logging-logger-image]: https://img.shields.io/npm/v/@vscode-logging/logger.svg +[npm-vscode-logging-logger-url]: https://www.npmjs.com/package/@vscode-logging/logger +[npm-vscode-logging-types-image]: https://img.shields.io/npm/v/@vscode-logging/types.svg +[npm-vscode-logging-types-url]: https://www.npmjs.com/package/@vscode-logging/types +[npm-vscode-logging-wrapper-image]: https://img.shields.io/npm/v/@vscode-logging/wrapper.svg +[npm-vscode-logging-wrapper-url]: https://www.npmjs.com/package/@vscode-logging/wrapper + +## Support + +Please report issues [here](https://github.com/SAP/app-studio-toolkit/issues/new/choose) and label them with `project:vscode-logging`. + +## Contributing + +See [CONTRIBUTING.md](https://github.com/SAP/app-studio-toolkit/blob/main/CONTRIBUTING.md). diff --git a/projects/vscode-logging/examples/README.md b/projects/vscode-logging/examples/README.md new file mode 100644 index 000000000..eca6d9c2b --- /dev/null +++ b/projects/vscode-logging/examples/README.md @@ -0,0 +1,8 @@ +# @vscode-logging/examples + +This directory contains examples of using @vscode-logging packages + +The Examples: + +- [Integration into a VSCode Extension Example](./extension) +- [Using a @vscode-logging/logger in a "regular" npm library consumed by a VSCode Extension](./library) diff --git a/projects/vscode-logging/examples/extension-wrapper/.vscode/launch.json b/projects/vscode-logging/examples/extension-wrapper/.vscode/launch.json new file mode 100644 index 000000000..c3b3cbbc1 --- /dev/null +++ b/projects/vscode-logging/examples/extension-wrapper/.vscode/launch.json @@ -0,0 +1,17 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/dist/src/**/*.js"] + } + ] +} diff --git a/projects/vscode-logging/examples/extension-wrapper/.vscode/settings.json b/projects/vscode-logging/examples/extension-wrapper/.vscode/settings.json new file mode 100644 index 000000000..194c9b6b6 --- /dev/null +++ b/projects/vscode-logging/examples/extension-wrapper/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.insertSpaces": false +} diff --git a/projects/vscode-logging/examples/extension-wrapper/.vscode/tasks.json b/projects/vscode-logging/examples/extension-wrapper/.vscode/tasks.json new file mode 100644 index 000000000..078ff7e01 --- /dev/null +++ b/projects/vscode-logging/examples/extension-wrapper/.vscode/tasks.json @@ -0,0 +1,20 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/projects/vscode-logging/examples/extension-wrapper/README.md b/projects/vscode-logging/examples/extension-wrapper/README.md new file mode 100644 index 000000000..c0f89ca6f --- /dev/null +++ b/projects/vscode-logging/examples/extension-wrapper/README.md @@ -0,0 +1,35 @@ +# @vscode-logging Extension-Wrapper Example + +This folder contains a sample VS code extension that demonstrates +how to integrate @vscode-logging/**wrapper** in a VSCode extension. + +## Running the Sample + +### Initial setup (once) + +- `yarn` in this repository's **root**. + +### Run/Debug the Example Extension in VSCode + +- Open this example folder in VSCode. +- Press F5 in VSCode to start the example Extension. + +### Basic logging flow + +- Activate the **Hello World Command**: `View Menu -> Command Palette -> Hello World` +- Open the **Output Panel**: `View Menu -> Output` +- Switch to the `logging-wrapper-example` output Channel using the dropdown. +- Inspect the output channel log and note that the location of the log **Files** was written. +- Inspect the contents of the log files using your favorite file system explorer. + +### Changing logging Configuration. + +- Open the **Settings Page**: `File -> Preferences -> Settings` +- Locate the **Example_Logging: Logging Level** configuration. +- Play with these settings and inspect how the logging output changes. + +## Points of Interest in this Example: + +- [extension.ts](./src/extension.ts) - `configureLogger` API invocation (inside `activate` function). +- [logger.ts](./src/logger.ts) - State management for the logger instance. +- [commands.ts](./src/commands.ts) - The implementation of the `Hello World` Command. diff --git a/projects/vscode-logging/examples/extension-wrapper/package.json b/projects/vscode-logging/examples/extension-wrapper/package.json new file mode 100644 index 000000000..6eb026067 --- /dev/null +++ b/projects/vscode-logging/examples/extension-wrapper/package.json @@ -0,0 +1,63 @@ +{ + "name": "vscode-logging-extension-wrapper-example", + "private": true, + "displayName": "logging-wrapper-example", + "description": "Example using @vscode-logging/logger and @vscode-logging/wrapper in a VSCode Extension", + "version": "2.0.9", + "publisher": "SAP", + "engines": { + "vscode": "^1.52.0" + }, + "activationEvents": [ + "onCommand:extension.helloWorld" + ], + "main": "./dist/src/extension.js", + "scripts": { + "ci": "npm-run-all clean compile", + "clean": "rimraf dist", + "compile": "tsc" + }, + "contributes": { + "commands": [ + { + "command": "extension.helloWorld", + "title": "Hello World" + } + ], + "configuration": { + "type": "object", + "title": "Example_Logging", + "properties": { + "Example_Logging.loggingLevel": { + "type": "string", + "enum": [ + "off", + "fatal", + "error", + "warn", + "info", + "debug", + "trace" + ], + "default": "error", + "description": "The verbosity of logging. The Order is off < fatal < error < warn < info < debug < trace.", + "scope": "window" + }, + "Example_Logging.sourceLocationTracking": { + "type": "boolean", + "default": false, + "description": "Should Source Code Location Info be added to log entries, DANGER - May be very slow, only use in debugging scenarios", + "scope": "window" + } + } + } + }, + "dependencies": { + "@vscode-logging/wrapper": "workspace:*" + }, + "//@types/vscode": "Pinned to the minimum VS Code API version supported here (kept from the standalone vscode-logging repo). Intentionally not raised to the monorepo root version, which would allow newer APIs and break support for older VS Code.", + "devDependencies": { + "@types/vscode": "^1.52.0", + "@vscode-logging/types": "workspace:*" + } +} diff --git a/projects/vscode-logging/examples/extension-wrapper/src/commands.ts b/projects/vscode-logging/examples/extension-wrapper/src/commands.ts new file mode 100644 index 000000000..475d67f87 --- /dev/null +++ b/projects/vscode-logging/examples/extension-wrapper/src/commands.ts @@ -0,0 +1,20 @@ +import { ExtensionContext, commands, window } from "vscode"; +import { getLogger } from "./logger"; + +export function registerCommands( + subscriptions: ExtensionContext["subscriptions"] +): void { + let logMessagesCounter = 1; + const commandSubscription = commands.registerCommand( + "extension.helloWorld", + // By providing a real function name rather than a `fat arrow` ( ()=>{} ) + // The `sourceLocationTracking` can provide a meaningful output. + function registerCallback() { + window.showInformationMessage("Hello Cruel World!"); + getLogger().error( + `Hip Hip Hurray, the Command was executed! counter: <${logMessagesCounter++}>` + ); + } + ); + subscriptions.push(commandSubscription); +} diff --git a/projects/vscode-logging/examples/extension-wrapper/src/extension.ts b/projects/vscode-logging/examples/extension-wrapper/src/extension.ts new file mode 100644 index 000000000..ea844f1bc --- /dev/null +++ b/projects/vscode-logging/examples/extension-wrapper/src/extension.ts @@ -0,0 +1,9 @@ +import { ExtensionContext } from "vscode"; +import { getLogger, initLogger } from "./logger"; +import { registerCommands } from "./commands"; + +export async function activate(context: ExtensionContext): Promise { + await initLogger(context); + registerCommands(context.subscriptions); + getLogger().info("extension is active, hip hip hurray!"); +} diff --git a/projects/vscode-logging/examples/extension-wrapper/src/logger.ts b/projects/vscode-logging/examples/extension-wrapper/src/logger.ts new file mode 100644 index 000000000..928936eda --- /dev/null +++ b/projects/vscode-logging/examples/extension-wrapper/src/logger.ts @@ -0,0 +1,54 @@ +/** + * This file manages the logger's state. + */ +import { readFile as readFileCallback } from "fs"; +import { promisify } from "util"; +import { resolve } from "path"; +import { ok } from "assert"; +import { ExtensionContext, window } from "vscode"; +import { IChildLogger, IVSCodeExtLogger } from "@vscode-logging/types"; +import { configureLogger, NOOP_LOGGER } from "@vscode-logging/wrapper"; + +const readFile = promisify(readFileCallback); + +// On file load we initialize our logger to `NOOP_LOGGER` +// this is done because the "real" logger cannot be initialized during file load. +// only once the `activate` function has been called in extension.ts +// as the `ExtensionContext` argument to `activate` contains the required `logPath` +let loggerImpel: IVSCodeExtLogger = NOOP_LOGGER; + +export function getLogger(): IChildLogger { + return loggerImpel; +} + +function setLogger(newLogger: IVSCodeExtLogger): void { + loggerImpel = newLogger; +} + +const LOGGING_LEVEL_PROP = "Example_Logging.loggingLevel"; +const SOURCE_LOCATION_PROP = "Example_Logging.sourceLocationTracking"; + +export async function initLogger(context: ExtensionContext): Promise { + const meta = JSON.parse( + await readFile(resolve(context.extensionPath, "package.json"), "utf8") + ); + + // By asserting the existence of the properties in the package.json + // at runtime, we avoid many copy-pasta mistakes... + const configProps = meta?.contributes?.configuration?.properties; + ok(configProps?.[LOGGING_LEVEL_PROP]); + ok(configProps?.[SOURCE_LOCATION_PROP]); + + const extLogger = configureLogger({ + extName: meta.displayName, + logPath: context.logPath, + logOutputChannel: window.createOutputChannel(meta.displayName), + // set to `true` if you also want your VSCode extension to log to the console. + logConsole: false, + loggingLevelProp: LOGGING_LEVEL_PROP, + sourceLocationProp: SOURCE_LOCATION_PROP, + subscriptions: context.subscriptions, + }); + + setLogger(extLogger); +} diff --git a/projects/vscode-logging/examples/extension-wrapper/tsconfig.json b/projects/vscode-logging/examples/extension-wrapper/tsconfig.json new file mode 100644 index 000000000..19d941f92 --- /dev/null +++ b/projects/vscode-logging/examples/extension-wrapper/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "dist", + "baseUrl": "." + }, + "include": ["./src/**/*.ts", "./api.d.ts"] +} diff --git a/projects/vscode-logging/examples/extension/.vscode/launch.json b/projects/vscode-logging/examples/extension/.vscode/launch.json new file mode 100644 index 000000000..c475e6199 --- /dev/null +++ b/projects/vscode-logging/examples/extension/.vscode/launch.json @@ -0,0 +1,28 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/lib/**/*.js"] + }, + { + "name": "Run Extension Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/test" + ], + "outFiles": ["${workspaceFolder}/test/**/*.js"] + } + ] +} diff --git a/projects/vscode-logging/examples/extension/.vscode/settings.json b/projects/vscode-logging/examples/extension/.vscode/settings.json new file mode 100644 index 000000000..194c9b6b6 --- /dev/null +++ b/projects/vscode-logging/examples/extension/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.insertSpaces": false +} diff --git a/projects/vscode-logging/examples/extension/.vscode/tasks.json b/projects/vscode-logging/examples/extension/.vscode/tasks.json new file mode 100644 index 000000000..078ff7e01 --- /dev/null +++ b/projects/vscode-logging/examples/extension/.vscode/tasks.json @@ -0,0 +1,20 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/projects/vscode-logging/examples/extension/README.md b/projects/vscode-logging/examples/extension/README.md new file mode 100644 index 000000000..09e659ac7 --- /dev/null +++ b/projects/vscode-logging/examples/extension/README.md @@ -0,0 +1,39 @@ +# @vscode-logging Extension Example + +This folder contains a sample VS code extension that demonstrates +how to integrate @vscode-logging/logger in a VSCode extension. + +## Running the Sample + +### Initial setup (once) + +- `yarn` in this repository's **root**. + +### Run/Debug the Example Extension in VSCode + +- Open this example folder in VSCode. +- Press F5 in VSCode to start the example Extension. + +### Basic logging flow + +- Activate the **Hello World Command**: `View Menu -> Command Palette -> Hello World` +- Open the **Output Panel**: `View Menu -> Output` +- Switch to the `vscode-logging-extension-example` output Channel using the dropdown. +- Inspect the output channel log and note that the location of the log **Files** was written. +- Inspect the contents of the log files using your favorite file system explorer. + +### Changing logging Configuration. + +- Open the **Settings Page**: `File -> Preferences -> Settings` +- Locate the **Example_Logging: Logging Level** configuration. +- Play with these settings and inspect how the logging output changes. + +## Points of Interest in this Example: + +- [extension.js](./lib/extension.js) - The main entry point and where the `activate()` method is implemented. +- [logger-wrapper.js](./lib/logger-wrapper.js) - Access point to the Logger for our VSCode Extension Files/Modules. +- [package.json -> configuration](./package.json) - Declaring end user configurable logging settings. +- [settings.js](./lib/settings.js) - Logic to read the logging configuration from the VSCode settings. +- [settings-changes-handler.js](./lib/settings-changes-handler.js) - Listening to VSCode settings changes and reacting by updating the Logger configuration. +- [passing-logger-to-library.js](./lib/passing-logger-to-library.js) - A simple Dependency Injection of the Logger to a consumed (external) library. +- [commands.js](./lib/commands.js) - The implementation of the `Hello World` Command. diff --git a/projects/vscode-logging/examples/extension/lib/commands.js b/projects/vscode-logging/examples/extension/lib/commands.js new file mode 100644 index 000000000..9202d412c --- /dev/null +++ b/projects/vscode-logging/examples/extension/lib/commands.js @@ -0,0 +1,28 @@ +const vscode = require("vscode"); + +// Our modules consume the logger via the logger-wrapper module +const { getLogger } = require("./logger-wrapper"); + +/** + * + * @param {vscode.ExtensionContext} context + */ +function registerCommands(context) { + let logMessagesCounter = 1; + const commandSubscription = vscode.commands.registerCommand( + "extension.helloWorld", + // By providing a real function name rather then a `fat arrow` ( ()=>{} ) + // The `sourceLocationTracking` can provide a meaningful output. + function registerCallback() { + vscode.window.showInformationMessage("Hello Cruel World!"); + getLogger().error( + `Hip Hip Hurray, the Command was executed! counter: <${logMessagesCounter++}>` + ); + } + ); + context.subscriptions.push(commandSubscription); +} + +module.exports = { + registerCommands: registerCommands, +}; diff --git a/projects/vscode-logging/examples/extension/lib/extension.js b/projects/vscode-logging/examples/extension/lib/extension.js new file mode 100644 index 000000000..68f574c97 --- /dev/null +++ b/projects/vscode-logging/examples/extension/lib/extension.js @@ -0,0 +1,70 @@ +const { resolve } = require("path"); +const vscode = require("vscode"); +const { getExtensionLogger } = require("@vscode-logging/logger"); +const { + getLoggingLevelSetting, + getSourceLocationTrackingSetting, +} = require("./settings"); +const { registerCommands } = require("./commands"); +const { listenToLogSettingsChanges } = require("./settings-changes-handler"); +const { callLibraryAndPassLogger } = require("./passing-logger-to-library"); +const { getLogger, initLogger } = require("./logger-wrapper"); + +/** + * Entry Point for a VSCode Extension. + * + * @param {vscode.ExtensionContext} context + */ +function activate(context) { + // The `logPath` is only available from the `ExtensionContext` + const logPath = context.logPath; + const logLevelSetting = getLoggingLevelSetting(); + const sourceLocationTrackingSettings = getSourceLocationTrackingSetting(); + const meta = require(resolve(__dirname, "..", "package.json")); + const logOutputChannel = vscode.window.createOutputChannel(meta.name); + + // The Logger must first be initialized before any logging commands may be invoked. + const extLogger = getExtensionLogger({ + extName: meta.name, + level: logLevelSetting, + logPath: logPath, + logOutputChannel: logOutputChannel, + sourceLocationTracking: sourceLocationTrackingSettings, + logConsole: true, + }); + + // Update our logger-wrapper with a reference to the extLogger. + initLogger(extLogger); + + // Lets log some useful entries... + getLogger().info( + `Extension: <${meta.name}> version: <${meta.version}> has been activated` + ); + getLogger().info(`Rotating Log Files will be written to: <${logPath}>`); + + // Simple Dependency Injection example of using the VSCode Logger in a "consumed" npm library/package. + callLibraryAndPassLogger(); + + getLogger().info(`Testing logging info level`); + getLogger().warn(`Testing logging warn level`); + getLogger().error(`Testing logging error level`); + + registerCommands(context); + + // We must subscribe to settings changes, otherwise the Logger will always remain in its initial state. + listenToLogSettingsChanges(context); +} + +/** + * this method is called when your extension is deactivated + */ +function deactivate() { + getLogger().info( + "Sad Sad Panda: extension was deactivated, why oh why?" + ); +} + +module.exports = { + activate, + deactivate, +}; diff --git a/projects/vscode-logging/examples/extension/lib/logger-wrapper.js b/projects/vscode-logging/examples/extension/lib/logger-wrapper.js new file mode 100644 index 000000000..6137e8d79 --- /dev/null +++ b/projects/vscode-logging/examples/extension/lib/logger-wrapper.js @@ -0,0 +1,37 @@ +/** + * A Simple Wrapper to hold the state of our "singleton" (per extension) IVSCodeExtLogger + * implementation. + */ + +/** + * @type {IVSCodeExtLogger} + */ +let _logger; +let isInitialized = false; + +/** + * Note the use of a getter function so the value would be lazy resolved on each use. + * This enables concise and simple consumption of the Logger throughout our Extension. + * + * @returns { IVSCodeExtLogger } + */ +function getLogger() { + if (!isInitialized) { + throw Error("Logger has not yet been initialized!"); + } + return _logger; +} + +/** + * This function should be invoked after the Logger has been initialized in the Extension's `activate` function. + * @param {IVSCodeExtLogger} newLogger + */ +function initLogger(newLogger) { + isInitialized = true; + _logger = newLogger; +} + +module.exports = { + getLogger, + initLogger, +}; diff --git a/projects/vscode-logging/examples/extension/lib/passing-logger-to-library.js b/projects/vscode-logging/examples/extension/lib/passing-logger-to-library.js new file mode 100644 index 000000000..85c71e1ec --- /dev/null +++ b/projects/vscode-logging/examples/extension/lib/passing-logger-to-library.js @@ -0,0 +1,22 @@ +const { Calculator } = require("@vscode-logging/library-example"); + +// Our modules consume the logger via the logger-wrapper module +const { getLogger } = require("./logger-wrapper"); + +function callLibraryAndPassLogger() { + // Using a childLogger will add a suffix to the `label` information in each logEntry. + const calculatorChildLogger = getLogger().getChildLogger({ + label: "Calculator", + }); + + const calc = new Calculator(calculatorChildLogger); + + // These methods will now log the same log targets as our VSCode Extension Logger (OutChannel / Rolling File) + calc.add(1, 2); + calc.subtract(5, 3); + calc.multiply(2, 6); +} + +module.exports = { + callLibraryAndPassLogger, +}; diff --git a/projects/vscode-logging/examples/extension/lib/settings-changes-handler.js b/projects/vscode-logging/examples/extension/lib/settings-changes-handler.js new file mode 100644 index 000000000..b43009b16 --- /dev/null +++ b/projects/vscode-logging/examples/extension/lib/settings-changes-handler.js @@ -0,0 +1,45 @@ +const vscode = require("vscode"); + +const { + LOGGING_LEVEL_CONFIG_PROP, + SOURCE_TRACKING_CONFIG_PROP, +} = require("./settings"); + +// Our modules consume the logger via the logger-wrapper module +const { getLogger } = require("./logger-wrapper"); + +/** + * @param {vscode.ExtensionContext} context + */ +function listenToLogSettingsChanges(context) { + // To enable dynamic logging level we must listen to VSCode configuration changes + // on our `loggingLevelConfigProp` configuration setting. + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration(LOGGING_LEVEL_CONFIG_PROP)) { + const logLevel = vscode.workspace + .getConfiguration() + .get(LOGGING_LEVEL_CONFIG_PROP); + + getLogger().changeLevel(logLevel); + } + }) + ); + + // Enable responding to changes in the sourceLocationTracking setting + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration(SOURCE_TRACKING_CONFIG_PROP)) { + const newSourceLocationTracking = vscode.workspace + .getConfiguration() + .get(SOURCE_TRACKING_CONFIG_PROP); + + getLogger().changeSourceLocationTracking(newSourceLocationTracking); + } + }) + ); +} + +module.exports = { + listenToLogSettingsChanges, +}; diff --git a/projects/vscode-logging/examples/extension/lib/settings.js b/projects/vscode-logging/examples/extension/lib/settings.js new file mode 100644 index 000000000..a320f0ff6 --- /dev/null +++ b/projects/vscode-logging/examples/extension/lib/settings.js @@ -0,0 +1,28 @@ +const vscode = require("vscode"); + +/** + * Note that the values of these configuration properties must match those defined in the package.json + */ +const LOGGING_LEVEL_CONFIG_PROP = "Example_Logging.loggingLevel"; +const SOURCE_TRACKING_CONFIG_PROP = "Example_Logging.sourceLocationTracking"; + +/** + * @returns {LogLevel} + */ +function getLoggingLevelSetting() { + return vscode.workspace.getConfiguration().get(LOGGING_LEVEL_CONFIG_PROP); +} + +/** + * @returns {boolean} + */ +function getSourceLocationTrackingSetting() { + return vscode.workspace.getConfiguration().get(SOURCE_TRACKING_CONFIG_PROP); +} + +module.exports = { + LOGGING_LEVEL_CONFIG_PROP, + SOURCE_TRACKING_CONFIG_PROP, + getLoggingLevelSetting, + getSourceLocationTrackingSetting, +}; diff --git a/projects/vscode-logging/examples/extension/package.json b/projects/vscode-logging/examples/extension/package.json new file mode 100644 index 000000000..835812213 --- /dev/null +++ b/projects/vscode-logging/examples/extension/package.json @@ -0,0 +1,59 @@ +{ + "name": "vscode-logging-extension-example", + "private": true, + "displayName": "logging-example", + "description": "Example using @vscode-logging/logger in a VSCode Extension", + "version": "2.0.9", + "publisher": "SAP", + "engines": { + "vscode": "^1.32.0" + }, + "activationEvents": [ + "onCommand:extension.helloWorld" + ], + "main": "./lib/extension.js", + "contributes": { + "commands": [ + { + "command": "extension.helloWorld", + "title": "Hello World" + } + ], + "configuration": { + "type": "object", + "title": "Example_Logging", + "properties": { + "Example_Logging.loggingLevel": { + "type": "string", + "enum": [ + "off", + "fatal", + "error", + "warn", + "info", + "debug", + "trace" + ], + "default": "info", + "description": "The verbosity of logging. The Order is off < fatal < error < warn < info < debug < trace.", + "scope": "resource" + }, + "Example_Logging.sourceLocationTracking": { + "type": "boolean", + "default": false, + "description": "Should Source Code Location Info be added to log entries, DANGER - May be very slow, only use in debugging scenarios", + "scope": "resource" + } + } + } + }, + "scripts": {}, + "dependencies": { + "@vscode-logging/library-example": "workspace:*", + "@vscode-logging/logger": "workspace:*" + }, + "//@types/vscode": "Pinned to the minimum VS Code API version supported here (kept from the standalone vscode-logging repo). Intentionally not raised to the monorepo root version, which would allow newer APIs and break support for older VS Code.", + "devDependencies": { + "@types/vscode": "^1.32.0" + } +} diff --git a/projects/vscode-logging/examples/library/README.md b/projects/vscode-logging/examples/library/README.md new file mode 100644 index 000000000..9cfc9ca36 --- /dev/null +++ b/projects/vscode-logging/examples/library/README.md @@ -0,0 +1,9 @@ +# @vscode-logging Library DI Example + +This folder contains an npm package example which uses simple constructor argument +[Dependency Injection][di] in order to use the @vscode-logging/logger in an npm library called from a VSCode Extension. + +This "library" is consumed by the extension example's [passing-logger-to-library.js](../extension/lib/passing-logger-to-library.js), +file. + +[di]: https://en.wikipedia.org/wiki/Dependency_injection diff --git a/projects/vscode-logging/examples/library/index.js b/projects/vscode-logging/examples/library/index.js new file mode 100644 index 000000000..5c03466ca --- /dev/null +++ b/projects/vscode-logging/examples/library/index.js @@ -0,0 +1,51 @@ +const NO_OPERATION = () => {}; + +/** + * Empty Implementation of the Logger in case none is provided via Dependency Injection. + * An alternative implementation could log to the console or provide another (real) implementation. + * + * @type {IChildLogger} + */ +const noopLogger = { + fatal: NO_OPERATION, + error: NO_OPERATION, + warn: NO_OPERATION, + info: NO_OPERATION, + debug: NO_OPERATION, + trace: NO_OPERATION, + getChildLogger: function (opts) { + return noopLogger; + }, +}; + +class Calculator { + /** + * @param {IChildLogger} [logger] + */ + constructor(logger = noopLogger) { + this.logger = logger; + } + + subtract(lhs, rhs) { + // Adding Metadata Objects to the JSON structured log entry. + this.logger.info("Subtract called with arguments:", { lhs: lhs, rhs: rhs }); + return lhs - rhs; + } + + add(lhs, rhs) { + // Using ECMAScript Template Literals to construct the message (Recommended!) + // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals + this.logger.warn(`add called with arguments: <${lhs}> + <${rhs}>`); + return lhs + rhs; + } + + multiply(lhs, rhs) { + // using splat style to construct the message + this.logger.error("multiple called with arguments: <%d> + <%d>", lhs, rhs); + return lhs * rhs; + } +} + +module.exports = { + Calculator: Calculator, +}; diff --git a/projects/vscode-logging/examples/library/package.json b/projects/vscode-logging/examples/library/package.json new file mode 100644 index 000000000..528033343 --- /dev/null +++ b/projects/vscode-logging/examples/library/package.json @@ -0,0 +1,12 @@ +{ + "name": "@vscode-logging/library-example", + "version": "2.0.8", + "private": true, + "description": "Example node library using @vscode-logging/logger via Dependency Injection", + "main": "index.js", + "repository": "https://github.com/sap/vscode-logging/", + "license": "Apache-2.0", + "devDependencies": { + "@vscode-logging/types": "workspace:*" + } +} diff --git a/projects/vscode-logging/packages/logger/.mocharc.js b/projects/vscode-logging/packages/logger/.mocharc.js new file mode 100644 index 000000000..d67fddbd6 --- /dev/null +++ b/projects/vscode-logging/packages/logger/.mocharc.js @@ -0,0 +1,9 @@ +const chai = require("chai"); +const chaiExclude = require("chai-exclude"); + +chai.use(chaiExclude); + +module.exports = { + // If coverage is enabled each test takes quite a while + timeout: 5000, +}; diff --git a/projects/vscode-logging/packages/logger/.npmignore b/projects/vscode-logging/packages/logger/.npmignore new file mode 100644 index 000000000..ce94c8112 --- /dev/null +++ b/projects/vscode-logging/packages/logger/.npmignore @@ -0,0 +1,16 @@ +# Mocha +.mocharc.js + +# TypeScript +tsconfig.json + +# NYC +.nyc_output +nyc.config.js +coverage + +# tests +test + +# Dev Docs +CONTRIBUTING.md \ No newline at end of file diff --git a/projects/vscode-logging/packages/logger/CHANGELOG.old.md b/projects/vscode-logging/packages/logger/CHANGELOG.old.md new file mode 100644 index 000000000..5608544a4 --- /dev/null +++ b/projects/vscode-logging/packages/logger/CHANGELOG.old.md @@ -0,0 +1,125 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [2.0.9](https://github.com/SAP/vscode-logging/compare/v2.0.8...v2.0.9) (2026-04-16) + +**Note:** Version bump only for package @vscode-logging/logger + +## [2.0.8](https://github.com/SAP/vscode-logging/compare/v2.0.7...v2.0.8) (2026-02-18) + +### Bug Fixes + +- correct repository URL case (SAP instead of sap) for npm provenance ([00a91ca](https://github.com/SAP/vscode-logging/commit/00a91ca002ba88ed27278f756b7aca8865eee1ee)) + +## [2.0.7](https://github.com/sap/vscode-logging/compare/v2.0.6...v2.0.7) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/logger + +## [2.0.6](https://github.com/sap/vscode-logging/compare/v2.0.5...v2.0.6) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/logger + +## [2.0.5](https://github.com/sap/vscode-logging/compare/v2.0.4...v2.0.5) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/logger + +## [2.0.4](https://github.com/sap/vscode-logging/compare/v2.0.3...v2.0.4) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/logger + +## [2.0.3](https://github.com/sap/vscode-logging/compare/v2.0.2...v2.0.3) (2026-02-17) + +**Note:** Version bump only for package @vscode-logging/logger + +## [2.0.2](https://github.com/sap/vscode-logging/compare/v2.0.1...v2.0.2) (2026-02-17) + +**Note:** Version bump only for package @vscode-logging/logger + +## [2.0.1](https://github.com/sap/vscode-logging/compare/v2.0.0...v2.0.1) (2026-02-15) + +### Bug Fixes + +- upgrade lodash to version 4.17.23 ([#241](https://github.com/sap/vscode-logging/issues/241)) ([69fc2ce](https://github.com/sap/vscode-logging/commit/69fc2ce233e960219efca1118027638cc115874c)) +- workspace protocol ([#243](https://github.com/sap/vscode-logging/issues/243)) ([3091351](https://github.com/sap/vscode-logging/commit/30913512554b29590b16bce4ad2a1eb3890b5dc9)) + +# 2.0.0 (2024-01-05) + +### Bug Fixes + +- add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/sap/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) +- convert timestamp format according to ISO-8601 ([b4ab3e4](https://github.com/sap/vscode-logging/commit/b4ab3e48829df42bd73c67de3f068385aabd1259)) +- **logger:** vscode-logging/types package should be a dependency ([e2ea6c7](https://github.com/sap/vscode-logging/commit/e2ea6c7d26efed219f2b983ad7e601eeb9f4704f)) +- source Location Tracking not affecting ChildLoggers ([72b151a](https://github.com/sap/vscode-logging/commit/72b151a773ba2707cb131d59799389a7cfe93c85)), closes [#15](https://github.com/sap/vscode-logging/issues/15) + +### Features + +- support console as log target ([#109](https://github.com/sap/vscode-logging/issues/109)) ([ea16211](https://github.com/sap/vscode-logging/commit/ea16211a5e2fbcdc86f4e96c8c60eaaf440d2431)) +- **logger:** a memory leak when creating 9 or more ChildLogger's ([#76](https://github.com/sap/vscode-logging/issues/76)) ([482c708](https://github.com/sap/vscode-logging/commit/482c708e9b8643849f6a14253c51650ffac70416)) +- aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/sap/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) +- initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) + +* Make OutputChannel optional. (#55) ([b7fa564](https://github.com/sap/vscode-logging/commit/b7fa56436693df9787f8ea720559beb3b0566612)), closes [#55](https://github.com/sap/vscode-logging/issues/55) [#55](https://github.com/sap/vscode-logging/issues/55) + +### BREAKING CHANGES + +- OutputChannel will not be created inside the logger as today. Alternatively, it + will be added as an optional parameter. The VS Code Extension will be able to create the + OutputChannel and send it to the logger as a parameter. + +## [1.2.3](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.2.2...@vscode-logging/logger@1.2.3) (2021-04-13) + +**Note:** Version bump only for package @vscode-logging/logger + +## [1.2.2](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.2.1...@vscode-logging/logger@1.2.2) (2020-12-10) + +**Note:** Version bump only for package @vscode-logging/logger + +## [1.2.1](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.2.0...@vscode-logging/logger@1.2.1) (2020-11-12) + +### Bug Fixes + +- convert timestamp format according to ISO-8601 ([b4ab3e4](https://github.com/sap/vscode-logging/commit/b4ab3e48829df42bd73c67de3f068385aabd1259)) + +# [1.2.0](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.1.0...@vscode-logging/logger@1.2.0) (2020-07-29) + +### Features + +- support console as log target ([#109](https://github.com/sap/vscode-logging/issues/109)) ([ea16211](https://github.com/sap/vscode-logging/commit/ea16211a5e2fbcdc86f4e96c8c60eaaf440d2431)) + +# [1.1.0](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.0.0...@vscode-logging/logger@1.1.0) (2020-05-21) + +### Features + +- **logger:** a memory leak when creating 9 or more ChildLogger's ([#76](https://github.com/sap/vscode-logging/issues/76)) ([482c708](https://github.com/sap/vscode-logging/commit/482c708e9b8643849f6a14253c51650ffac70416)) + +# [1.0.0](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@0.1.2...@vscode-logging/logger@1.0.0) (2020-04-16) + +- Make OutputChannel optional. (#55) ([b7fa564](https://github.com/sap/vscode-logging/commit/b7fa56436693df9787f8ea720559beb3b0566612)), closes [#55](https://github.com/sap/vscode-logging/issues/55) [#55](https://github.com/sap/vscode-logging/issues/55) + +### BREAKING CHANGES + +- OutputChannel will not be created inside the logger as today. Alternatively, it + will be added as an optional parameter. The VS Code Extension will be able to create the + OutputChannel and send it to the logger as a parameter. + +## [0.1.2](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@0.1.1...@vscode-logging/logger@0.1.2) (2020-02-19) + +### Bug Fixes + +- add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/sap/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) +- **logger:** vscode-logging/types package should be a dependency ([e2ea6c7](https://github.com/sap/vscode-logging/commit/e2ea6c7d26efed219f2b983ad7e601eeb9f4704f)) + +## [0.1.1](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@0.1.0...@vscode-logging/logger@0.1.1) (2020-01-20) + +### Bug Fixes + +- source Location Tracking not affecting ChildLoggers ([72b151a](https://github.com/sap/vscode-logging/commit/72b151a773ba2707cb131d59799389a7cfe93c85)), closes [#15](https://github.com/sap/vscode-logging/issues/15) + +# 0.1.0 (2020-01-14) + +### Features + +- aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/sap/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) +- initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) diff --git a/projects/vscode-logging/packages/logger/README.md b/projects/vscode-logging/packages/logger/README.md new file mode 100644 index 000000000..c093ab30d --- /dev/null +++ b/projects/vscode-logging/packages/logger/README.md @@ -0,0 +1,141 @@ +# @vscode-logging/logger + +A Logging Library for VSCode Extension which supports the following features: + +- JSON structure log entries output. +- Logging to a VSCode outputChannel. +- Logging to rolling file logs. +- [Source Location Tracking](#on-sourcelocationtracking). + +## Installation + +With npm: + +- `npm install @vscode-logging/logger --save` + +With Yarn: + +- `yarn add @vscode-logging/logger` + +## Usage + +Please see the [TypeScript Definitions](./api.d.ts) for full API details. + +Runnable and documented usage examples can be found in the [examples](../../examples) folder. +It is recommended to review these examples as integrating @vscode-logging/logger into a +VSCode Extension necessitates **multiple changes**, mainly around managing the configuration options +as user exposed settings. + +### Basic Usage + +The only function exposed (directly) by @vscode-logging/logger is `getExtensionLogger` which +should be invoked by a VSCode extension's `activate()` function. + +```javascript +const { getExtensionLogger } = require("@vscode-logging/logger"); + +function activate(context) { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + level: "info", // See LogLevel type in @vscode-logging/types for possible logLevels + logPath: context.logPath, // The logPath is only available from the `vscode.ExtensionContext` + logOutputChannel: logOutputChannel, // OutputChannel for the logger + sourceLocationTracking: false, + logConsol: false, // define if messages should be logged to the consol + }); + + extLogger.warn("Hello World"); + // Will Log The following entry to **both** + // - To outputChannel `logOutputChannel` + // - To log files in `logPath` + // { + // "label": "MyExtName", + // "level": "warn", + // "message": "Hello World", + // "time": "2020-01-10 15:29:52.038Z" + // } +} +``` + +### On ChildLoggers + +The `getChildLogger` API is available on the interface returned by `getExtensionLogger`. +This can be used to obtain sub logger which log to the same targets (outChannel/files) as +the root logger, but with a more specific label. + +```javascript +const { getExtensionLogger } = require("@vscode-logging/logger"); + +const extLogger = getExtensionLogger({ extName: "MyExtName" /* ... */ }); + +const childLogger = extLogger.getChildLogger({ label: "MyClass" }); +childLogger.warn("Hello World"); +// Will Log to the same targets as extLogger but with a suffix added to the `label` +// { +// "label": "MyExtName.MyClass", // Note the `.MyClass` suffix in the label +// "level": "warn", +// "message": "Hello World", +// "time": "2020-01-10 15:29:52.038Z" +// } + +const grandChildLogger = childLogger.getChildLogger({ label: "MyMethod" }); +grandChildLogger.warn("Hip Hip Hurray"); +// { +// "label": "MyExtName.MyClass.MyMethod", // Note the `.MyMethod` suffix in the label +// "level": "warn", +// "message": "Hello World", +// "time": "2020-01-10 15:30:52.038Z" +// } +``` + +Important notes on child loggers: + +- They share the same configuration (e.g logging level) as the root extension logger. +- Child Loggers cannot change their configuration, all configuration mutation must be done + via the root extension logger. +- Child Loggers can be created from other child loggers, the `label` property would simply + expend with the additional suffixes. +- Child Loggers are **cached** using their label as the key, so repeatedly calling `getChildLogger` + using the same `label` on the same Logger object would return the **same** childLogger object. + +### On `sourceLocationTracking` + +When enabled the `sourceLocationTracking` will augment the log entries with the function +name and location (file/line/column) where the log method was invoked, e.g: + +```json +{ + "label": "osem", + "level": "error", + "message": "Hip Hip Hurray, the Command was executed! counter: <1>", + "source": { + "function": "registerCalback", + "location": "c:\\workspace\\vscode-logging\\examples\\extension\\lib\\commands.js:21:19" + }, + "time": "2020-01-11 11:51:48.659Z" +} +``` + +Important things to note on `sourceLocationTracking` + +- This functionality is not guaranteed to always work. +- e.g anonymous functions have no name... + +- Obtaining the source location information is **very slow**. + + - This means that this feature should **not be used** in productive flows. + - Therefore A "Fatal" log entry will be logged each time this option is enabled. + +- Processes which manipulate the source code such as [bundling][vscode-bundling] or compilation (TypeScript/Babel) + may make the information produced less relevant due to the lack of sourceMaps support. + Therefore this feature may be more useful during development flows rather then productive flows. + +[vscode-bundling]: https://code.visualstudio.com/api/working-with-extensions/bundling-extension + +## Support + +Please report issues [here](https://github.com/SAP/app-studio-toolkit/issues/new/choose) and label them with `project:vscode-logging`. + +## Contributing + +See [CONTRIBUTING.md](https://github.com/SAP/app-studio-toolkit/blob/main/CONTRIBUTING.md). diff --git a/projects/vscode-logging/packages/logger/api.d.ts b/projects/vscode-logging/packages/logger/api.d.ts new file mode 100644 index 000000000..5b05741bc --- /dev/null +++ b/projects/vscode-logging/packages/logger/api.d.ts @@ -0,0 +1,76 @@ +/** + * Get A Root VSCode Extension Logger capable of Logging to: + * - Rolling Log File (optional) + * - VSCode OutputChannel + */ +import { IVSCodeExtLogger, LogLevel } from "@vscode-logging/types"; +export { + IVSCodeExtLogger, + IChildLogger, + LogLevel, +} from "@vscode-logging/types"; + +/** + * This is a sub-type of VSCode.OutputChannel. + * We define it to avoid direct dependency to @vscode/types. + * In some edge cases having multiple versions of @vscode/types can cause compilation errors. + */ +export type BasicOutputChannel = { + appendLine(value: string): void; + show(): void; + dispose(): void; +}; + +export function getExtensionLogger( + opts: getExtensionLoggerOpts +): IVSCodeExtLogger; + +export type getExtensionLoggerOpts = { + /** + * This parameter will be used for two things: + * + * + * - Root Label used when creating log entries. + * This will also be used as the prefix label for any childLogger e.g: + * - "Root.child" + * - "Root.child.grandChild" + */ + extName: string; + /** + * The Initial Log Level to use. + * This should normally provided by a configuration setting exposed to the Extension's end-users. + * - See: https://github.com/SAP/vscode-logging/tree/master/examples/extension for a runnable example + * exposing logging configuration to the end user. + */ + level: LogLevel; + /** + * Optional directory where the rolling log files should be kept. + * If this is not passed no rolling File Logs will be used. + * + * If the directory does not exist it would be created. + * + * It is normally expected to send the `ExtensionContext.logPath` in the parameter, + * - https://code.visualstudio.com/api/references/vscode-api#ExtensionContext.logPath + * which means `getExtensionLogger` should be called from the Extension's main `activate` function. + * - https://code.visualstudio.com/api/references/activation-events#Start-up + */ + logPath?: string; + /** + * Flag for adding sourceLocation information to each logEntry. + * This is disabled by default and should only be enabled during debugging flows as it + * Will likely cause significant performance regressions in productive flows. + */ + sourceLocationTracking?: boolean; + /** + * Optional Output channel where the logs should be shown. + * If this is not passed no Output channel will be used. + */ + logOutputChannel?: BasicOutputChannel; + /** + * Optional Console output channel, if set to true the log message will be printed to the Console output stream (stdout/stderr in Linux) + * Error and Fatal messages are printed to the standard error console + * Warn, Info, Debug and Trace are printed to the standard output console + * Default = false + */ + logConsole?: boolean; +}; diff --git a/projects/vscode-logging/packages/logger/lib/api.js b/projects/vscode-logging/packages/logger/lib/api.js new file mode 100644 index 000000000..4337b7767 --- /dev/null +++ b/projects/vscode-logging/packages/logger/lib/api.js @@ -0,0 +1,100 @@ +const { has } = require("lodash"); +const { createLogger } = require("winston"); +/** + * Normally we would prefer to depend upon `vscode` and `streamroller` inside their respective transports. + * However, by depending on those here in `api.js` we are able to more easily mock/stub these + * using proxyQuire. + * + * This is the lesser evil as the other options of: + * - Using `@global` with proxyQuire. + * - Running fuller (much more copmlex) integration tests with a real VSCode and real files. + * + * Seem much worse. + */ +const { RollingFileStream } = require("streamroller"); + +const { buildLoggerFormat } = require("./format"); +const { VSCodeExtLogger } = require("./logger"); +const { + VscodeOutChannelTransport, +} = require("./transports/vscode-out-channel"); +const { RollingFileTransport } = require("./transports/rolling-file"); +const { levelsConfig, isValidLogLevel, levels } = require("./levels"); +const { ConsoleTransport } = require("./transports/console-output"); + +/** + * @param {import("../api").getExtensionLoggerOpts} opts + * @return {import("@vscode-logging/types").IVSCodeExtLogger} + */ +function getExtensionLogger(opts) { + if (isValidLogLevel(opts.level) === false) { + throw Error(`Attempt to use unknown logging level: <${opts.level}>!`); + } + + if ( + !has(opts, "logOutputChannel") && + !has(opts, "logPath") && + !has(opts, "logConsole") + ) { + throw Error( + "Logger must have at least one logging target defined, it should includes one (or more) of these options: logOutputChannel, logPath and logConsole" + ); + } + + /** + * @type {any[]} + */ + const transports = []; + + if (opts.logConsole) { + transports.push(new ConsoleTransport()); + } + + if (opts.logOutputChannel) { + transports.push( + new VscodeOutChannelTransport({ + outChannel: opts.logOutputChannel, + }) + ); + } + + if (opts.logPath) { + transports.push( + new RollingFileTransport({ + logPath: opts.logPath, + extName: opts.extName, + RollingFileStream: RollingFileStream, + }) + ); + } + + const format = buildLoggerFormat(opts.extName); + const rootWinstonLogger = createLogger({ + levels: levelsConfig, + // We are handling the log levels ourselves, to avoid calling winston's re-configure method + // as it would require us to re-create the winston transports and may create race conditions + // - Winston does not have a minimal API to change **only** the level... + level: levels.trace, + format: format, + transports: transports, + }); + + return new VSCodeExtLogger({ + label: opts.extName, + level: opts.level, + loggerImpel: rootWinstonLogger, + outChannel: opts.logOutputChannel, + sourceLocationTracking: opts.sourceLocationTracking, + consoleOutput: opts.logConsole, + }); +} + +/** + * Ensure actual runtime API matches the declared public API + * @type {typeof import("../api")} + */ +const publicApi = { + getExtensionLogger: getExtensionLogger, +}; + +module.exports = publicApi; diff --git a/projects/vscode-logging/packages/logger/lib/format.js b/projects/vscode-logging/packages/logger/lib/format.js new file mode 100644 index 000000000..364df03c8 --- /dev/null +++ b/projects/vscode-logging/packages/logger/lib/format.js @@ -0,0 +1,30 @@ +const { format } = require("winston"); +const { MESSAGE } = require("triple-beam"); +// @ts-ignore +const { stableStringify } = require("fast-safe-stringify"); + +const utcTimestampProp = format((info) => { + // The timestamp format is according to ISO-8601 and the format is: YYYY-MM-DDTHH:mm:ss.sssZ + info.time = new Date().toISOString(); + + return info; +}); + +// TODO: stable json should be provided directly by winston / logForm (future versions). +const JSONStable = format((info) => { + info[MESSAGE] = stableStringify(info, null, 2); + return info; +}); + +function buildLoggerFormat(label) { + const resultFormat = format.combine( + format.splat(), + utcTimestampProp(), + JSONStable() + ); + return resultFormat; +} + +module.exports = { + buildLoggerFormat: buildLoggerFormat, +}; diff --git a/projects/vscode-logging/packages/logger/lib/levels.js b/projects/vscode-logging/packages/logger/lib/levels.js new file mode 100644 index 000000000..2ce055b97 --- /dev/null +++ b/projects/vscode-logging/packages/logger/lib/levels.js @@ -0,0 +1,21 @@ +const { partial, mapValues, includes, keys } = require("lodash"); + +const levelsConfig = { + off: -1, + fatal: 0, + error: 1, + warn: 2, + info: 3, + debug: 4, + trace: 5, +}; + +const logLevelsKeys = mapValues(levelsConfig, (val, key) => key); + +const isValidLogLevel = partial(includes, keys(levelsConfig)); + +module.exports = { + levelsConfig: levelsConfig, + levels: logLevelsKeys, + isValidLogLevel: isValidLogLevel, +}; diff --git a/projects/vscode-logging/packages/logger/lib/logger.js b/projects/vscode-logging/packages/logger/lib/logger.js new file mode 100644 index 000000000..f37381443 --- /dev/null +++ b/projects/vscode-logging/packages/logger/lib/logger.js @@ -0,0 +1,201 @@ +const { forEach, findKey } = require("lodash"); +const stacktrace = require("stacktrace-js"); +const { levelsConfig, isValidLogLevel } = require("./levels"); +const ADD_SOURCE_LOCATION_INFO = Symbol("addSourceLocationInfo"); +const CHANGE_LEVEL = Symbol("changeLevel"); +const CHANGE_SOURCE_LOCATION_TRACKING = Symbol("changeSourceLocationTracking"); + +const LABEL = Symbol("label"); +const LOGGER_IMPEL = Symbol("loggerImpel"); +const LEVEL_INT = Symbol("levelInt"); +const SOURCE_LOCATION_TRACKING = Symbol("sourceLocationTracking"); +const CONSOLE_OUTPUT = Symbol("consoleOutput"); +const CHILD_LOGGERS = Symbol("childLoggers"); +const OUT_CHANNEL = Symbol("outChannel"); +const WARN_IF_LOCATION_TRACKING_IS_ENABLED = Symbol( + "warnIfLocationTrackingIsEnabled" +); + +class BaseLogger { + /** + * @param {object} opts + * @param {string} opts.label + * @param {string} opts.level + * @param {boolean} [opts.sourceLocationTracking] + * @param {boolean} [opts.consoleOutput] + * @param {import("winston").Logger} opts.loggerImpel + */ + constructor(opts) { + this[LABEL] = opts.label; + this[LOGGER_IMPEL] = opts.loggerImpel; + this[LEVEL_INT] = levelsConfig[opts.level]; + // disabled by default as this may cause performance regressions + this[SOURCE_LOCATION_TRACKING] = opts.sourceLocationTracking || false; + this[CONSOLE_OUTPUT] = opts.consoleOutput || false; + // Possible memory leak if users forget to release their childLogger Resources + // Could have been resolved using WeakMap, however Weak Collections in JavaScript are not iterable (yet) + // So they are insufficient for our needs (see `getChildLogger` method). + // - https://github.com/tc39/proposal-weakrefs#iterable-weakmaps + this[CHILD_LOGGERS] = new Map([]); + } + + getChildLogger(opts) { + const newLabel = this[LABEL] + "." + opts.label; + + if (this[CHILD_LOGGERS].has(newLabel)) { + return this[CHILD_LOGGERS].get(newLabel); + } + + const newChildLoggerImpel = new BaseLogger({ + label: newLabel, + sourceLocationTracking: this[SOURCE_LOCATION_TRACKING], + consoleOutput: this[CONSOLE_OUTPUT], + level: findKey(levelsConfig, (val) => val === this[LEVEL_INT]), + loggerImpel: this[LOGGER_IMPEL], + }); + + this[CHILD_LOGGERS].set(newLabel, newChildLoggerImpel); + + return newChildLoggerImpel; + } + + fatal(msg, ...args) { + if (this[LEVEL_INT] >= levelsConfig.fatal) { + this[ADD_SOURCE_LOCATION_INFO](args); + args.push({ label: this[LABEL] }); + // @ts-ignore + this[LOGGER_IMPEL].fatal(msg, ...args); + } + } + + error(msg, ...args) { + if (this[LEVEL_INT] >= levelsConfig.error) { + this[ADD_SOURCE_LOCATION_INFO](args); + args.push({ label: this[LABEL] }); + this[LOGGER_IMPEL].error(msg, ...args); + } + } + + warn(msg, ...args) { + if (this[LEVEL_INT] >= levelsConfig.warn) { + this[ADD_SOURCE_LOCATION_INFO](args); + args.push({ label: this[LABEL] }); + this[LOGGER_IMPEL].warn(msg, ...args); + } + } + + info(msg, ...args) { + if (this[LEVEL_INT] >= levelsConfig.info) { + this[ADD_SOURCE_LOCATION_INFO](args); + args.push({ label: this[LABEL] }); + this[LOGGER_IMPEL].info(msg, ...args); + } + } + + debug(msg, ...args) { + if (this[LEVEL_INT] >= levelsConfig.debug) { + this[ADD_SOURCE_LOCATION_INFO](args); + args.push({ label: this[LABEL] }); + this[LOGGER_IMPEL].debug(msg, ...args); + } + } + + trace(msg, ...args) { + if (this[LEVEL_INT] >= levelsConfig.trace) { + this[ADD_SOURCE_LOCATION_INFO](args); + args.push({ label: this[LABEL] }); + // @ts-ignore + this[LOGGER_IMPEL].trace(msg, ...args); + } + } +} + +// private methods using Symbols to hide it, need to be added directly on the prototype +BaseLogger.prototype[CHANGE_LEVEL] = function (newLevel) { + this[LEVEL_INT] = levelsConfig[newLevel]; + // @ts-ignore + forEach([...this[CHILD_LOGGERS].values()], (childLogger) => { + // Recursive Call + childLogger[CHANGE_LEVEL](newLevel); + }); +}; + +// private methods using Symbols to hide it, need to be added directly on the prototype +BaseLogger.prototype[CHANGE_SOURCE_LOCATION_TRACKING] = function ( + isSourceLocTrack +) { + this[SOURCE_LOCATION_TRACKING] = isSourceLocTrack; + // @ts-ignore + forEach([...this[CHILD_LOGGERS].values()], (childLogger) => { + // Recursive Call + childLogger[CHANGE_SOURCE_LOCATION_TRACKING](isSourceLocTrack); + }); +}; + +BaseLogger.prototype[ADD_SOURCE_LOCATION_INFO] = function (args) { + if (this[SOURCE_LOCATION_TRACKING] === true) { + const stack = stacktrace.getSync(); + // we need to go 2 levels up the stack to get to the end user's code + const userFrame = stack[2]; + const sourceLocMsg = `${userFrame.fileName}:${userFrame.lineNumber}:${userFrame.columnNumber}`; + // Balance between reducing number of properties and readability + args.push({ + source: { + function: userFrame.functionName, + location: sourceLocMsg, + }, + }); + } +}; + +class VSCodeExtLogger extends BaseLogger { + /** + * @param {object} opts + * @param {string} opts.label + * @param {string} opts.level + * @param {import("winston").Logger} opts.loggerImpel + * @param {import("../api").BasicOutputChannel} [opts.outChannel] + * @param {boolean} [opts.sourceLocationTracking] + * @param {boolean} [opts.consoleOutput] + */ + constructor(opts) { + super(opts); + + this[OUT_CHANNEL] = opts.outChannel || undefined; + this.changeLevel(opts.level); + this[WARN_IF_LOCATION_TRACKING_IS_ENABLED](); + } + + changeLevel(newLevel) { + if (isValidLogLevel(newLevel) === false) { + this.fatal( + `Attempt to use unknown logging level: <${newLevel}> has been ignored.` + ); + } else { + // A strange kind of `super.changeLevel` call + this[CHANGE_LEVEL](newLevel); + } + } + + changeSourceLocationTracking(newSourceLocation) { + this[CHANGE_SOURCE_LOCATION_TRACKING](newSourceLocation); + this[WARN_IF_LOCATION_TRACKING_IS_ENABLED](); + } +} + +// private method using Symbols to hide it, need to be added directly on the prototype +VSCodeExtLogger.prototype[WARN_IF_LOCATION_TRACKING_IS_ENABLED] = function () { + if (this[SOURCE_LOCATION_TRACKING] === true) { + this.fatal( + "SourceLocationTracking is Enabled, This must only be used during debugging flows as it causes performance regressions" + ); + + if (this[OUT_CHANNEL]) { + this[OUT_CHANNEL].show(); + } + } +}; + +module.exports = { + VSCodeExtLogger: VSCodeExtLogger, +}; diff --git a/projects/vscode-logging/packages/logger/lib/transports/console-output.js b/projects/vscode-logging/packages/logger/lib/transports/console-output.js new file mode 100644 index 000000000..b6230d3c8 --- /dev/null +++ b/projects/vscode-logging/packages/logger/lib/transports/console-output.js @@ -0,0 +1,39 @@ +const Transport = require("winston-transport"); +const { LEVEL, MESSAGE } = require("triple-beam"); + +class ConsoleTransport extends Transport { + constructor() { + super(); + } + + /** + * Core logging method exposed to Winston. + * @param {Object} info + * @param {Function} callback + */ + log(info, callback) { + setImmediate(() => this.emit("logged", info)); + + switch (info[LEVEL]) { + case "error": + case "fatal": + console.error(info[MESSAGE]); + break; + case "warn": + console.warn(info[MESSAGE]); + break; + default: + console.log(info[MESSAGE]); + } + /* istanbul ignore else - winston transports use guard conditions around the callbacks + * however I have no idea how to simulate the callback not existing... (winston internals...) + **/ + if (callback) { + callback(); // eslint-disable-line callback-return + } + } +} + +module.exports = { + ConsoleTransport: ConsoleTransport, +}; diff --git a/projects/vscode-logging/packages/logger/lib/transports/rolling-file.js b/projects/vscode-logging/packages/logger/lib/transports/rolling-file.js new file mode 100644 index 000000000..596f07904 --- /dev/null +++ b/projects/vscode-logging/packages/logger/lib/transports/rolling-file.js @@ -0,0 +1,75 @@ +const { EOL } = require("os"); +const Transport = require("winston-transport"); +const { MESSAGE } = require("triple-beam"); +const { ensureDirSync } = require("fs-extra"); +const { resolve } = require("path"); +const { defaults } = require("lodash"); + +class RollingFileTransport extends Transport { + /** + * @param {object} opts + * @param {*} opts.RollingFileStream + * @param {string} opts.logPath + * @param {string} opts.extName + * @param {string} [opts.maxFiles] - Maximum number of files to roll before overwriting previous logs. + * @param {string} [opts.maxFileSize] - In MegaByte, total max log size would be in Bytes. + */ + constructor(opts) { + super(); + + const actualOpts = defaults(opts, { + maxFiles: 10, + maxFileSize: 1024 * 1024, + }); + + // better safe then sorry + ensureDirSync(actualOpts.logPath); + + const logFilePath = resolve( + actualOpts.logPath, + `${actualOpts.extName}.log` + ); + + this.rollingFileStream = new opts.RollingFileStream( + logFilePath, + actualOpts.maxFileSize, + actualOpts.maxFiles + ); + } + + log(info, cb) { + setImmediate(() => { + this.emit("logged", info); + }); + + this.rollingFileStream.write(info[MESSAGE] + EOL); + + /* istanbul ignore else - winston transports use guard conditions around the callbacks + * however I have no idea how to simulate the callback not existing... (winston internals...) + **/ + if (cb) { + cb(); + } + } + + /* istanbul ignore next - Our flows never close a transport, this only happens on exist of VSCode + * Probably not worth the effort of mocking this or creating a proper integration test. + **/ + close(cb) { + if (this.rollingFileStream === undefined) { + return; + } + + this.rollingFileStream.end(() => { + if (cb) { + cb(); + } + this.emit("flush"); + this.emit("closed"); + }); + } +} + +module.exports = { + RollingFileTransport: RollingFileTransport, +}; diff --git a/projects/vscode-logging/packages/logger/lib/transports/vscode-out-channel.js b/projects/vscode-logging/packages/logger/lib/transports/vscode-out-channel.js new file mode 100644 index 000000000..71bfbcd10 --- /dev/null +++ b/projects/vscode-logging/packages/logger/lib/transports/vscode-out-channel.js @@ -0,0 +1,48 @@ +const Transport = require("winston-transport"); +const { MESSAGE } = require("triple-beam"); + +class VscodeOutChannelTransport extends Transport { + /** + * @param {object} opts + * @param {import("../../api").BasicOutputChannel} opts.outChannel + */ + constructor(opts) { + super(); + + this.outChannel = opts.outChannel; + } + + log(info, cb) { + setImmediate(() => { + this.emit("logged", info); + }); + + this.outChannel.appendLine(info[MESSAGE]); + + /* istanbul ignore else - winston transports use guard conditions around the callbacks + * however I have no idea how to simulate the callback not existing... (winston internals...) + **/ + if (cb) { + cb(); + } + } + + /* istanbul ignore next - Our flows never close a transport, this only happens on exist of VSCode + * Probably not worth the effort of mocking this or creating a proper integration test. + **/ + close(cb) { + if (this.outChannel === undefined) { + return; + } + + this.outChannel.dispose(); + + if (cb) { + cb(); + } + } +} + +module.exports = { + VscodeOutChannelTransport: VscodeOutChannelTransport, +}; diff --git a/projects/vscode-logging/packages/logger/nyc.config.js b/projects/vscode-logging/packages/logger/nyc.config.js new file mode 100644 index 000000000..46814df87 --- /dev/null +++ b/projects/vscode-logging/packages/logger/nyc.config.js @@ -0,0 +1,8 @@ +module.exports = { + include: ["lib/**/*.js"], + reporter: ["text", "lcov"], + // logger's tests use proxyquire; without this nyc drops the remapped + // lib/ files after remap and reports 0% (a false-green against the 100% + // gate). Mirrors the wrapper package's nyc.config.js. + excludeAfterRemap: false, +}; diff --git a/projects/vscode-logging/packages/logger/package.json b/projects/vscode-logging/packages/logger/package.json new file mode 100644 index 000000000..8e15663df --- /dev/null +++ b/projects/vscode-logging/packages/logger/package.json @@ -0,0 +1,58 @@ +{ + "name": "@vscode-logging/logger", + "version": "2.0.9", + "description": "Logger Library for VSCode Extensions", + "keywords": [ + "vscode", + "logger" + ], + "files": [ + ".reuse", + "LICENSES", + "lib", + "api.d.ts", + "CHANGELOG.md", + "CHANGELOG.old.md" + ], + "main": "lib/api.js", + "repository": { + "type": "git", + "url": "https://github.com/SAP/app-studio-toolkit.git", + "directory": "projects/vscode-logging/packages/logger" + }, + "bugs": { + "url": "https://github.com/SAP/app-studio-toolkit/issues" + }, + "license": "Apache-2.0", + "typings": "./api.d.ts", + "dependencies": { + "@vscode-logging/types": "workspace:*", + "fast-safe-stringify": "2.1.1", + "fs-extra": "11.2.0", + "lodash": "4.18.1", + "stacktrace-js": "2.0.2", + "streamroller": "3.1.5", + "triple-beam": "1.4.1", + "winston": "3.11.0", + "winston-transport": "4.6.0" + }, + "//@types/vscode": "Pinned to the minimum VS Code API version supported here (kept from the standalone vscode-logging repo). Intentionally not raised to the monorepo root version, which would allow newer APIs and break support for older VS Code.", + "devDependencies": { + "@types/fs-extra": "11.0.4", + "@types/vscode": "1.51.0", + "chai-exclude": "2.1.0", + "proxyquire": "2.1.3", + "sinon": "17.0.1", + "sinon-chai": "3.7.0" + }, + "scripts": { + "ci": "npm-run-all type-check coverage:*", + "test": "mocha \"./test/**/*spec.js\"", + "coverage:run": "nyc mocha \"./test/**/*spec.js\"", + "coverage:check": "nyc check-coverage --lines 100 --branches 100 --statements 100 --functions 100", + "type-check": "tsc" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/projects/vscode-logging/packages/logger/test/api-spec.js b/projects/vscode-logging/packages/logger/test/api-spec.js new file mode 100644 index 000000000..cc35290c8 --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/api-spec.js @@ -0,0 +1,19 @@ +const { expect } = require("chai"); +const { getExtensionLogger } = require("../lib/api.js"); + +describe("VSCode Extension Logger", () => { + /** + * @type {typeof import("../api").getExtensionLogger} + */ + + context("when receiving no OutputChannel and no logPath", () => { + it("will throw on invalid/missing parameters", () => { + expect(() => { + getExtensionLogger({ + extName: "MyExtName", + level: "error", + }); + }).to.throw("logOutputChannel, logPath and logConsole"); + }); + }); +}); diff --git a/projects/vscode-logging/packages/logger/test/child-logger-spec.js b/projects/vscode-logging/packages/logger/test/child-logger-spec.js new file mode 100644 index 000000000..a6fcd79e8 --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/child-logger-spec.js @@ -0,0 +1,345 @@ +const { map } = require("lodash"); +const { expect } = require("chai"); +const proxyquire = require("proxyquire").noCallThru(); + +const { VSCodeStub } = require("./stubs/vscode-stub"); +const { resolve } = require("path"); +const TESTS_LOG_PATH = resolve(__dirname, ".log-out"); + +describe("VSCode Extension Logger", () => { + context("childLogger capabilities", () => { + /** + * @type {typeof import("../api").getExtensionLogger} + */ + let getExtensionLogger; + let vsCodeStub; + beforeEach(() => { + // VSCode outChannel is optional but we still need a stub for it + // in order to test its functionality + vsCodeStub = new VSCodeStub(); + const mainModuleStubbed = proxyquire("../lib/api.js", { + vscode: vsCodeStub, + }); + getExtensionLogger = mainModuleStubbed.getExtensionLogger; + }); + + it("will log to childLogger", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + + const childLogger = extLogger.getChildLogger({ label: "myLibName" }); + childLogger.fatal("Oops I did it again!"); + + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: "MyExtName.myLibName", + level: "fatal", + message: "Oops I did it again!", + }, + ]); + }); + + it("will handle logging level at the root Logger of all childLoggers", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + + const childLogger = extLogger.getChildLogger({ label: "myLibName" }); + childLogger.warn("Oops I did it again!"); + // nothing logged, warn < 'error' + expect(vsCodeStub.lines).to.be.empty; + + extLogger.changeLevel("info"); + + // Changes on the root affected the level of the child... + childLogger.warn("Oops I did it again!"); + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: "MyExtName.myLibName", + level: "warn", + message: "Oops I did it again!", + }, + ]); + }); + + it("will handle sourceLocationTracking option at the root Logger of all childLoggers", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "info", + }); + + const childLogger = extLogger.getChildLogger({ label: "myLibName" }); + const grandChildLogger = childLogger.getChildLogger({ + label: "myClassName", + }); + + childLogger.warn("Oops I did it again!"); + grandChildLogger.warn("Oops I did it again!"); + const logEntriesBefore = map(vsCodeStub.lines, JSON.parse); + expect(logEntriesBefore[0]).to.not.have.property("source"); + expect(logEntriesBefore[1]).to.not.have.property("source"); + + // Change at the root Logger Source Location + extLogger.changeSourceLocationTracking(true); + vsCodeStub.lines = []; + childLogger.warn("Oops I did it again!"); + grandChildLogger.warn("Oops I did it again!"); + + const logEntriesAfter = map(vsCodeStub.lines, JSON.parse); + expect(logEntriesAfter[0]).to.have.property("source"); + expect(logEntriesAfter[1]).to.have.property("source"); + }); + + it("will cache and re-use the same childLogger for the same label", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + + const childLogger = extLogger.getChildLogger({ label: "myLibName" }); + const childLoggerSameLabel = extLogger.getChildLogger({ + label: "myLibName", + }); + expect(childLogger).to.equal(childLoggerSameLabel); + }); + }); + + context("childLogger load", () => { + /** + * @type {typeof import("../api").getExtensionLogger} + */ + let getExtensionLogger; + let vsCodeStub; + beforeEach(() => { + const mainModuleStubbed = proxyquire("../lib/api.js", {}); + getExtensionLogger = mainModuleStubbed.getExtensionLogger; + }); + + function createExtLoggerWithNumberOfChildLoggers(childLoggersNumbers) { + const extLogger = getExtensionLogger({ + extName: "MainLoggerExtension", + logPath: TESTS_LOG_PATH, + level: "error", + }); + + for ( + let childLoggerNumber = 1; + childLoggerNumber <= childLoggersNumbers; + childLoggerNumber++ + ) { + extLogger.getChildLogger({ + label: "ChildLoggerClass" + childLoggerNumber, + }); + } + } + + /** + * Issue #76 reproduction. + * + * The issue is reproducible + * when running "yarn ci" in the home directory vscode-logging + * with only on this it, context or describe; + * or when running "yarn test" in vscode-logging/packages/logger folder. + * + * The following errors appeared before fixing Issue #76 after the successful test result: + childLogger load + √ will create 9 childLoggers and write error message to stderr + (node:2280) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 unpipe listeners added to [RollingFileTransport]. Use emitter.setMaxListeners() to increase limit + (node:2280) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [RollingFileTransport]. Use emitter.setMaxListeners() to increase limit + */ + it("will create 9 childLoggers and write error message to stderr (before fixing Issue #76)", () => { + return createExtLoggerWithNumberOfChildLoggers(9); + }); + + /** + * Issue #76 doesn't happen when the number of ChildLogger's is less than 9. + * + * No errors appear after the successful test result: + childLogger load + √ will create 8 childLoggers and no error message to stderr + */ + it("will create 8 childLoggers and no error message to stderr (before fixing Issue #76)", () => { + return createExtLoggerWithNumberOfChildLoggers(8); + }); + }); + context( + "childLogger will log to childLogger log entry with correct label", + () => { + /** + * @type {typeof import("../api").getExtensionLogger} + */ + let getExtensionLogger; + let vsCodeStub; + beforeEach(() => { + // VSCode outChannel is optional but we still need a stub for it + // in order to test its functionality + vsCodeStub = new VSCodeStub(); + const mainModuleStubbed = proxyquire("../lib/api.js", { + vscode: vsCodeStub, + }); + getExtensionLogger = mainModuleStubbed.getExtensionLogger; + }); + + it("in fatal log message for extension class", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "trace", + }); + + const classLogger = extLogger.getChildLogger({ label: "myClassName" }); + classLogger.fatal("Oops I did it again!"); + + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: "MyExtName.myClassName", + level: "fatal", + message: "Oops I did it again!", + }, + ]); + }); + + it("in error log message for extension library class", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "trace", + }); + + const libLogger = extLogger.getChildLogger({ label: "myLibName" }); + const classLogger = libLogger.getChildLogger({ label: "myClassName" }); + + classLogger.error("Oops I did it again!"); + + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: "MyExtName.myLibName.myClassName", + level: "error", + message: "Oops I did it again!", + }, + ]); + }); + + it("in warn log message for extension internal library class", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "trace", + }); + + const lib1Logger = extLogger.getChildLogger({ label: "myLib1Name" }); + const lib2Logger = lib1Logger.getChildLogger({ label: "myLib2Name" }); + const classLogger = lib2Logger.getChildLogger({ label: "myClassName" }); + classLogger.warn("Oops I did it again!"); + + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: "MyExtName.myLib1Name.myLib2Name.myClassName", + level: "warn", + message: "Oops I did it again!", + }, + ]); + }); + + it("in info log message for extension internal library class", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "trace", + }); + + const lib1Logger = extLogger.getChildLogger({ label: "myLib1Name" }); + const lib2Logger = lib1Logger.getChildLogger({ label: "myLib2Name" }); + const lib3Logger = lib2Logger.getChildLogger({ label: "myLib3Name" }); + const classLogger = lib3Logger.getChildLogger({ label: "myClassName" }); + classLogger.info("Oops I did it again!"); + + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: "MyExtName.myLib1Name.myLib2Name.myLib3Name.myClassName", + level: "info", + message: "Oops I did it again!", + }, + ]); + }); + it("in debug log message for extension internal library class", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "trace", + }); + + const lib1Logger = extLogger.getChildLogger({ label: "myLib1Name" }); + const lib2Logger = lib1Logger.getChildLogger({ label: "myLib2Name" }); + const lib3Logger = lib2Logger.getChildLogger({ label: "myLib3Name" }); + const lib4Logger = lib3Logger.getChildLogger({ label: "myLib4Name" }); + const classLogger = lib4Logger.getChildLogger({ label: "myClassName" }); + classLogger.debug("Oops I did it again!"); + + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: + "MyExtName.myLib1Name.myLib2Name.myLib3Name.myLib4Name.myClassName", + level: "debug", + message: "Oops I did it again!", + }, + ]); + }); + it("in trace log message for extension internal library class", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "trace", + }); + + const lib1Logger = extLogger.getChildLogger({ label: "myLib1Name" }); + const lib2Logger = lib1Logger.getChildLogger({ label: "myLib2Name" }); + const lib3Logger = lib2Logger.getChildLogger({ label: "myLib3Name" }); + const lib4Logger = lib3Logger.getChildLogger({ label: "myLib4Name" }); + const lib5Logger = lib4Logger.getChildLogger({ label: "myLib5Name" }); + const classLogger = lib5Logger.getChildLogger({ label: "myClassName" }); + classLogger.trace("Oops I did it again!"); + + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: + "MyExtName.myLib1Name.myLib2Name.myLib3Name.myLib4Name.myLib5Name.myClassName", + level: "trace", + message: "Oops I did it again!", + }, + ]); + }); + } + ); +}); diff --git a/projects/vscode-logging/packages/logger/test/console-output-spec.js b/projects/vscode-logging/packages/logger/test/console-output-spec.js new file mode 100644 index 000000000..3eeec18fc --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/console-output-spec.js @@ -0,0 +1,105 @@ +const proxyquire = require("proxyquire").noCallThru(); +const chai = require("chai"); +const sinonChai = require("sinon-chai"); +const sinon = require("sinon"); +const { StreamRollerStub } = require("./stubs/stream-roller-stub"); +const { resolve } = require("path"); + +chai.use(sinonChai); + +const expect = chai.expect; + +const TESTS_LOG_PATH = resolve(__dirname, ".log-out"); + +describe("VSCode Extension Logger", function () { + let vsCodeStub; + /** + * @type {typeof import("../api").getExtensionLogger} + */ + let getExtensionLogger; + let errSpy, warnSpy, logSpy; + let streamRollerStub; + beforeEach(() => { + streamRollerStub = new StreamRollerStub(); + const mainModuleStubbed = proxyquire("../lib/api.js", { + streamroller: streamRollerStub, + }); + getExtensionLogger = mainModuleStubbed.getExtensionLogger; + errSpy = sinon.spy(console, "error"); + warnSpy = sinon.spy(console, "warn"); + logSpy = sinon.spy(console, "log"); + }); + + afterEach(function () { + console.error.restore(); + console.warn.restore(); + console.log.restore(); + }); + + describe("Console Logging - check that the output stream match the message type and log level - error/warn/info and stdout/stderr", function () { + it("should log to console in info level", function () { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + level: "info", + logConsole: true, + }); + extLogger.fatal("Oy Vey!"); + extLogger.error("Oh Dear..."); + extLogger.info("Oh Wow..."); + + sinon.assert.calledTwice(console.error); + sinon.assert.called(console.log); + sinon.assert.notCalled(console.warn); + expect(errSpy.args[0][0]).to.include("Oy Vey!"); + expect(errSpy.args[1][0]).to.include("Oh Dear..."); + expect(logSpy.args[0][0]).to.include("Oh Wow..."); + }); + + it("should log to console in error level", function () { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + level: "error", + logConsole: true, + }); + extLogger.fatal("Oy Vey!"); + extLogger.error("Oh Dear..."); + extLogger.info("Oh Wow..."); + + sinon.assert.calledTwice(console.error); + sinon.assert.notCalled(console.log); + sinon.assert.notCalled(console.warn); + expect(errSpy.args[0][0]).to.include("Oy Vey!"); + expect(errSpy.args[1][0]).to.include("Oh Dear..."); + }); + + it("should log to console in warn level", function () { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + level: "warn", + logConsole: true, + }); + extLogger.warn("Oy Might..."); + extLogger.error("Oh Dear..."); + extLogger.info("Oh Wow..."); + + sinon.assert.called(console.error); + sinon.assert.called(console.warn); + sinon.assert.notCalled(console.log); + expect(errSpy.args[0][0]).to.include("Oh Dear..."); + expect(warnSpy.args[0][0]).to.include("Oy Might..."); + }); + + it("should not log to console when consoleOutput is false", function () { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logPath: TESTS_LOG_PATH, + level: "info", + }); + extLogger.fatal("Oy Vey!"); + extLogger.info("Oy Wow!"); + sinon.assert.notCalled(console.error); + sinon.assert.notCalled(console.warn); + sinon.assert.notCalled(console.log); + }); + }); +}); diff --git a/projects/vscode-logging/packages/logger/test/levels-spec.js b/projects/vscode-logging/packages/logger/test/levels-spec.js new file mode 100644 index 000000000..2e0651403 --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/levels-spec.js @@ -0,0 +1,116 @@ +const { map, forEach, pickBy, take } = require("lodash"); +const { expect } = require("chai"); +const proxyquire = require("proxyquire").noCallThru(); + +const { levels, levelsConfig } = require("../lib/levels"); +const { VSCodeStub } = require("./stubs/vscode-stub"); + +describe("VSCode Extension Logger", () => { + /** + * @type {typeof import("../api").getExtensionLogger} + */ + let getExtensionLogger; + let vsCodeStub; + beforeEach(() => { + // VSCode outChannel is optional but we still need a stub for it + // in order to test its functionality + vsCodeStub = new VSCodeStub(); + const mainModuleStubbed = proxyquire("../lib/api.js", { + vscode: vsCodeStub, + }); + getExtensionLogger = mainModuleStubbed.getExtensionLogger; + }); + + context("when using an invalid level", () => { + it(`will throw on initial invalid level`, () => { + expect(() => { + getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "Emergency", // This is a sysLog severity + }); + }).to.throw("Attempt to use unknown logging level: !"); + }); + + it(`will warn and ignore on subsequent invalid level`, () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "fatal", + }); + + extLogger.changeLevel("Emergency"); + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries[0].message).to.eql( + "Attempt to use unknown logging level: has been ignored." + ); + + expect(vsCodeStub.lines).to.have.lengthOf(1); + extLogger.error( + "should not be logger as the logger should still be on fatal mode" + ); + expect(vsCodeStub.lines).to.have.lengthOf(1); + extLogger.fatal("should be logged"); + expect(vsCodeStub.lines).to.have.lengthOf(2); + }); + }); + + context("Specific logging Levels Support", () => { + const allLevelsExceptOff = pickBy(levels, (levelKey) => levelKey !== "off"); + + const fullExpectedLogEntries = [ + { + level: "fatal", + message: "fatal", + }, + { + level: "error", + message: "error", + }, + { + level: "warn", + message: "warn", + }, + { + level: "info", + message: "info", + }, + { + level: "debug", + message: "debug", + }, + { + level: "trace", + message: "trace", + }, + ]; + + forEach(levels, (currLevel) => { + it(`will only log correct levels in '${currLevel}'`, () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: currLevel, + }); + + // try logging using all the possible levels + forEach(allLevelsExceptOff, (level) => { + extLogger[level](level); + }); + + const logEntries = map(vsCodeStub.lines, JSON.parse); + + // The higher the log level the more entries are expected to be logged. + const expectedLogEntries = take( + fullExpectedLogEntries, + // 0 based index necessitates '+1" + levelsConfig[currLevel] + 1 + ); + + expect(logEntries) + .excluding(["time", "label"]) + .to.deep.eql(expectedLogEntries); + }); + }); + }); +}); diff --git a/projects/vscode-logging/packages/logger/test/log-methods-apis-spec.js b/projects/vscode-logging/packages/logger/test/log-methods-apis-spec.js new file mode 100644 index 000000000..c9493430a --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/log-methods-apis-spec.js @@ -0,0 +1,90 @@ +const { map } = require("lodash"); +const { expect } = require("chai"); +const proxyquire = require("proxyquire").noCallThru(); + +const { VSCodeStub } = require("./stubs/vscode-stub"); + +describe("VSCode Extension extLogger", () => { + context("Log Methods APIs and Argument Types", () => { + /** + * @type {typeof import("../api").getExtensionLogger}output + */ + let getExtensionLogger; + let vsCodeStub; + beforeEach(() => { + // VSCode outChannel is optional but we still need a stub for it + // in order to test its functionality + vsCodeStub = new VSCodeStub(); + const mainModuleStubbed = proxyquire("../lib/api.js", { + vscode: vsCodeStub, + }); + getExtensionLogger = mainModuleStubbed.getExtensionLogger; + }); + + it("supports splat arguments", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + + extLogger.fatal("hello %s and 1 + %i equals 2", "world", 1); + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries[0].message).to.equal("hello world and 1 + 1 equals 2"); + }); + + it("supports metadata object arguments", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + + extLogger.fatal("hello world", { a: 666, b: "oops" }, { c: 333 }); + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries[0].a).to.equal(666); + expect(logEntries[0].b).to.equal("oops"); + expect(logEntries[0].c).to.equal(333); + }); + + it("supports combining splat and object arguments", () => { + const extLogger = getExtensionLogger({ + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + + extLogger.fatal("hello %s", "world", { a: 666, b: "oops" }, { c: 333 }); + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries[0].message).to.equal("hello world"); + expect(logEntries[0].a).to.equal(666); + expect(logEntries[0].b).to.equal("oops"); + expect(logEntries[0].c).to.equal(333); + }); + + it("overwrites label field", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + + extLogger.fatal("hello world", { label: "kuku" }); + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries[0].label).to.equal("MyExtName"); + }); + + it("overwrites label field for ChildLogger", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + const libLogger = extLogger.getChildLogger({ label: "MyLibName" }); + const classLogger = libLogger.getChildLogger({ label: "MyClassName" }); + + classLogger.fatal("hello world", { label: "kuku" }); + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries[0].label).to.equal("MyExtName.MyLibName.MyClassName"); + }); + }); +}); diff --git a/projects/vscode-logging/packages/logger/test/out-channel-spec.js b/projects/vscode-logging/packages/logger/test/out-channel-spec.js new file mode 100644 index 000000000..6b42548f9 --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/out-channel-spec.js @@ -0,0 +1,97 @@ +const { VSCodeStub } = require("./stubs/vscode-stub"); +const { map } = require("lodash"); +const { basename } = require("path"); + +const { expect } = require("chai"); +const proxyquire = require("proxyquire").noCallThru(); + +describe("VSCode Extension Logger", () => { + context("OutChannel Logging", () => { + let vsCodeStub; + /** + * @type {typeof import("../api").getExtensionLogger} + */ + let getExtensionLogger; + + beforeEach(() => { + vsCodeStub = new VSCodeStub(); + const mainModuleStubbed = proxyquire("../lib/api.js", { + vscode: vsCodeStub, + }); + getExtensionLogger = mainModuleStubbed.getExtensionLogger; + }); + + it("will Log in JSON Format", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + extLogger.fatal("Oy Vey!"); + extLogger.error("Oh Dear..."); + + const logEntries = map(vsCodeStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: "MyExtName", + level: "fatal", + message: "Oy Vey!", + }, + { + label: "MyExtName", + level: "error", + message: "Oh Dear...", + }, + ]); + }); + + it("can add sourceLocation information", function myFuncName() { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + sourceLocationTracking: true, + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + extLogger.fatal("Oh dear"); + const logEntries = map(vsCodeStub.lines, JSON.parse); + // [0] contains the warning about using `sourceLocationTracking` + const logJsonEntry = logEntries[1]; + expect(logJsonEntry.label).to.eql("MyExtName"); + expect(logJsonEntry.level).to.eql("fatal"); + expect(logJsonEntry.message).to.eql("Oh dear"); + expect(logJsonEntry.source.function).to.eql("Context.myFuncName"); + const baseFileName = basename(__filename); + const fileNameAndLocRegExp = new RegExp(`.+${baseFileName}:\\d+:\\d+`); + // The location includes our own source file name! :) + expect(logJsonEntry.source.location).to.match(fileNameAndLocRegExp); + }); + + context( + "will **show** on the outChannel and log a warning when the sourceLocationTracking is enabled", + () => { + it("on initialization", () => { + getExtensionLogger({ + extName: "MyExtName", + sourceLocationTracking: true, + logOutputChannel: vsCodeStub.OutputChannel, + level: "warn", + }); + expect(vsCodeStub.shown).to.be.true; + }); + + it("on sourceLocationChange change", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logOutputChannel: vsCodeStub.OutputChannel, + level: "error", + }); + expect(vsCodeStub.shown).to.be.false; + extLogger.changeSourceLocationTracking(true); + expect(vsCodeStub.shown).to.be.true; + }); + } + ); + }); +}); diff --git a/projects/vscode-logging/packages/logger/test/output-channel-spec.ts b/projects/vscode-logging/packages/logger/test/output-channel-spec.ts new file mode 100644 index 000000000..a279e433c --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/output-channel-spec.ts @@ -0,0 +1,7 @@ +import { window, OutputChannel } from "vscode"; +import { BasicOutputChannel } from "../api"; + +// We check that vscodeOutputChannel is assignable to BasicOutputChannel. +// Checked in `type-check` npm script +const vscodeChannel: OutputChannel = window.createOutputChannel("test"); +const basicChannel: BasicOutputChannel = vscodeChannel; diff --git a/projects/vscode-logging/packages/logger/test/rolling-file-spec.js b/projects/vscode-logging/packages/logger/test/rolling-file-spec.js new file mode 100644 index 000000000..157da1b7e --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/rolling-file-spec.js @@ -0,0 +1,76 @@ +const { map } = require("lodash"); +const { expect } = require("chai"); +const proxyquire = require("proxyquire").noCallThru(); +const { basename, resolve } = require("path"); + +const { StreamRollerStub } = require("./stubs/stream-roller-stub"); + +const TESTS_LOG_PATH = resolve(__dirname, ".log-out"); + +describe("VSCode Extension Logger", () => { + /** + * We are not testing the behavior of the StreamRoller, that is not our responsibility... + * Rather we are only inspecting what gets written to the stream. + */ + context("Rolling File Logging", () => { + /** + * @type {typeof import("../api").getExtensionLogger} + */ + let getExtensionLogger; + let streamRollerStub; + beforeEach(() => { + streamRollerStub = new StreamRollerStub(); + const mainModuleStubbed = proxyquire("../lib/api.js", { + streamroller: streamRollerStub, + }); + getExtensionLogger = mainModuleStubbed.getExtensionLogger; + }); + + it("will Log in JSON Format", () => { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + logPath: TESTS_LOG_PATH, + level: "error", + }); + extLogger.fatal("Oy Vey!"); + extLogger.error("Oh Dear..."); + + const logEntries = map(streamRollerStub.lines, JSON.parse); + expect(logEntries) + .excluding("time") + .to.deep.eql([ + { + label: "MyExtName", + level: "fatal", + message: "Oy Vey!", + }, + { + label: "MyExtName", + level: "error", + message: "Oh Dear...", + }, + ]); + }); + + it("can add sourceLocation information", function myFuncName() { + const extLogger = getExtensionLogger({ + extName: "MyExtName", + sourceLocationTracking: true, + logPath: TESTS_LOG_PATH, + level: "error", + }); + extLogger.fatal("Oy Vey!"); + const logEntries = map(streamRollerStub.lines, JSON.parse); + // [0] contains the warning about using `sourceLocationTracking` + const logJsonEntry = logEntries[1]; + expect(logJsonEntry.label).to.eql("MyExtName"); + expect(logJsonEntry.level).to.eql("fatal"); + expect(logJsonEntry.message).to.eql("Oy Vey!"); + expect(logJsonEntry.source.function).to.eql("Context.myFuncName"); + const baseFileName = basename(__filename); + const fileNameAndLocRegExp = new RegExp(`.+${baseFileName}:\\d+:\\d+`); + // The location includes our own source file name! :) + expect(logJsonEntry.source.location).to.match(fileNameAndLocRegExp); + }); + }); +}); diff --git a/projects/vscode-logging/packages/logger/test/stubs/stream-roller-stub.js b/projects/vscode-logging/packages/logger/test/stubs/stream-roller-stub.js new file mode 100644 index 000000000..a0b50cfe6 --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/stubs/stream-roller-stub.js @@ -0,0 +1,24 @@ +class StreamRollerStub { + constructor() { + this.lines = []; + const that = this; + + this.RollingFileStream = class RollingFileStreamStub { + constructor() { + that.lines = []; + } + + write(txt) { + that.lines.push(txt); + } + + end() { + // NOOP + } + }; + } +} + +module.exports = { + StreamRollerStub: StreamRollerStub, +}; diff --git a/projects/vscode-logging/packages/logger/test/stubs/vscode-stub.js b/projects/vscode-logging/packages/logger/test/stubs/vscode-stub.js new file mode 100644 index 000000000..02ab1718a --- /dev/null +++ b/projects/vscode-logging/packages/logger/test/stubs/vscode-stub.js @@ -0,0 +1,21 @@ +class VSCodeStub { + constructor() { + this.lines = []; + this.shown = false; + + const that = this; + this.OutputChannel = { + dispose: () => {}, + show: () => { + that.shown = true; + }, + appendLine: (txt) => { + that.lines.push(txt); + }, + }; + } +} + +module.exports = { + VSCodeStub: VSCodeStub, +}; diff --git a/projects/vscode-logging/packages/logger/tsconfig.json b/projects/vscode-logging/packages/logger/tsconfig.json new file mode 100644 index 000000000..1c8d916dc --- /dev/null +++ b/projects/vscode-logging/packages/logger/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noEmit": true, + "checkJs": true, + "allowJs": true + }, + "include": ["./lib/**/*.js", "./api.d.ts", "./test/output-channel-spec.ts"] +} diff --git a/projects/vscode-logging/packages/types/.npmignore b/projects/vscode-logging/packages/types/.npmignore new file mode 100644 index 000000000..b5a7e13f4 --- /dev/null +++ b/projects/vscode-logging/packages/types/.npmignore @@ -0,0 +1,5 @@ +# TypeScript +tsconfig.json + +# Dev Docs +CONTRIBUTING.md \ No newline at end of file diff --git a/projects/vscode-logging/packages/types/CHANGELOG.old.md b/projects/vscode-logging/packages/types/CHANGELOG.old.md new file mode 100644 index 000000000..c0aa1b0ce --- /dev/null +++ b/projects/vscode-logging/packages/types/CHANGELOG.old.md @@ -0,0 +1,70 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [2.0.8](https://github.com/SAP/vscode-logging/compare/v2.0.7...v2.0.8) (2026-02-18) + +### Bug Fixes + +- correct repository URL case (SAP instead of sap) for npm provenance ([00a91ca](https://github.com/SAP/vscode-logging/commit/00a91ca002ba88ed27278f756b7aca8865eee1ee)) + +## [2.0.7](https://github.com/sap/vscode-logging/compare/v2.0.6...v2.0.7) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/types + +## [2.0.6](https://github.com/sap/vscode-logging/compare/v2.0.5...v2.0.6) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/types + +## [2.0.5](https://github.com/sap/vscode-logging/compare/v2.0.4...v2.0.5) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/types + +## [2.0.4](https://github.com/sap/vscode-logging/compare/v2.0.3...v2.0.4) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/types + +## [2.0.3](https://github.com/sap/vscode-logging/compare/v2.0.2...v2.0.3) (2026-02-17) + +**Note:** Version bump only for package @vscode-logging/types + +## [2.0.2](https://github.com/sap/vscode-logging/compare/v2.0.1...v2.0.2) (2026-02-17) + +**Note:** Version bump only for package @vscode-logging/types + +# 2.0.0 (2024-01-05) + +### Bug Fixes + +- add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/sap/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) + +### Features + +- aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/sap/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) +- initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) + +## [0.1.4](https://github.com/sap/vscode-logging/compare/@vscode-logging/types@0.1.3...@vscode-logging/types@0.1.4) (2021-04-13) + +**Note:** Version bump only for package @vscode-logging/types + +## [0.1.3](https://github.com/sap/vscode-logging/compare/@vscode-logging/types@0.1.2...@vscode-logging/types@0.1.3) (2020-11-12) + +**Note:** Version bump only for package @vscode-logging/types + +## [0.1.2](https://github.com/sap/vscode-logging/compare/@vscode-logging/types@0.1.1...@vscode-logging/types@0.1.2) (2020-02-19) + +### Bug Fixes + +- add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/sap/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) + +## [0.1.1](https://github.com/sap/vscode-logging/compare/@vscode-logging/types@0.1.0...@vscode-logging/types@0.1.1) (2020-01-20) + +**Note:** Version bump only for package @vscode-logging/types + +# 0.1.0 (2020-01-14) + +### Features + +- aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/sap/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) +- initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) diff --git a/projects/vscode-logging/packages/types/README.md b/projects/vscode-logging/packages/types/README.md new file mode 100644 index 000000000..a5bf09257 --- /dev/null +++ b/projects/vscode-logging/packages/types/README.md @@ -0,0 +1,44 @@ +# @vscode-logging/types + +Types used by @vscode-logging/logger extracted to a separate package to enable +in-direct dependents to type check against the correct interfaces. + +For example imagine a VSCode extension called `Foo` which uses an npm package called `Bar` +as a dependency and `Bar` APIs can accept an optional logger implementation: + +```typescript +// Code in `Bar` npm package +// ------------------------------------------------------- + +// By defining the `IChildLogger` interface` in `@vscode-logging/types` +// `Bar` only depends (dev dependency) on the interface **not** the implementation. +import { IChildLogger } from "@vscode-logging/types"; + +export function add(lhs: number, rhs: number, logger: IChildLogger): number { + logger.info("Entering function with params:", { lhs: lhs, rhs: rhs }); + return lhs + rhs; +} +``` + +## Installation + +With npm: + +- `npm install @vscode-logging/types --save-dev` + +With Yarn: + +- `yarn add @vscode-logging/types --dev` + +## Usage + +As shown above, simply import the `IChildLogger` interface and use it to define +the type of your injected logger implementation. + +## Support + +Please report issues [here](https://github.com/SAP/app-studio-toolkit/issues/new/choose) and label them with `project:vscode-logging`. + +## Contributing + +See [CONTRIBUTING.md](https://github.com/SAP/app-studio-toolkit/blob/main/CONTRIBUTING.md). diff --git a/projects/vscode-logging/packages/types/api.d.ts b/projects/vscode-logging/packages/types/api.d.ts new file mode 100644 index 000000000..923b3fa22 --- /dev/null +++ b/projects/vscode-logging/packages/types/api.d.ts @@ -0,0 +1,67 @@ +type LogLevel = "off" | "fatal" | "error" | "warn" | "info" | "debug" | "trace"; + +export interface IVSCodeExtLogger extends IChildLogger { + /** + * Modify the Logging Level. + * This will affect all child loggers as well. + * Note that the level "off" is used to 'disable' the Logger. + */ + changeLevel(newLevel: LogLevel): void; + + /** + * Modify the Log's sourceLocation tracking behavior. + */ + changeSourceLocationTracking(newSourceLocation: boolean): void; +} + +export interface IChildLogger { + /** + * @param msg - The Message to log, It may include format specifiers as defined in + * - https://nodejs.org/api/util.html#util_util_format_format_args + * @param [args] - Args can be either: + * - Optional Variable Argument List of values to "replace" the format specifiers: + * myLogger.fatal("Oy %s %d", "Vey", 123) // --> { message: "Oy Vey 123"} + * - Metadata Objects: + * myLogger.fatal("Exec Command", { p1: 666, p2: "foo" }) // --> { message: "Exec Command", p1: 666, p2: "foo"} + * Or a combination of both, however note that format specifier values should appear before + * any metadata objects. + * + */ + fatal(msg: string, ...args: any[]): void; + /** + * @see {IChildLogger.fatal} + */ + error(msg: string, ...args: any[]): void; + /** + * @see {IChildLogger.fatal} + */ + warn(msg: string, ...args: any[]): void; + /** + * @see {IChildLogger.fatal} + */ + info(msg: string, ...args: any[]): void; + /** + * @see {IChildLogger.fatal} + */ + debug(msg: string, ...args: any[]): void; + /** + * @see {IChildLogger.fatal} + */ + trace(msg: string, ...args: any[]): void; + + /** + * Will create an logger which uses the **same** log Targets (VSCode outputChannel / Rolling File(?)). + * With one difference: This "child-logger" will prefix its name to the log entries label property. + * This enables distinguishing between logging messages arriving from different sources, e.g: + * + * - Per Class Logger + * - Per Module Logger + * - Per Library Dependency Logger (via dependency injection) + * - ... + * + * Note that the childLoggers are cached using the label as the key, this means that + * One can safely request a (same label) childLogger **multiple times** and not worry about + * memory leaks due to the creation of many objects. + */ + getChildLogger(opts: { label: string }): IChildLogger; +} diff --git a/projects/vscode-logging/packages/types/package.json b/projects/vscode-logging/packages/types/package.json new file mode 100644 index 000000000..88fd536b2 --- /dev/null +++ b/projects/vscode-logging/packages/types/package.json @@ -0,0 +1,33 @@ +{ + "name": "@vscode-logging/types", + "version": "2.0.8", + "description": "Common Type Signatures for @vscode-logging/logger", + "keywords": [ + "vscode", + "logger" + ], + "files": [ + ".reuse", + "LICENSES", + "api.d.ts", + "CHANGELOG.md", + "CHANGELOG.old.md" + ], + "repository": { + "type": "git", + "url": "https://github.com/SAP/app-studio-toolkit.git", + "directory": "projects/vscode-logging/packages/types" + }, + "bugs": { + "url": "https://github.com/SAP/app-studio-toolkit/issues" + }, + "license": "Apache-2.0", + "typings": "./api.d.ts", + "scripts": { + "ci": "npm-run-all type-check", + "type-check": "tsc" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/projects/vscode-logging/packages/types/tsconfig.json b/projects/vscode-logging/packages/types/tsconfig.json new file mode 100644 index 000000000..0fe006951 --- /dev/null +++ b/projects/vscode-logging/packages/types/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "noEmit": true + }, + "include": ["./"] +} diff --git a/projects/vscode-logging/packages/wrapper/.npmignore b/projects/vscode-logging/packages/wrapper/.npmignore new file mode 100644 index 000000000..784afc436 --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/.npmignore @@ -0,0 +1,13 @@ +# TypeScript +tsconfig.json + +# NYC +.nyc_output +nyc.config.js +coverage + +# tests +test + +# Dev Docs +CONTRIBUTING.md diff --git a/projects/vscode-logging/packages/wrapper/CHANGELOG.old.md b/projects/vscode-logging/packages/wrapper/CHANGELOG.old.md new file mode 100644 index 000000000..72c392bda --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/CHANGELOG.old.md @@ -0,0 +1,88 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [2.0.9](https://github.com/SAP/vscode-logging/compare/v2.0.8...v2.0.9) (2026-04-16) + +**Note:** Version bump only for package @vscode-logging/wrapper + +## [2.0.8](https://github.com/SAP/vscode-logging/compare/v2.0.7...v2.0.8) (2026-02-18) + +### Bug Fixes + +- correct repository URL case (SAP instead of sap) for npm provenance ([00a91ca](https://github.com/SAP/vscode-logging/commit/00a91ca002ba88ed27278f756b7aca8865eee1ee)) + +## [2.0.7](https://github.com/sap/vscode-logging/compare/v2.0.6...v2.0.7) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/wrapper + +## [2.0.6](https://github.com/sap/vscode-logging/compare/v2.0.5...v2.0.6) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/wrapper + +## [2.0.5](https://github.com/sap/vscode-logging/compare/v2.0.4...v2.0.5) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/wrapper + +## [2.0.4](https://github.com/sap/vscode-logging/compare/v2.0.3...v2.0.4) (2026-02-18) + +**Note:** Version bump only for package @vscode-logging/wrapper + +## [2.0.3](https://github.com/sap/vscode-logging/compare/v2.0.2...v2.0.3) (2026-02-17) + +**Note:** Version bump only for package @vscode-logging/wrapper + +## [2.0.2](https://github.com/sap/vscode-logging/compare/v2.0.1...v2.0.2) (2026-02-17) + +**Note:** Version bump only for package @vscode-logging/wrapper + +## [2.0.1](https://github.com/sap/vscode-logging/compare/v2.0.0...v2.0.1) (2026-02-15) + +### Bug Fixes + +- upgrade lodash to version 4.17.23 ([#241](https://github.com/sap/vscode-logging/issues/241)) ([69fc2ce](https://github.com/sap/vscode-logging/commit/69fc2ce233e960219efca1118027638cc115874c)) +- workspace protocol ([#243](https://github.com/sap/vscode-logging/issues/243)) ([3091351](https://github.com/sap/vscode-logging/commit/30913512554b29590b16bce4ad2a1eb3890b5dc9)) + +# 2.0.0 (2024-01-05) + +### Bug Fixes + +- upgrade lodash version due to CVE-2021-23337 ([c514c16](https://github.com/sap/vscode-logging/commit/c514c169ae5941cea9ebd5ae0dcb4eef8fc431d8)) + +### Features + +- **wrapper:** remove dep to @types/vscode ([#167](https://github.com/sap/vscode-logging/issues/167)) ([b362e5c](https://github.com/sap/vscode-logging/commit/b362e5c3b11020ab09a5e705d7834fa53e8bd48e)) +- logger wrapper package ([#163](https://github.com/sap/vscode-logging/issues/163)) ([fc6abc5](https://github.com/sap/vscode-logging/commit/fc6abc5ea43403c3039edb8589c68a0a339e5ebc)) + +### BREAKING CHANGES + +- **wrapper:** `getConfigurations` and `onDidChangeConfiguration` properties + were removed from the `configureLogger` public API + +## [1.0.2](https://github.com/sap/vscode-logging/compare/@vscode-logging/wrapper@1.0.1...@vscode-logging/wrapper@1.0.2) (2021-09-12) + +### Bug Fixes + +- upgrade lodash version due to CVE-2021-23337 ([c514c16](https://github.com/sap/vscode-logging/commit/c514c169ae5941cea9ebd5ae0dcb4eef8fc431d8)) + +## [1.0.1](https://github.com/sap/vscode-logging/compare/@vscode-logging/wrapper@1.0.0...@vscode-logging/wrapper@1.0.1) (2021-04-13) + +**Note:** Version bump only for package @vscode-logging/wrapper + +# [1.0.0](https://github.com/sap/vscode-logging/compare/@vscode-logging/wrapper@0.2.0...@vscode-logging/wrapper@1.0.0) (2021-02-10) + +### Features + +- **wrapper:** remove dep to @types/vscode ([#167](https://github.com/sap/vscode-logging/issues/167)) ([b362e5c](https://github.com/sap/vscode-logging/commit/b362e5c3b11020ab09a5e705d7834fa53e8bd48e)) + +### BREAKING CHANGES + +- **wrapper:** `getConfigurations` and `onDidChangeConfiguration` properties + were removed from the `configureLogger` public API + +# 0.2.0 (2021-02-07) + +### Features + +- logger wrapper package ([#163](https://github.com/sap/vscode-logging/issues/163)) ([fc6abc5](https://github.com/sap/vscode-logging/commit/fc6abc5ea43403c3039edb8589c68a0a339e5ebc)) diff --git a/projects/vscode-logging/packages/wrapper/README.md b/projects/vscode-logging/packages/wrapper/README.md new file mode 100644 index 000000000..fa3db1f1f --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/README.md @@ -0,0 +1,29 @@ +# @vscode-logging/wrapper + +A **optional** and **opinionated** wrapper utilities for @vscode-logging/logger. + +## Installation + +With npm: + +- `npm install @vscode-logging/wrapper --save` + +With Yarn: + +- `yarn add @vscode-logging/wrapper` + +## Usage + +Please see the [TypeScript Definitions](./api.d.ts) for full API details. + +## Example + +An example VSCode extension is available in the [examples' folder](https://github.com/SAP/app-studio-toolkit/tree/main/projects/vscode-logging/examples/extension-wrapper). + +## Support + +Please report issues [here](https://github.com/SAP/app-studio-toolkit/issues/new/choose) and label them with `project:vscode-logging`. + +## Contributing + +See [CONTRIBUTING.md](https://github.com/SAP/app-studio-toolkit/blob/main/CONTRIBUTING.md). diff --git a/projects/vscode-logging/packages/wrapper/api.d.ts b/projects/vscode-logging/packages/wrapper/api.d.ts new file mode 100644 index 000000000..8eddf63f2 --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/api.d.ts @@ -0,0 +1,53 @@ +import { IVSCodeExtLogger } from "@vscode-logging/types"; +import { BasicOutputChannel } from "@vscode-logging/logger"; + +/** + * An "empty" Logger implementation. + * This value should be used to initialize an extension's logger + * before the `activate` function has been called. + */ +export declare const NOOP_LOGGER: IVSCodeExtLogger; + +export interface ConfigureLoggerOpts { + /** + * @see {GetExtensionLoggerOpts.extName} in "@vscode-logging/logger" + */ + extName: string; + /** + * @see {GetExtensionLoggerOpts.logPath} in "@vscode-logging/logger" + */ + logPath: string; + loggingLevelProp: string; + sourceLocationProp: string; + /** + * @see {GetExtensionLoggerOpts.logOutputChannel} in "@vscode-logging/logger" + */ + logOutputChannel?: BasicOutputChannel; + /** + * @see {GetExtensionLoggerOpts.logConsole} in "@vscode-logging/logger" + */ + logConsole?: boolean; + /** + * The vscode extension's subscriptions + * This is normally available via the `activate` function's `context` + * parameter. + */ + subscriptions: { dispose(): any }[]; +} + +/** + * The main opinionated wrapper utility. + * This will do the following: + * + * 1. Initialize an @vscode-logging/logger instance. + * 2. Log the initial file `logPath` and `logLevel`. + * 3. Register a listener for VSCode workspace configuration changes + * for the `logLevel` and `sourceLocationTracking` + * + * - Note that this utility is slightly opinionated and has more more restrictive + * than the @vscode-logging/logger APIs, e.g: + * 1. `logPath` param is mandatory. + * 2. default logLevel (if none is found in the workspace configuration) is `error`. + * 3. ... + */ +export function configureLogger(opts: ConfigureLoggerOpts): IVSCodeExtLogger; diff --git a/projects/vscode-logging/packages/wrapper/nyc.config.js b/projects/vscode-logging/packages/wrapper/nyc.config.js new file mode 100644 index 000000000..de7679021 --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/nyc.config.js @@ -0,0 +1,22 @@ +module.exports = { + reporter: ["text", "lcov"], + "check-coverage": false, + all: true, + include: ["dist/src/**/*.js"], + exclude: [ + // The `api.ts` is the only file which imports("vscode") + // We could still test it (e.g: with `proxyquire`) + // However the added value of such a test is smaller than + // the overhead cost of implementing such a test: + // - The api.ts only + // 1. Import needed params from `vscode` package. + // 2. merges user arguments with `vscode` params and calls an internal API. + // + // - These merges are partially "tested" at design time by TSC. + // - Implementing a test with proxyquire would not test real VSCode scenarios + // - The existing `configureLogger` tests are fairly complex and we are better off avoiding duplicating them. + // - See: `configure-logger-spec.ts`. + "dist/src/api.js", + ], + excludeAfterRemap: false, +}; diff --git a/projects/vscode-logging/packages/wrapper/package.json b/projects/vscode-logging/packages/wrapper/package.json new file mode 100644 index 000000000..33d876f9f --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/package.json @@ -0,0 +1,58 @@ +{ + "name": "@vscode-logging/wrapper", + "version": "2.0.9", + "description": "opinionated DI based wrapper for @vscode-logging/logger", + "keywords": [ + "vscode", + "logger" + ], + "files": [ + ".reuse", + "LICENSES", + "dist/src", + "api.d.ts", + "CHANGELOG.md", + "CHANGELOG.old.md" + ], + "main": "dist/src/api.js", + "repository": { + "type": "git", + "url": "https://github.com/SAP/app-studio-toolkit.git", + "directory": "projects/vscode-logging/packages/wrapper" + }, + "bugs": { + "url": "https://github.com/SAP/app-studio-toolkit/issues" + }, + "license": "Apache-2.0", + "typings": "./api.d.ts", + "dependencies": { + "@vscode-logging/logger": "workspace:*", + "@vscode-logging/types": "workspace:*" + }, + "//@types/vscode": "Pinned to the minimum VS Code API version supported here (kept from the standalone vscode-logging repo). Intentionally not raised to the monorepo root version, which would allow newer APIs and break support for older VS Code.", + "devDependencies": { + "@types/lodash": "4.14.202", + "@types/vscode": "1.52.0", + "lodash": "4.18.1" + }, + "scripts": { + "ci": "npm-run-all clean compile coverage:*", + "clean": "rimraf ./dist ./coverage", + "compile": "tsc", + "test": "mocha \"./dist/test/**/*spec.js\"", + "coverage:run": "nyc mocha \"./dist/test/**/*spec.js\"", + "coverage:check": "nyc check-coverage --lines 100 --branches 100 --statements 100 --functions 100" + }, + "publishConfig": { + "access": "public" + }, + "nyc": { + "include": [ + "dist/src/**/*.js" + ], + "reporter": [ + "text", + "lcov" + ] + } +} diff --git a/projects/vscode-logging/packages/wrapper/src/api.ts b/projects/vscode-logging/packages/wrapper/src/api.ts new file mode 100644 index 000000000..1ad1df7bb --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/src/api.ts @@ -0,0 +1,25 @@ +/** + * This is the only file in the `src` folder we import("vscode") + * The rest of the code is implemented with Dependency Injection to + * the relevant VSCode APIs to enable easier testing. + */ +import { workspace } from "vscode"; +import { configureLoggerInternal } from "./configure-logger"; +import { BasicOutputChannel } from "@vscode-logging/logger"; +export { NOOP_LOGGER } from "./noop-logger"; + +export function configureLogger(opts: { + extName: string; + logPath: string; + loggingLevelProp: string; + sourceLocationProp: string; + logOutputChannel?: BasicOutputChannel; + logConsole?: boolean; + subscriptions: { dispose(): any }[]; +}) { + return configureLoggerInternal({ + ...opts, + getConfiguration: workspace.getConfiguration, + onDidChangeConfiguration: workspace.onDidChangeConfiguration, + }); +} diff --git a/projects/vscode-logging/packages/wrapper/src/configure-logger.ts b/projects/vscode-logging/packages/wrapper/src/configure-logger.ts new file mode 100644 index 000000000..3b78025cd --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/src/configure-logger.ts @@ -0,0 +1,69 @@ +import { workspace } from "vscode"; +import { getExtensionLogger } from "@vscode-logging/logger"; +import { IVSCodeExtLogger } from "@vscode-logging/types"; +import { ConfigureLoggerOpts } from "../api"; +import { + getLoggingLevelSetting, + getSourceLocationTrackingSetting, +} from "./settings"; +import { + listenToLogSettingsChanges, + logLoggerDetails, +} from "./settings-changes-handler"; + +export type ConfigureLoggerDIOpts = { + /** + * The `vscode.workspace.getConfiguration` method. + * Note this is the method itself, not the returned value from executing it. + */ + getConfiguration: typeof workspace.getConfiguration; + /** + * The `vscode.workspace.onDidChangeConfiguration` method. + * Note this is the method itself, not the returned value from executing it. + */ + onDidChangeConfiguration: typeof workspace.onDidChangeConfiguration; +}; + +export type configureLoggerInternalOpts = ConfigureLoggerOpts & + ConfigureLoggerDIOpts; + +export function configureLoggerInternal( + opts: configureLoggerInternalOpts +): IVSCodeExtLogger { + const logLevelSetting = getLoggingLevelSetting({ + getConfiguration: opts.getConfiguration, + loggingLevelProp: opts.loggingLevelProp, + }); + + const sourceLocationTrackingSettings = getSourceLocationTrackingSetting({ + getConfiguration: opts.getConfiguration, + sourceLocationProp: opts.sourceLocationProp, + }); + + const extensionLogger = getExtensionLogger({ + extName: opts.extName, + level: logLevelSetting, + logPath: opts.logPath, + sourceLocationTracking: sourceLocationTrackingSettings, + logConsole: opts.logConsole, + logOutputChannel: opts.logOutputChannel, + }); + + logLoggerDetails({ + logger: extensionLogger, + logPath: opts.logPath, + logLevel: logLevelSetting, + }); + + listenToLogSettingsChanges({ + subscriptions: opts.subscriptions, + onDidChangeConfiguration: opts.onDidChangeConfiguration, + getConfiguration: opts.getConfiguration, + loggingLevelProp: opts.loggingLevelProp, + sourceLocationProp: opts.sourceLocationProp, + logger: extensionLogger, + logPath: opts.logPath, + }); + + return extensionLogger; +} diff --git a/projects/vscode-logging/packages/wrapper/src/helper-types.d.ts b/projects/vscode-logging/packages/wrapper/src/helper-types.d.ts new file mode 100644 index 000000000..794695c56 --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/src/helper-types.d.ts @@ -0,0 +1,33 @@ +import { + IChildLogger, + IVSCodeExtLogger, + LogLevel, +} from "@vscode-logging/types"; +// importing "vscode" in a **d.ts** files avoids a actual runtime dependency to vscode. +import { ExtensionContext, workspace } from "vscode"; + +export interface GetLoggingLevelOpts { + getConfiguration: typeof workspace.getConfiguration; + loggingLevelProp: string; +} + +export interface GetSourceLocationOpts { + getConfiguration: typeof workspace.getConfiguration; + sourceLocationProp: string; +} + +export interface ListenToLogSettingsOpts { + subscriptions: ExtensionContext["subscriptions"]; + onDidChangeConfiguration: typeof workspace.onDidChangeConfiguration; + getConfiguration: typeof workspace.getConfiguration; + loggingLevelProp: string; + sourceLocationProp: string; + logger: IVSCodeExtLogger; + logPath: string; +} + +export interface LogLoggerDetailsOpts { + logger: IChildLogger; + logPath: string; + logLevel: LogLevel; +} diff --git a/projects/vscode-logging/packages/wrapper/src/noop-logger.ts b/projects/vscode-logging/packages/wrapper/src/noop-logger.ts new file mode 100644 index 000000000..89e079588 --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/src/noop-logger.ts @@ -0,0 +1,19 @@ +import { IVSCodeExtLogger, IChildLogger } from "@vscode-logging/types"; + +export function noop() {} + +export const NOOP_LOGGER: IVSCodeExtLogger = { + changeLevel: noop, + changeSourceLocationTracking: noop, + debug: noop, + error: noop, + fatal: noop, + getChildLogger(opts: { label: string }): IChildLogger { + return this; + }, + info: noop, + trace: noop, + warn: noop, +}; + +Object.freeze(NOOP_LOGGER); diff --git a/projects/vscode-logging/packages/wrapper/src/settings-changes-handler.ts b/projects/vscode-logging/packages/wrapper/src/settings-changes-handler.ts new file mode 100644 index 000000000..24071cc48 --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/src/settings-changes-handler.ts @@ -0,0 +1,45 @@ +import { LogLevel } from "@vscode-logging/types"; +import { ListenToLogSettingsOpts, LogLoggerDetailsOpts } from "./helper-types"; +import { + getLoggingLevelSetting, + getSourceLocationTrackingSetting, +} from "./settings"; + +export function logLoggerDetails(opts: LogLoggerDetailsOpts): void { + opts.logger.info(`Start Logging in Log Level: <${opts.logLevel}>`); + opts.logger.info(`Full Logs can be found in the <${opts.logPath}> folder.`); +} + +export function listenToLogSettingsChanges( + opts: ListenToLogSettingsOpts +): void { + opts.subscriptions.push( + opts.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration(opts.loggingLevelProp)) { + const logLevel: LogLevel = getLoggingLevelSetting({ + loggingLevelProp: opts.loggingLevelProp, + getConfiguration: opts.getConfiguration, + }); + opts.logger.changeLevel(logLevel); + logLoggerDetails({ + logger: opts.logger, + logPath: opts.logPath, + logLevel, + }); + } + }) + ); + + opts.subscriptions.push( + opts.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration(opts.sourceLocationProp)) { + const newSourceLocationTracking: boolean = + getSourceLocationTrackingSetting({ + sourceLocationProp: opts.sourceLocationProp, + getConfiguration: opts.getConfiguration, + }); + opts.logger.changeSourceLocationTracking(newSourceLocationTracking); + } + }) + ); +} diff --git a/projects/vscode-logging/packages/wrapper/src/settings.ts b/projects/vscode-logging/packages/wrapper/src/settings.ts new file mode 100644 index 000000000..23496fede --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/src/settings.ts @@ -0,0 +1,12 @@ +import { LogLevel } from "@vscode-logging/logger"; +import { GetLoggingLevelOpts, GetSourceLocationOpts } from "./helper-types"; + +export function getLoggingLevelSetting(opts: GetLoggingLevelOpts): LogLevel { + return opts.getConfiguration().get(opts.loggingLevelProp, "error"); +} + +export function getSourceLocationTrackingSetting( + opts: GetSourceLocationOpts +): boolean { + return opts.getConfiguration().get(opts.sourceLocationProp, false); +} diff --git a/projects/vscode-logging/packages/wrapper/test/configure-logger-spec.ts b/projects/vscode-logging/packages/wrapper/test/configure-logger-spec.ts new file mode 100644 index 000000000..165a299d5 --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/test/configure-logger-spec.ts @@ -0,0 +1,81 @@ +import { expect } from "chai"; +import { ExtensionContext, WorkspaceConfiguration } from "vscode"; +import { IChildLogger, LogLevel } from "@vscode-logging/types"; +import { BasicOutputChannel } from "@vscode-logging/logger"; +import { configureLoggerInternal } from "../src/configure-logger"; +import { configureLoggerInternalOpts } from "../src/configure-logger"; + +describe("The `configureLogger` main wrapper utility function", () => { + let outputChannelMock: BasicOutputChannel; + let subscriptions: ExtensionContext["subscriptions"]; + let onDidChangeConfiguration: configureLoggerInternalOpts["onDidChangeConfiguration"]; + let loggingLevelProp: string; + let sourceLocationProp: string; + let configLogLevel: LogLevel; + let getConfiguration: configureLoggerInternalOpts["getConfiguration"]; + let loggedLines: Record[]; + + beforeEach(() => { + loggedLines = []; + subscriptions = []; + }); + + before(() => { + outputChannelMock = { + appendLine(value: string): void { + loggedLines.push(JSON.parse(value)); + }, + dispose(): void {}, + show(): void {}, + }; + + // we are not testing configuration changes in this spec, these will be tested + // in `settings-changes-handler-spec` + onDidChangeConfiguration = () => { + return { dispose() {} }; + }; + + loggingLevelProp = "my_vscode_ext.loggingLevel"; + sourceLocationProp = "my_vscode_ext.sourceLocationTracking"; + configLogLevel = "debug"; + const settingsMap = new Map([ + [loggingLevelProp, configLogLevel], + [sourceLocationProp, false], + ]); + // don't worry be happy (or unknown)... + getConfiguration = (_) => settingsMap as unknown as WorkspaceConfiguration; + }); + + function configureLoggerHelper(): IChildLogger { + return configureLoggerInternal({ + extName: "my_vscode_ext", + // only logging "in memory" during our test. + logPath: undefined as unknown as string, + loggingLevelProp: "my_vscode_ext.loggingLevel", + sourceLocationProp: "my_vscode_ext.sourceLocationTracking", + logOutputChannel: outputChannelMock, + subscriptions, + getConfiguration, + onDidChangeConfiguration, + }); + } + + it("will log initial details on configure", () => { + expect(loggedLines).to.be.empty; + configureLoggerHelper(); + expect(loggedLines).to.have.lengthOf(2); + const loggerDetailsEntry = loggedLines[0]; + expect(loggerDetailsEntry.message).to.include(configLogLevel); + }); + + it("will return a VSCodeExtLogger that can be used for 'regular logging' e.g log/info/debug", () => { + expect(loggedLines).to.be.empty; + const testLogger = configureLoggerHelper(); + loggedLines = []; + testLogger.debug("hello world", { param1: 666 }); + expect(loggedLines).to.have.lengthOf(1); + const loggerEntry = loggedLines[0]; + expect(loggerEntry.message).to.equal("hello world"); + expect(loggerEntry.param1).to.equal(666); + }); +}); diff --git a/projects/vscode-logging/packages/wrapper/test/noop-logger-spec.ts b/projects/vscode-logging/packages/wrapper/test/noop-logger-spec.ts new file mode 100644 index 000000000..0ed8a678f --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/test/noop-logger-spec.ts @@ -0,0 +1,35 @@ +import { expect } from "chai"; +import { keys, difference, forEach } from "lodash"; +import { noop, NOOP_LOGGER } from "../src/noop-logger"; + +describe("The no-operation logger", () => { + const noopFuncProps = difference(keys(NOOP_LOGGER), ["getChildLogger"]); + + forEach(noopFuncProps, (funcPropName) => { + it(`exposes a no operation method for <${funcPropName}>`, () => { + // @ts-expect-error -- runtime reflection + expect(NOOP_LOGGER[funcPropName]).to.eql(noop); + }); + }); + + it("uses an **empty** method for the noop implementation", () => { + const noopSource = noop.toString(); + // + // The `cov_...` section in optional capturing group handles the case of execution under nyc wrapper + // - instrumented code, in which case `toString` would return something like: + // ``` + // function noop(){cov_1w547p8w8s().f[0]++;}' + // ``` + expect(noopSource).to.match( + /function\s*\w+\s*\(\s*\)\s*{\s*(cov_\w+\(\)\.f\[\d]\+\+;)?}/ + ); + }); + + it("will not throw when executing the NOOP function", () => { + expect(noop()).to.not.throw; + }); + + it("implements by returning 'itself'", () => { + expect(NOOP_LOGGER.getChildLogger({ label: "foo" })).to.equal(NOOP_LOGGER); + }); +}); diff --git a/projects/vscode-logging/packages/wrapper/test/settings-changes-handler-spec.ts b/projects/vscode-logging/packages/wrapper/test/settings-changes-handler-spec.ts new file mode 100644 index 000000000..af7989654 --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/test/settings-changes-handler-spec.ts @@ -0,0 +1,158 @@ +import { expect } from "chai"; +import { + ConfigurationChangeEvent, + Disposable, + ExtensionContext, + WorkspaceConfiguration, +} from "vscode"; +import { LogLevel } from "@vscode-logging/logger"; +import { IChildLogger, IVSCodeExtLogger } from "@vscode-logging/types"; +import { configureLoggerInternalOpts } from "../src/configure-logger"; +import { listenToLogSettingsChanges } from "../src/settings-changes-handler"; + +describe("The `listenToLogSettingsChanges` utility function", () => { + let logger: IVSCodeExtLogger; + let loggingLevelProp: string; + let sourceLocationProp: string; + let logPath: string; + let getConfiguration: configureLoggerInternalOpts["getConfiguration"]; + let onDidChangeConfiguration: configureLoggerInternalOpts["onDidChangeConfiguration"]; + let subscriptions: ExtensionContext["subscriptions"]; + let currentLogLevel: LogLevel; + let currentSourceLocationTracking: boolean; + + beforeEach(() => { + currentLogLevel = "error"; + currentSourceLocationTracking = false; + }); + + before(() => { + logPath = "c:\\logs"; + + logger = { + changeLevel(newLevel: LogLevel) { + currentLogLevel = newLevel; + }, + changeSourceLocationTracking(newSourceLocation: boolean) { + currentSourceLocationTracking = newSourceLocation; + }, + getChildLogger(): IChildLogger { + return this; + }, + fatal(): void {}, + error(): void {}, + warn(): void {}, + info(): void {}, + debug(): void {}, + trace(): void {}, + }; + + loggingLevelProp = "my_vscode_ext.loggingLevel"; + sourceLocationProp = "my_vscode_ext.sourceLocationTracking"; + subscriptions = []; + }); + + context("affects Configuration", () => { + before(() => { + const settingsMap = new Map([ + [loggingLevelProp, "info"], + [sourceLocationProp, true], + ]); + // don't worry be happy (or unknown)... + getConfiguration = (_) => + settingsMap as unknown as WorkspaceConfiguration; + + onDidChangeConfiguration = function ( + cb: (e: ConfigurationChangeEvent) => void + ): Disposable { + const e: ConfigurationChangeEvent = { + affectsConfiguration(): boolean { + return true; + }, + }; + cb(e); + return { dispose(): any {} }; + }; + }); + + it("will listen to `logLevel` changes", () => { + expect(currentLogLevel).to.equal("error"); + listenToLogSettingsChanges({ + sourceLocationProp, + loggingLevelProp, + getConfiguration, + logger, + logPath, + onDidChangeConfiguration, + subscriptions, + }); + expect(currentLogLevel).to.equal("info"); + }); + + it("will listen to `sourceLocationTracking` changes", () => { + expect(currentSourceLocationTracking).to.be.false; + listenToLogSettingsChanges({ + sourceLocationProp, + loggingLevelProp, + getConfiguration, + logger, + logPath, + onDidChangeConfiguration, + subscriptions, + }); + expect(currentSourceLocationTracking).to.be.true; + }); + }); + + context("does **not** affects Configuration", () => { + before(() => { + const settingsMap = new Map([ + ["someOtherProp", "blue"], + ["someOtherProp2", 666], + ]); + // don't worry be happy (or unknown)... + getConfiguration = (_) => + settingsMap as unknown as WorkspaceConfiguration; + + onDidChangeConfiguration = function ( + cb: (e: ConfigurationChangeEvent) => void + ): Disposable { + const e: ConfigurationChangeEvent = { + affectsConfiguration(): boolean { + return false; + }, + }; + cb(e); + return { dispose(): any {} }; + }; + }); + + it("will **not** listen to unrelated configuration changes - logLevel", () => { + expect(currentLogLevel).to.equal("error"); + listenToLogSettingsChanges({ + sourceLocationProp, + loggingLevelProp, + getConfiguration, + logger, + logPath, + onDidChangeConfiguration, + subscriptions, + }); + expect(currentLogLevel).to.equal("error"); + }); + + it("will **not** listen to unrelated configuration changes - sourceLocationTracking", () => { + expect(currentSourceLocationTracking).to.be.false; + listenToLogSettingsChanges({ + sourceLocationProp, + loggingLevelProp, + getConfiguration, + logger, + logPath, + onDidChangeConfiguration, + subscriptions, + }); + expect(currentSourceLocationTracking).to.be.false; + }); + }); +}); diff --git a/projects/vscode-logging/packages/wrapper/test/settings-spec.ts b/projects/vscode-logging/packages/wrapper/test/settings-spec.ts new file mode 100644 index 000000000..9f222a60d --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/test/settings-spec.ts @@ -0,0 +1,51 @@ +import { expect } from "chai"; +import { + getLoggingLevelSetting, + getSourceLocationTrackingSetting, +} from "../src/settings"; +import { GetLoggingLevelOpts } from "../src/helper-types"; +import { WorkspaceConfiguration } from "vscode"; + +describe("The settings related utilities", () => { + describe("The function", () => { + let getConfiguration: GetLoggingLevelOpts["getConfiguration"]; + + before(() => { + const settingsMap = new Map([ + ["ext1.loggingLevel", "debug"], + ["otherExt.loggingLevel", "info"], + ]); + getConfiguration = (_) => + settingsMap as unknown as WorkspaceConfiguration; + }); + + it("Can retrieve the logging level value from a configuration", () => { + const logLevel = getLoggingLevelSetting({ + getConfiguration, + loggingLevelProp: "ext1.loggingLevel", + }); + expect(logLevel).to.equal("debug"); + }); + }); + + describe("The function", () => { + let getConfiguration: GetLoggingLevelOpts["getConfiguration"]; + + before(() => { + const settingsMap = new Map([ + ["ext1.sourceLocationTracking", true], + ["otherExt.sourceLocationTracking", false], + ]); + getConfiguration = (_) => + settingsMap as unknown as WorkspaceConfiguration; + }); + + it("Can retrieve the source location tracking value from a configuration", () => { + const sourceLocationEnabled = getSourceLocationTrackingSetting({ + getConfiguration, + sourceLocationProp: "otherExt.sourceLocationTracking", + }); + expect(sourceLocationEnabled).to.be.false; + }); + }); +}); diff --git a/projects/vscode-logging/packages/wrapper/tsconfig.json b/projects/vscode-logging/packages/wrapper/tsconfig.json new file mode 100644 index 000000000..3de9cb2a6 --- /dev/null +++ b/projects/vscode-logging/packages/wrapper/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "dist", + "baseUrl": "." + }, + "include": ["./src/**/*.ts", "./test/**/*.ts", "./api.d.ts"] +} diff --git a/projects/vscode-mta-tools/package.json b/projects/vscode-mta-tools/package.json index 1c58698ea..96b2fd994 100644 --- a/projects/vscode-mta-tools/package.json +++ b/projects/vscode-mta-tools/package.json @@ -215,7 +215,7 @@ "lodash": "4.17.21", "comment-json": "4.1.1", "fs-extra": "10.0.0", - "@vscode-logging/logger": "1.2.3", + "@vscode-logging/logger": "workspace:*", "@sap/swa-for-sapbas-vsx": "1.2.15", "@sap/mta-lib": "1.7.4", "@sap/cf-tools": "2.0.1", diff --git a/scripts/merge-coverage.js b/scripts/merge-coverage.js index 96ae648ed..d4f680d2d 100644 --- a/scripts/merge-coverage.js +++ b/scripts/merge-coverage.js @@ -3,7 +3,7 @@ * This report will can be found at the root `coverage` dir and will also be uploaded to coveralls.io * - based on https://github.com/istanbuljs/istanbuljs/blob/1fe490e51909607137ded25b1688581c9fd926cd/monorepo-merge-reports.js */ -const { dirname, basename, join, resolve } = require("path"); +const { dirname, join, resolve } = require("path"); const { spawnSync } = require("child_process"); const rimraf = require("rimraf"); @@ -14,16 +14,20 @@ process.chdir(resolve(__dirname, "..")); rimraf.sync(".nyc_output"); makeDir.sync(".nyc_output"); -// Merge coverage data from each package so we can generate a complete reports -glob.sync("packages/*/.nyc_output").forEach((nycOutput) => { +// Merge coverage data from each package so we can generate a complete reports. +// Includes both root-level packages and packages nested under projects/*. +const nycOutputs = [ + ...glob.sync("packages/*/.nyc_output"), + ...glob.sync("projects/*/packages/*/.nyc_output"), +]; +nycOutputs.forEach((nycOutput) => { const cwd = dirname(nycOutput); + // Use the package's path (not just its basename) for the merged file name so + // same-named packages across projects (e.g. */packages/types) do not collide. + const mergedName = cwd.replace(/[\\/]/g, "__") + ".json"; const { status, stderr } = spawnSync( resolve("node_modules", ".bin", "nyc"), - [ - "merge", - ".nyc_output", - join(__dirname, "..", ".nyc_output", basename(cwd) + ".json"), - ], + ["merge", ".nyc_output", join(__dirname, "..", ".nyc_output", mergedName)], { encoding: "utf8", shell: true,