From 32c2feb2e084762d9a8c1ccaca8497fafdeec16b Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Fri, 15 Aug 2025 22:45:14 +0200 Subject: [PATCH 01/10] feat: add mssql support --- .gitignore | 1 + README.md | 2 + package-lock.json | 1226 ++++++++++++++++- package.json | 8 +- src/commands/actions/generatePlanAsync.ts | 18 +- .../actions/generateSnapshotsAsync.ts | 98 +- src/commands/actions/tryConnectionAsync.ts | 31 +- src/commands/analyzeDataAsync.ts | 15 +- src/commands/analyzeDataOnImportAsync.ts | 7 +- src/commands/analyzeDataOnRefreshAsync.ts | 9 +- src/commands/migrateDataAsync.ts | 321 ++++- src/templates/configuration.ts | 2 + src/utils/database/databaseProvider.ts | 32 + src/utils/database/databaseProviderFactory.ts | 14 + src/utils/database/mssqlProvider.ts | 147 ++ src/utils/database/postgresProvider.ts | 107 ++ src/utils/postgresProvider.ts | 107 ++ src/utils/query.ts | 161 ++- src/utils/types.ts | 3 + src/utils/utils.ts | 16 +- tsconfig.json | 8 +- 21 files changed, 2130 insertions(+), 203 deletions(-) create mode 100644 src/utils/database/databaseProvider.ts create mode 100644 src/utils/database/databaseProviderFactory.ts create mode 100644 src/utils/database/mssqlProvider.ts create mode 100644 src/utils/database/postgresProvider.ts create mode 100644 src/utils/postgresProvider.ts create mode 100644 src/utils/types.ts diff --git a/.gitignore b/.gitignore index 08b341a..beed46a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ pnpm-lock.yaml yarn-lock.json .vscode/database.json yarn-error.log +.history diff --git a/README.md b/README.md index b041c0d..f086af0 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Get it from [Visual Studio Marketplace](https://marketplace.visualstudio.com/ite "patternName": { // Your source database connection section "source": { + "type": "postgres", // Or mssql "user": string, "password": string, "host": string, @@ -90,6 +91,7 @@ Get it from [Visual Studio Marketplace](https://marketplace.visualstudio.com/ite // Your target database connection section "target": { + "type": "postgres", // Or mssql "user": string, "password": string, "host": string, diff --git a/package-lock.json b/package-lock.json index 2f676cb..f88c1e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "data-sync", - "version": "1.0.27", + "version": "1.0.31", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "data-sync", - "version": "1.0.27", + "version": "1.0.31", "license": "MIT", "dependencies": { "@types/chance": "^1.1.3", @@ -15,6 +15,7 @@ "diff": "^5.1.0", "fs-extra": "^11.1.1", "lodash.groupby": "^4.6.0", + "msnodesqlv8": "^5.1.1", "pg": "^8.11.1", "pg-iterator": "^0.3.0", "pg-promise": "^11.5.0" @@ -26,6 +27,7 @@ "@types/glob": "^8.1.0", "@types/lodash.groupby": "^4.6.7", "@types/mocha": "^10.0.1", + "@types/mssql": "^9.1.7", "@types/node": "^20.2.5", "@types/pg": "^8.10.2", "@types/pg-promise": "^5.4.3", @@ -43,6 +45,7 @@ "eslint": "^8.45.0", "glob": "^10.2.6", "mocha": "^10.2.0", + "mssql": "^11.0.1", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "process": "^0.11.10", @@ -65,6 +68,281 @@ "node": ">=0.10.0" } }, + "node_modules/@azure-rest/core-client": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.0.tgz", + "integrity": "sha512-KMVIPxG6ygcQ1M2hKHahF7eddKejYsWTjoLIfTWiqnaj42dBkYzj4+S8rK9xxmlOaEHKZHcMrRbm0NfN4kgwHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-rest-pipeline": "^1.5.0", + "@azure/core-tracing": "^1.0.1", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.0.tgz", + "integrity": "sha512-88Djs5vBvGbHQHf5ZZcaoNHo6Y8BKZkt3cw2iuJIQzLEgH4Ox6Tm4hjFhbqOxyYsgIG/eJbFEHpxRIfEEWv5Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.0.tgz", + "integrity": "sha512-O4aP3CLFNodg8eTHXECaH3B3CjicfzkxVtnrfLkOq0XNP7TIECGfHpK/C6vADZkWP75wzmdBnsIA8ksuJMk18g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.20.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.0.tgz", + "integrity": "sha512-qLQujmUypBBG0gxHd0j6/Jdmul6ttl24c8WGiLXIk7IHXdBlfoBqW27hyz3Xn6xbfdyVSarl1Ttbk0AwnZBYCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.20.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.0.tgz", + "integrity": "sha512-OKHmb3/Kpm06HypvB3g6Q3zJuvyXcpxDpCS1PnU8OV6AJgSFaee/covXBcPbWc6XDDxtEPlbi3EMQ6nUiPaQtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.8.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.0.tgz", + "integrity": "sha512-+XvmZLLWPe67WXNZo9Oc9CrPj/Tm8QnHR92fFAFdnbzwNdCH1h+7UdpaQgRSBsMY+oW1kHXNUZQLdZ1gHX3ROw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.0.tgz", + "integrity": "sha512-o0psW8QWQ58fq3i24Q1K2XfS/jYTxr7O1HRcyUE9bV9NttLU+kYOH82Ixj8DGlMTOWgxm1Sss2QAfKK5UkSPxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.10.2.tgz", + "integrity": "sha512-Uth4vz0j+fkXCkbvutChUj03PDCokjbC6Wk9JT8hHEUtpy/EurNKAseb3+gO6Zi9VYBvwt61pgbzn1ovk942Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.5.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/keyvault-common": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", + "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.10.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-keys": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.10.0.tgz", + "integrity": "sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure-rest/core-client": "^2.3.3", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-lro": "^2.7.2", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.0", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/keyvault-common": "^2.0.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.18.0.tgz", + "integrity": "sha512-esQwdtHHVkFJhcKWnysnCTchiKsy3dmNZGs8AckD9PO3t8Lp5VtY0xcrbCBC0JbttG/5w2/xukUQOsMpoUFKrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.9.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.9.0.tgz", + "integrity": "sha512-lbz/D+C9ixUG3hiZzBLjU79a0+5ZXCorjel3mwXluisKNH0/rOS/ajm8yi4yI9RP5Uc70CAcs9Ipd0051Oh/kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.6.4.tgz", + "integrity": "sha512-jMeut9UQugcmq7aPWWlJKhJIse4DQ594zc/JaP6BIxg55XaX3aM/jcPuIQ4ryHnI4QSf03wUspy/uqAvjWKbOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.9.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", @@ -1134,6 +1412,13 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@js-joda/core": { + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.6.5.tgz", + "integrity": "sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@manypkg/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", @@ -1281,6 +1566,13 @@ "node": ">=14" } }, + "node_modules/@tediousjs/connection-string": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.5.0.tgz", + "integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -1404,11 +1696,27 @@ "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, + "node_modules/@types/mssql": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-9.1.7.tgz", + "integrity": "sha512-eIOEe78nuSW5KctDHImDhLZ9a+jV/z/Xs5RBhcG/jrk+YWqhdNmzBmHVWV7aWQ5fW+jbIGtX6Ph+bbVqfhzafg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "tarn": "^3.0.1", + "tedious": "*" + } + }, "node_modules/@types/node": { - "version": "20.4.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", - "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==", - "dev": true + "version": "20.19.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", + "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -1447,6 +1755,16 @@ "pg-query-stream": "*" } }, + "node_modules/@types/readable-stream": { + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.21.tgz", + "integrity": "sha512-19eKVv9tugr03IgfXlA9UVUVRbW6IuqRO5B92Dl4a6pT7K8uaGrNS0GkxiZD0BOk6PLuXl5FhWl//eX/pzYdTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/semver": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", @@ -1657,6 +1975,59 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.0.tgz", + "integrity": "sha512-sOx1PKSuFwnIl7z4RN0Ls7N9AQawmR9r66eI5rFCzLDIs8HTIYrIpH9QjYWoX0lkgGrkLxXhi4QnK7MizPRrIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@typespec/ts-http-runtime/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/@vscode/test-electron": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.3.tgz", @@ -1874,6 +2245,19 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -2122,7 +2506,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -2159,6 +2542,36 @@ "node": ">=8" } }, + "node_modules/bl": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.1.tgz", + "integrity": "sha512-yYc8UIHrd1ZTLgNBIE7JjMzUPZH+dec3q7nWkrSHEbtvkQ3h6WKC63W9K5jthcL5EXFyMuWYq+2pq5WMSIgFHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -2328,6 +2741,13 @@ "ieee754": "^1.2.1" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2348,6 +2768,22 @@ "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", "dev": true }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -2480,6 +2916,12 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -2831,12 +3273,66 @@ "node": ">=0.10.0" } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -2849,6 +3345,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -2884,6 +3393,15 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -2939,6 +3457,16 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.461", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.461.tgz", @@ -2972,6 +3500,15 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -3533,6 +4070,16 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -3552,6 +4099,15 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/extendable-error": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", @@ -3736,6 +4292,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -3842,6 +4404,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "10.3.3", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", @@ -4174,7 +4742,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -4280,8 +4847,13 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" }, "node_modules/internal-slot": { "version": "1.0.5", @@ -4433,6 +5005,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4478,6 +5066,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -4682,6 +5289,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -4759,6 +5382,13 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4807,6 +5437,29 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -4855,6 +5508,29 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -4942,12 +5618,61 @@ "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.startcase": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", @@ -5177,6 +5902,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -5210,6 +5947,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", @@ -5242,6 +5988,12 @@ "node": ">= 8.0.0" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -5489,6 +6241,57 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/msnodesqlv8": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/msnodesqlv8/-/msnodesqlv8-5.1.1.tgz", + "integrity": "sha512-Q0uhQFOulfUrimhyIoPPSqyWtY4PaPu3Gg5wnpCTBY2ugTb5KOju0pPtNT+sikFBdyClvv6IL7hdj2Pn16Ssrw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "os": [ + "win32", + "linux", + "darwin" + ], + "dependencies": { + "node-abi": "^4.12.0", + "node-addon-api": "^8.4.0", + "prebuild-install": "^7.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mssql": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-11.0.1.tgz", + "integrity": "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tediousjs/connection-string": "^0.5.0", + "commander": "^11.0.0", + "debug": "^4.3.3", + "rfdc": "^1.3.0", + "tarn": "^3.0.2", + "tedious": "^18.2.1" + }, + "bin": { + "mssql": "bin/mssql" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mssql/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/nanoid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", @@ -5501,6 +6304,19 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/native-duplexpair": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", + "integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==", + "dev": true, + "license": "MIT" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5519,6 +6335,27 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/node-abi": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.12.0.tgz", + "integrity": "sha512-bPSN9a/qIEiURzVVO/I7P/8oPeYTSl+vnvVZBXM/8XerKOgA3dMAIUjl+a+lz9VwTowwSKS3EMsgz/vWDXOkuQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.6.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", @@ -5617,11 +6454,29 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -6223,6 +7078,44 @@ "integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==", "dev": true }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/preferred-pm": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-3.0.3.tgz", @@ -6364,6 +7257,16 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -6421,6 +7324,30 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -6490,7 +7417,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -6623,6 +7549,13 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -6668,6 +7601,19 @@ "inherits": "^2.0.1" } }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6713,7 +7659,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -6768,13 +7713,10 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -6782,18 +7724,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -6887,6 +7817,51 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7194,7 +8169,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -7400,6 +8374,121 @@ "node": ">=6" } }, + "node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/tar-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/tedious": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-18.6.1.tgz", + "integrity": "sha512-9AvErXXQTd6l7TDd5EmM+nxbOGyhnmdbp/8c3pw+tjaiSXW9usME90ET/CRG1LN1Y9tPMtz/p83z4Q97B4DDpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/core-auth": "^1.7.2", + "@azure/identity": "^4.2.1", + "@azure/keyvault-keys": "^4.4.0", + "@js-joda/core": "^5.6.1", + "@types/node": ">=18", + "bl": "^6.0.11", + "iconv-lite": "^0.6.3", + "js-md4": "^0.3.2", + "native-duplexpair": "^1.0.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tedious/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tedious/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/term-size": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", @@ -7618,6 +8707,13 @@ "node": ">=8" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/tty-table": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/tty-table/-/tty-table-4.2.1.tgz", @@ -7787,6 +8883,18 @@ "node": ">=12" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7877,10 +8985,11 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7904,6 +9013,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -7967,8 +9083,17 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, "node_modules/validate-npm-package-license": { "version": "3.0.4", @@ -8337,8 +9462,23 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/xtend": { "version": "4.0.2", @@ -8357,12 +9497,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index 40899e0..2e176c6 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ "database", "management", "sync", - "postgresql" + "postgresql", + "mssql" ], "contributes": { "viewsContainers": { @@ -338,6 +339,7 @@ "@types/glob": "^8.1.0", "@types/lodash.groupby": "^4.6.7", "@types/mocha": "^10.0.1", + "@types/mssql": "^9.1.7", "@types/node": "^20.2.5", "@types/pg": "^8.10.2", "@types/pg-promise": "^5.4.3", @@ -355,6 +357,7 @@ "eslint": "^8.45.0", "glob": "^10.2.6", "mocha": "^10.2.0", + "mssql": "^11.0.1", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "process": "^0.11.10", @@ -371,6 +374,7 @@ "diff": "^5.1.0", "fs-extra": "^11.1.1", "lodash.groupby": "^4.6.0", + "msnodesqlv8": "^5.1.1", "pg": "^8.11.1", "pg-iterator": "^0.3.0", "pg-promise": "^11.5.0" @@ -381,4 +385,4 @@ }, "publisher": "nguyenngoclong", "license": "MIT" -} \ No newline at end of file +} diff --git a/src/commands/actions/generatePlanAsync.ts b/src/commands/actions/generatePlanAsync.ts index 398fd19..ea5a77f 100644 --- a/src/commands/actions/generatePlanAsync.ts +++ b/src/commands/actions/generatePlanAsync.ts @@ -30,8 +30,9 @@ const writePlanOutput = async (filePath: string, lines: string[]): Promise => { - const { fileManager, tables } = options; + const { fileManager, tables, dbType } = options; // Read plan detail const sessionPath = fileManager.getSessionPath(); @@ -85,7 +86,7 @@ export const generatePlanAsync = async (options: { // Line start with '+' => insert if (line.startsWith('+')) { const values = JSON.parse(line.substring(1)); - const insertQuery = makeInsertQuery(table, planDetail, values); + const insertQuery = makeInsertQuery(table, planDetail, values, dbType); allPlanLines.push(insertQuery); insertPlanLines.push(insertQuery); planDetail.insert++; @@ -95,7 +96,7 @@ export const generatePlanAsync = async (options: { // Line start with '-' => remove if (line.startsWith('-')) { const values = JSON.parse(line.substring(1)); - const deleteQuery = makeDeleteQuery(table, planDetail, values); + const deleteQuery = makeDeleteQuery(table, planDetail, values, dbType); allPlanLines.push(deleteQuery); deletePlanLines.push(deleteQuery); planDetail.delete++; @@ -130,14 +131,14 @@ export const generatePlanAsync = async (options: { const currentRecord = JSON.parse(currentLine); switch (operation) { case '+': { - const insertQuery = makeInsertQuery(table, planDetail, currentRecord); + const insertQuery = makeInsertQuery(table, planDetail, currentRecord, dbType); allPlanLines.push(insertQuery); insertPlanLines.push(insertQuery); planDetail.insert++; break; } case '-': { - const deleteQuery = makeDeleteQuery(table, planDetail, currentRecord); + const deleteQuery = makeDeleteQuery(table, planDetail, currentRecord, dbType); allPlanLines.push(deleteQuery); deletePlanLines.push(deleteQuery); planDetail.delete++; @@ -160,13 +161,13 @@ export const generatePlanAsync = async (options: { const sameCurrentLine = groupItems[1]; if (rawCurrentLine.startsWith('+') && sameCurrentLine.startsWith('-')) { const record = JSON.parse(rawCurrentLine.substring(1)); - const updateQuery = makeUpdateQuery(table, planDetail, record); + const updateQuery = makeUpdateQuery(table, planDetail, record, dbType); allPlanLines.push(updateQuery); updatePlanLines.push(updateQuery); planDetail.update++; } else if (rawCurrentLine.startsWith('-') && sameCurrentLine.startsWith('+')) { const record = JSON.parse(sameCurrentLine.substring(1)); - const updateQuery = makeUpdateQuery(table, planDetail, record); + const updateQuery = makeUpdateQuery(table, planDetail, record, dbType); allPlanLines.push(updateQuery); updatePlanLines.push(updateQuery); planDetail.update++; @@ -192,17 +193,14 @@ export const generatePlanAsync = async (options: { await writePlanOutput(allPlanFilePath, allPlanLines); logger.info(`The ${allPlanFilePath} was successfully generated.`); - // Output all plan files const insertPlanFilePath = fileManager.getPlanOutputPath('insert'); await writePlanOutput(insertPlanFilePath, insertPlanLines); logger.info(`The ${insertPlanFilePath} was successfully generated.`); - // Output all plan files const updatePlanFilePath = fileManager.getPlanOutputPath('update'); await writePlanOutput(updatePlanFilePath, updatePlanLines); logger.info(`The ${updatePlanFilePath} was successfully generated.`); - // Output all plan files const deletePlanFilePath = fileManager.getPlanOutputPath('delete'); await writePlanOutput(deletePlanFilePath, deletePlanLines); logger.info(`The ${deletePlanFilePath} was successfully generated.`); diff --git a/src/commands/actions/generateSnapshotsAsync.ts b/src/commands/actions/generateSnapshotsAsync.ts index 191ec9d..6917019 100644 --- a/src/commands/actions/generateSnapshotsAsync.ts +++ b/src/commands/actions/generateSnapshotsAsync.ts @@ -1,48 +1,13 @@ import fs from 'fs-extra'; import { EOL } from 'node:os'; -import pg from 'pg'; -import { QueryIterablePool } from 'pg-iterator'; import { FileManager, SnapshotType } from '../../utils/fileManager'; import { logger } from '../../utils/logger'; -import { PatternConfig, PatternSession, TableConfig, getDatabaseInfo, getTabWidth } from '../../utils/utils'; - -const getPrimaryKeys = async (options: { pool: pg.Pool; table: TableConfig }): Promise => { - const { pool, table } = options; - const rawQuery = ` - SELECT c.column_name - FROM information_schema.table_constraints t - JOIN information_schema.constraint_column_usage c - ON c.constraint_name = t.constraint_name - WHERE t.constraint_type = 'PRIMARY KEY' AND c.table_name = '${table.name}'`; - const result = await pool.query(rawQuery); - const primaryKeys = []; - for (const row of result.rows) { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { column_name } = row as { column_name: string }; - primaryKeys.push(column_name); - } - return primaryKeys; -}; - -const getColumnNames = async (options: { pool: pg.Pool; table: TableConfig }): Promise => { - const { pool, table } = options; - const rawQuery = ` - SELECT column_name - FROM information_schema.columns - WHERE table_name = '${table.name}' - ORDER BY ordinal_position`; - const result = await pool.query(rawQuery); - const tableColumns = []; - for await (const row of result.rows) { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { column_name } = row as { column_name: string }; - tableColumns.push(column_name); - } - return tableColumns; -}; +import { PatternConfig, PatternSession, TableConfig, getTabWidth } from '../../utils/utils'; +import { createDatabaseProvider } from '../../utils/database/databaseProviderFactory'; +import { DatabaseProvider } from '../../utils/database/databaseProvider'; export type GenerateSnapshotOptions = { - pool: pg.Pool; + dbProvider: DatabaseProvider; fileManager: FileManager; snapshotType: SnapshotType; formatLine?: boolean; @@ -51,10 +16,12 @@ export type GenerateSnapshotOptions = { }; export const createSnapshotFiles = async (options: GenerateSnapshotOptions): Promise => { - const { pool, fileManager, tables, snapshotType, session, formatLine = false } = options; + const { dbProvider, fileManager, tables, snapshotType, session, formatLine = false } = options; const tab = getTabWidth(); - const qs = new QueryIterablePool(pool); + try { + await dbProvider.connect(); + for (let i = 0; i < tables.length; i++) { const table = tables[i]; @@ -70,14 +37,14 @@ export const createSnapshotFiles = async (options: GenerateSnapshotOptions): Pro // Get primary key let primaryKeys = table.primaryKeys; if (!primaryKeys || primaryKeys.length <= 0) { - primaryKeys = await getPrimaryKeys({ pool, table }); + primaryKeys = await dbProvider.getPrimaryKeys(table); } session.plan[table.name].primaryKeys = primaryKeys; // Get columns for table let tableColumns = table.columns; if (!tableColumns || tableColumns.length <= 0) { - tableColumns = await getColumnNames({ pool, table }); + tableColumns = await dbProvider.getColumnNames(table); } session.plan[table.name].columns = tableColumns; @@ -88,8 +55,14 @@ export const createSnapshotFiles = async (options: GenerateSnapshotOptions): Pro } // Generate select query - const selectColumns = tableColumns.length > 0 ? tableColumns.map((tc) => `"${tc}"`).join(', ') : '*'; - const selectQuery = `SELECT ${selectColumns} FROM ${table.name}`; + const selectColumns = + tableColumns.length > 0 ? tableColumns.map((tc) => dbProvider.escapeIdentifier(tc)).join(', ') : '*'; + + const tableIdentifier = table.schema + ? `${dbProvider.escapeIdentifier(table.schema)}.${dbProvider.escapeIdentifier(table.name)}` + : dbProvider.escapeIdentifier(table.name); + + const selectQuery = `SELECT ${selectColumns} FROM ${tableIdentifier}`; const where = table.where ? `WHERE ${table.where}` : ''; const orderBy = table.orderBy ? `ORDER BY ${table.orderBy}` : ''; const rawQuery = [selectQuery, where, orderBy].filter(Boolean).join(' '); @@ -99,19 +72,15 @@ export const createSnapshotFiles = async (options: GenerateSnapshotOptions): Pro await fs.remove(snapshotPath); await fs.ensureFile(snapshotPath); - // Get stream data from table + // Stream data from table to file logger.info(`Execute '${rawQuery}' at '${table.name}' table to create snapshot.`); - const rows = qs.query(rawQuery); - // Stream to snapshot file - logger.info(`Stream ${table.name} data to ${snapshotPath} ...`); - for await (const row of rows) { - // TODO: format json line to easy diff with JSONL - // const rowContent = formatLine ? JSON.stringify(row, null, tab) : JSON.stringify(row); + for await (const row of dbProvider.queryStream(rawQuery)) { const rowContent = JSON.stringify(row); fs.appendFileSync(snapshotPath, rowContent.concat(EOL), { encoding: 'utf-8' }); } - logger.info(`The ${table.name} data was successfully streamed .`); + + logger.info(`The ${table.name} data was successfully streamed.`); } // Save table detail @@ -119,19 +88,10 @@ export const createSnapshotFiles = async (options: GenerateSnapshotOptions): Pro await fs.outputJSON(sessionPath, session, { spaces: tab }); logger.info(`The ${sessionPath} was successfully created.`); } finally { - if (qs) { - qs.release(); - } + await dbProvider.disconnect(); } }; -/** - * Generate snapshot files - * - migrations//snapshots/original - * - migrations//snapshots/modified - * Create new migrate info file - * - migrations//session.json - */ export const generateSnapshotAsync = async (options: { fileManager: FileManager; selectedPattern: string; @@ -150,11 +110,11 @@ export const generateSnapshotAsync = async (options: { }; // Generate snapshot for original database (target apply) - const targetPool = new pg.Pool(target); - logger.info(`Generating target snapshot with db connection '${getDatabaseInfo(target)}'....`); + const targetProvider = createDatabaseProvider(target); + logger.info(`Generating target snapshot with db connection '${targetProvider.getDatabaseInfo()}'....`); await createSnapshotFiles({ fileManager, - pool: targetPool, + dbProvider: targetProvider, snapshotType: SnapshotType.original, formatLine, session, @@ -163,11 +123,11 @@ export const generateSnapshotAsync = async (options: { logger.info('The target snapshot files was successfully generated'); // Generate snapshot for modified database (source changed) - const sourcePool = new pg.Pool(source); - logger.info(`Generating source snapshot with db connection '${getDatabaseInfo(source)}'....`); + const sourceProvider = createDatabaseProvider(source); + logger.info(`Generating source snapshot with db connection '${sourceProvider.getDatabaseInfo()}'....`); await createSnapshotFiles({ fileManager, - pool: sourcePool, + dbProvider: sourceProvider, snapshotType: SnapshotType.modified, formatLine, session, diff --git a/src/commands/actions/tryConnectionAsync.ts b/src/commands/actions/tryConnectionAsync.ts index eb3bd20..efa036c 100644 --- a/src/commands/actions/tryConnectionAsync.ts +++ b/src/commands/actions/tryConnectionAsync.ts @@ -1,32 +1,17 @@ import { logger } from '../../utils/logger'; -import { getDatabaseInfo } from '../../utils/utils'; -import { Client, PoolConfig } from 'pg'; +import { DatabaseConfig } from '../../utils/database/databaseProvider'; +import { createDatabaseProvider } from '../../utils/database/databaseProviderFactory'; -/** - * Check database connection - */ -export const tryConnectionAsync = async (poolConfig: PoolConfig): Promise => { - let client: Client | undefined = undefined; +export const tryConnectionAsync = async (dbConfig: DatabaseConfig): Promise => { + const dbProvider = createDatabaseProvider(dbConfig); try { - client = new Client({ - host: poolConfig.host, - user: poolConfig.user, - password: poolConfig.password, - port: poolConfig.port, - database: poolConfig.database || 'postgres' - }); - await client.connect(); - - // Get current version - logger.info(`Connecting to the '${getDatabaseInfo(poolConfig)}' successful.`); + await dbProvider.connect(); + logger.info(`Connecting to the '${dbProvider.getDatabaseInfo()}' successful.`); return true; } catch (err) { - logger.info(`Could not connect to the '${getDatabaseInfo(poolConfig)}'.`, err); + logger.info(`Could not connect to the '${dbProvider.getDatabaseInfo()}'.`, err); return false; } finally { - if (client) { - await client.end(); - client = undefined; - } + await dbProvider.disconnect(); } }; diff --git a/src/commands/analyzeDataAsync.ts b/src/commands/analyzeDataAsync.ts index 19f9950..e937479 100644 --- a/src/commands/analyzeDataAsync.ts +++ b/src/commands/analyzeDataAsync.ts @@ -136,6 +136,11 @@ export const analyzeDataAsync = async (selectedPattern: string): Promise = showIncorrectConfigWarning(selectedPattern); return; } + // Determine dbType from pattern config (default to 'postgres') + const dbType: 'postgres' | 'mssql' = + (pattern?.source?.type as 'postgres' | 'mssql') || + (pattern?.target?.type as 'postgres' | 'mssql') || + 'postgres'; // Show output panel const config = workspace.getConfiguration(APP_ID) as ExtensionConfiguration; @@ -152,14 +157,14 @@ export const analyzeDataAsync = async (selectedPattern: string): Promise = }, async (progress) => { // Show password input if not defined - if (pattern.source.password === undefined) { + if (pattern.source.password === undefined && pattern.source.connectionString === undefined) { const inputPassword = await showInputPassword('source', pattern.source); if (typeof inputPassword === 'undefined') { return false; } pattern.source.password = inputPassword; } - if (pattern.target.password === undefined) { + if (pattern.target.password === undefined && pattern.target.connectionString === undefined) { const inputPassword = await showInputPassword('target', pattern.target); if (typeof inputPassword === 'undefined') { return false; @@ -180,7 +185,7 @@ export const analyzeDataAsync = async (selectedPattern: string): Promise = } // Store the password if connect successful - store.sourcePassword = pattern.source.password.toString(); + store.sourcePassword = pattern.source?.password?.toString(); // Check the target database connection config if (config.checkDatabaseConnection) { @@ -195,7 +200,7 @@ export const analyzeDataAsync = async (selectedPattern: string): Promise = } // Store the password if connect successful - store.targetPassword = pattern.target.password.toString(); + store.targetPassword = pattern.target?.password?.toString(); // Generate snapshot files (read database and output to file) showProgressReport(progress, 'Generating all snapshot files...'); @@ -221,7 +226,7 @@ export const analyzeDataAsync = async (selectedPattern: string): Promise = // Generate plan files (from diff files, generate to plan) showProgressReport(progress, 'Generating plan files...'); - const isGeneratePlanSuccess = await generatePlanAsync({ fileManager, tables }); + const isGeneratePlanSuccess = await generatePlanAsync({ fileManager, tables, dbType }); if (!isGeneratePlanSuccess) { showProgressWarn('Failed to generate plan files!'); return; diff --git a/src/commands/analyzeDataOnImportAsync.ts b/src/commands/analyzeDataOnImportAsync.ts index 618262f..66ec0bf 100644 --- a/src/commands/analyzeDataOnImportAsync.ts +++ b/src/commands/analyzeDataOnImportAsync.ts @@ -141,6 +141,11 @@ export const analyzeDataOnImportAsync = async (): Promise => { showIncorrectConfigWarning(selectedPattern); return; } + // Determine dbType from pattern config (default to 'postgres') + const dbType: 'postgres' | 'mssql' = + (pattern?.source?.type as 'postgres' | 'mssql') || + (pattern?.target?.type as 'postgres' | 'mssql') || + 'postgres'; // Show output panel const config = workspace.getConfiguration(APP_ID) as ExtensionConfiguration; @@ -167,7 +172,7 @@ export const analyzeDataOnImportAsync = async (): Promise => { // Generate plan files (from diff files, generate to plan) showProgressReport(progress, 'Generating plan files...'); - const isGeneratePlanSuccess = await generatePlanAsync({ fileManager, tables }); + const isGeneratePlanSuccess = await generatePlanAsync({ fileManager, tables, dbType }); if (!isGeneratePlanSuccess) { showProgressWarn('Failed to generate plan files!'); return; diff --git a/src/commands/analyzeDataOnRefreshAsync.ts b/src/commands/analyzeDataOnRefreshAsync.ts index e40b14a..6395d57 100644 --- a/src/commands/analyzeDataOnRefreshAsync.ts +++ b/src/commands/analyzeDataOnRefreshAsync.ts @@ -42,7 +42,14 @@ export const analyzeDataOnRefreshAsync = async (selectedPattern: string, migrate // Init configuration const { patterns, verbose } = configContent; + const pattern = patterns[selectedPattern]; + + const dbType: 'postgres' | 'mssql' = + (pattern?.source?.type as 'postgres' | 'mssql') || + (pattern?.target?.type as 'postgres' | 'mssql') || + 'postgres'; + const tables = pattern?.diff?.tables; if (!tables || tables.length <= 0) { showIncorrectConfigWarning(); @@ -81,7 +88,7 @@ export const analyzeDataOnRefreshAsync = async (selectedPattern: string, migrate // Generate plan files (from diff files, generate to plan) showProgressReport(progress, 'Generating plan files...'); - const isGeneratePlanSuccess = await generatePlanAsync({ fileManager, tables }); + const isGeneratePlanSuccess = await generatePlanAsync({ fileManager, tables, dbType }); if (!isGeneratePlanSuccess) { showProgressWarn('Failed to generate plan files!'); return; diff --git a/src/commands/migrateDataAsync.ts b/src/commands/migrateDataAsync.ts index 1b78526..a11e4fb 100644 --- a/src/commands/migrateDataAsync.ts +++ b/src/commands/migrateDataAsync.ts @@ -1,6 +1,9 @@ import fs from 'fs-extra'; import { EOL } from 'node:os'; import pg, { PoolConfig } from 'pg'; +import * as mssql from 'mssql'; +import * as mssqlV8 from 'mssql/msnodesqlv8'; + import { ProgressLocation, QuickPickItem, window, workspace } from 'vscode'; import { ExtensionConfiguration } from '../extension'; import { ConfigManager } from '../utils/configManager'; @@ -47,7 +50,294 @@ const handleWarningQueries = ( } }; -export const executeMigrate = async (options: { +// MSSQL helpers for IDENTITY_INSERT handling +const isMssqlIdentityInsertError = (err: unknown): boolean => { + const anyErr = err as any; + const number = anyErr?.number as number | undefined; + const message = (anyErr?.message as string | undefined)?.toLowerCase() || ''; + return number === 544 || message.includes('identity_insert is set to off'); +}; + +const extractInsertTargetTable = (sql: string): string | undefined => { + // Captures object name after INSERT INTO up to first space or '(' + // Works with [dbo].[Table], dbo.Table, [Table], Table + const m = /^\s*insert\s+into\s+([^\s(]+)\s*/i.exec(sql); + return m?.[1]; +}; + +// Normalize unquoted boolean literals to BIT (1/0) for SQL Server. +// Skips single-quoted strings, bracketed identifiers, line and block comments. +const normalizeBooleanLiteralsForMssql = (sql: string): string => { + let out = ''; + let i = 0; + const n = sql.length; + let inSingle = false; + let inBracketIdent = false; + let inLineComment = false; + let inBlockComment = false; + + const isWordChar = (c: string) => /[A-Za-z0-9_]/.test(c); + + while (i < n) { + const ch = sql[i]; + const next = i + 1 < n ? sql[i + 1] : ''; + + // End line comment + if (inLineComment) { + out += ch; + if (ch === '\n') inLineComment = false; + i++; + continue; + } + + // End block comment + if (inBlockComment) { + out += ch; + if (ch === '*' && next === '/') { + out += next; + i += 2; + inBlockComment = false; + } else { + i++; + } + continue; + } + + // Inside quoted string + if (inSingle) { + out += ch; + if (ch === "'") { + // Escaped '' stays in string + if (next === "'") { + out += next; + i += 2; + continue; + } + inSingle = false; + } + i++; + continue; + } + + // Inside bracketed identifier + if (inBracketIdent) { + out += ch; + if (ch === ']') inBracketIdent = false; + i++; + continue; + } + + // Detect starts of comments/strings/identifiers + if (ch === '-' && next === '-') { + out += ch + next; + i += 2; + inLineComment = true; + continue; + } + if (ch === '/' && next === '*') { + out += ch + next; + i += 2; + inBlockComment = true; + continue; + } + if (ch === "'") { + out += ch; + i++; + inSingle = true; + continue; + } + if (ch === '[') { + out += ch; + i++; + inBracketIdent = true; + continue; + } + + // Replace standalone true/false + if (/[A-Za-z_]/.test(ch)) { + let j = i; + while (j < n && isWordChar(sql[j])) j++; + const word = sql.slice(i, j); + const prev = i > 0 ? sql[i - 1] : ''; + const nextCh = j < n ? sql[j] : ''; + const isBoundaryLeft = !isWordChar(prev); + const isBoundaryRight = !isWordChar(nextCh); + if (isBoundaryLeft && isBoundaryRight) { + const lw = word.toLowerCase(); + if (lw === 'true') { + out += '1'; + i = j; + continue; + } + if (lw === 'false') { + out += '0'; + i = j; + continue; + } + } + // Not a boolean literal; copy as-is + out += word; + i = j; + continue; + } + + // Default: copy char + out += ch; + i++; + } + + return out; +}; + +// Add a new executeMigrateMssql function +const executeMigrateMssql = async (options: { + poolConfig: any; + migrateConfig?: MigrateConfig; + migrateUpLines: string[]; +}): Promise<{ insert: number; update: number; delete: number; error?: unknown }> => { + const { poolConfig, migrateConfig, migrateUpLines } = options; + + // Pick driver like MssqlProvider + let driver: typeof mssql | typeof mssqlV8 = mssql; + let pool: mssql.ConnectionPool | mssqlV8.ConnectionPool | undefined = undefined; + let transaction: mssql.Transaction | mssqlV8.Transaction | undefined = undefined; + + let rowAffected = { + insert: 0, + update: 0, + delete: 0 + }; + + try { + if (poolConfig.connectionString) { + const parsed = mssql.ConnectionPool.parseConnectionString(poolConfig.connectionString); + const hasUser = !!parsed.user; + const hasPassword = !!parsed.password; + + if (!hasUser || !hasPassword) { + // Switch to msnodesqlv8 (Windows Auth) + parsed.driver = 'msnodesqlv8'; + parsed.options = { + ...parsed.options, + trustServerCertificate: true, + trustedConnection: true + }; + pool = await new mssqlV8.ConnectionPool(parsed).connect(); + driver = mssqlV8; + } else { + pool = await new mssql.ConnectionPool(poolConfig.connectionString).connect(); + driver = mssql; + } + } else { + // Standard config object + pool = await new mssql.ConnectionPool({ + server: poolConfig.host, + port: poolConfig.port, + user: poolConfig.user, + password: poolConfig.password, + database: poolConfig.database, + domain: poolConfig.domain, + options: { + trustServerCertificate: poolConfig.mssqlOptions?.trustServerCertificate ?? true, + encrypt: poolConfig.mssqlOptions?.encrypt ?? true + } + }).connect(); + driver = mssql; + } + + transaction = new driver.Transaction(pool); + await transaction.begin(); + + const request = new driver.Request(transaction); + + const noAffectedQueries: { rawQuery: string; affected: number }[] = []; + const multiAffectedQueries: { rawQuery: string; affected: number }[] = []; + + for (let i = 0; i < migrateUpLines.length; i++) { + const rawQuery = migrateUpLines[i].trim(); + const isInsert = isInsertQuery(rawQuery); + const isUpdate = isUpdateQuery(rawQuery); + const isDelete = isDeleteQuery(rawQuery); + + const isBlankLine = rawQuery === ''; + const isCommentLine = rawQuery.startsWith('--'); + if (isBlankLine || isCommentLine) { + continue; + } + + const preparedQuery = normalizeBooleanLiteralsForMssql(rawQuery); + + logger.info(`Execute '${rawQuery}' to migrate...`); + + let result: any; + try { + result = await request.query(preparedQuery); + } catch (err) { + // Retry INSERT with IDENTITY_INSERT if needed + if (isInsert && isMssqlIdentityInsertError(err)) { + const target = extractInsertTargetTable(preparedQuery); + if (!target) throw err; + logger.info(`Retrying with IDENTITY_INSERT ON for ${target}...`); + await request.query(`SET IDENTITY_INSERT ${target} ON;`); + try { + result = await request.query(preparedQuery); + } finally { + // Best-effort turn OFF; let transaction rollback if this fails + try { + await request.query(`SET IDENTITY_INSERT ${target} OFF;`); + } catch { + /* noop */ + } + } + } else { + throw err; + } + } + + const rowCount = Array.isArray(result.rowsAffected) + ? result.rowsAffected[0] + : (result.rowsAffected as number); + + if (isInsert) rowAffected.insert++; + if (isUpdate) rowAffected.update++; + if (isDelete) rowAffected.delete++; + + if ((rowCount ?? 0) <= 0) { + noAffectedQueries.push({ rawQuery, affected: rowCount ?? 0 }); + } + if ((rowCount ?? 0) >= 2) { + multiAffectedQueries.push({ rawQuery, affected: rowCount ?? 0 }); + } + + logger.info(`The '${rawQuery}' was successful migrated!`); + } + + if (noAffectedQueries.length > 0) { + const message = `The query was no affected to database:`; + handleWarningQueries(noAffectedQueries, migrateConfig?.noRowAffected || 'throw', message); + } + if (multiAffectedQueries.length > 0) { + const message = `The query was multiple affected to database:`; + handleWarningQueries(multiAffectedQueries, migrateConfig?.multipleRowAffected || 'throw', message); + } + + await transaction.commit(); + return rowAffected; + } catch (error) { + if (transaction) { + logger.error(`Failed to migrate data. Starting rollback data...`, error); + await transaction.rollback(); + logger.info(`The data was successful rollback!`); + } + return { ...rowAffected, error }; + } finally { + if (pool) { + await pool.close(); + } + } +}; + +export const executeMigratePostgres = async (options: { poolConfig: PoolConfig; migrateConfig?: MigrateConfig; migrateUpLines: string[]; @@ -240,11 +530,30 @@ export const migrateDataAsync = async (migrateFilePath: string, systemInfo?: Sys // Execute migrate showProgressReport(progress, `Starting migrate data...`); logger.info(`Migrate to target with db connection '${getDatabaseInfo(pattern.target)}'....`); - const rowAffected = await executeMigrate({ - migrateUpLines, - migrateConfig: pattern.migrate, - poolConfig: pattern.target - }); + const dbType = pattern.target.type; + let rowAffected; + if (dbType === 'mssql') { + rowAffected = await executeMigrateMssql({ + migrateUpLines, + migrateConfig: pattern.migrate, + poolConfig: pattern.target + }); + } else { + rowAffected = await executeMigratePostgres({ + migrateUpLines, + migrateConfig: pattern.migrate, + poolConfig: pattern.target + }); + } + + if (rowAffected.error) { + showErrorMessageWithDetail( + `Failed to migrate data. The data will be rollback successful!`, + rowAffected.error + ); + return false; + } + if (rowAffected.error) { showErrorMessageWithDetail( `Failed to migrate data. The data will be rollback successful!`, diff --git a/src/templates/configuration.ts b/src/templates/configuration.ts index 9cdc2b6..9096993 100644 --- a/src/templates/configuration.ts +++ b/src/templates/configuration.ts @@ -13,6 +13,7 @@ export const configTemplate: DataSyncConfig = { patterns: { patternName: { source: { + type: 'postgres', database: '', host: '', port: 5432, @@ -20,6 +21,7 @@ export const configTemplate: DataSyncConfig = { password: '' }, target: { + type: 'postgres', database: '', host: '', port: 5432, diff --git a/src/utils/database/databaseProvider.ts b/src/utils/database/databaseProvider.ts new file mode 100644 index 0000000..33221c6 --- /dev/null +++ b/src/utils/database/databaseProvider.ts @@ -0,0 +1,32 @@ +import { TableConfig } from '../utils'; +import { QueryResultRow } from '../types'; +import { PoolConfig } from 'pg'; + +export interface DatabaseConfig extends PoolConfig { + type: 'postgres' | 'mssql'; + host: string; + port: number; + user: string; + password: string; + database: string; + connectionString?: string; + domain?: string; + mssqlOptions?: { + trustServerCertificate?: boolean; + encrypt?: boolean; + }; +} + +export interface DatabaseProvider { + connect(): Promise; + disconnect(): Promise; + beginTransaction(): Promise; + commitTransaction(): Promise; + rollbackTransaction(): Promise; + query(sql: string): Promise<{ rows: QueryResultRow[]; rowCount: number }>; + queryStream(sql: string): AsyncIterable; + getPrimaryKeys(table: TableConfig): Promise; + getColumnNames(table: TableConfig): Promise; + escapeIdentifier(identifier: string): string; + getDatabaseInfo(): string; +} diff --git a/src/utils/database/databaseProviderFactory.ts b/src/utils/database/databaseProviderFactory.ts new file mode 100644 index 0000000..d9a273e --- /dev/null +++ b/src/utils/database/databaseProviderFactory.ts @@ -0,0 +1,14 @@ +import { DatabaseProvider, DatabaseConfig } from './databaseProvider'; +import { PostgresProvider } from './postgresProvider'; +import { MssqlProvider } from './mssqlProvider'; + +export function createDatabaseProvider(config: DatabaseConfig): DatabaseProvider { + switch (config.type) { + case 'postgres': + return new PostgresProvider(config); + case 'mssql': + return new MssqlProvider(config); + default: + throw new Error(`Unsupported database type: ${config.type}`); + } +} diff --git a/src/utils/database/mssqlProvider.ts b/src/utils/database/mssqlProvider.ts new file mode 100644 index 0000000..4b85d52 --- /dev/null +++ b/src/utils/database/mssqlProvider.ts @@ -0,0 +1,147 @@ +import * as mssqlV8 from 'mssql/msnodesqlv8'; +import * as mssql from 'mssql'; + +import { DatabaseProvider, DatabaseConfig } from './databaseProvider'; +import { TableConfig } from '../utils'; +import { QueryResultRow } from '../types'; + +export class MssqlProvider implements DatabaseProvider { + private pool: mssql.ConnectionPool | null = null; + private transaction: mssql.Transaction | null = null; + private config: DatabaseConfig; + private driver: typeof mssql | typeof mssqlV8 = mssql; + + constructor(config: DatabaseConfig) { + this.config = config; + } + + async connect(): Promise { + if (this.config.connectionString) { + const parsed = mssql.ConnectionPool.parseConnectionString(this.config.connectionString); + const hasUser = !!parsed.user; + const hasPassword = !!parsed.password; + // Windows Auth is only supported with 'msnodesqlv8' driver + if (!hasUser || !hasPassword) { + parsed.driver = 'msnodesqlv8'; + parsed.options = { + ...parsed.options, + trustServerCertificate: true, + trustedConnection: true + }; + this.pool = await new mssqlV8.ConnectionPool(parsed).connect(); + this.driver = mssqlV8; // <-- Set driver + } else { + this.pool = await new mssql.ConnectionPool(this.config.connectionString).connect(); + this.driver = mssql; // <-- Set driver + } + } else { + this.pool = await new mssql.ConnectionPool({ + server: this.config.host, + port: this.config.port, + user: this.config.user, + password: this.config.password, + database: this.config.database, + domain: this.config.domain, + options: { + trustServerCertificate: this.config.mssqlOptions?.trustServerCertificate ?? true, + encrypt: this.config.mssqlOptions?.encrypt ?? true + } + }).connect(); + this.driver = mssql; // <-- Set driver + } + } + + async disconnect(): Promise { + if (this.transaction) { + await this.transaction.rollback(); + this.transaction = null; + } + if (this.pool) { + await this.pool.close(); + this.pool = null; + } + } + + async beginTransaction(): Promise { + if (!this.pool) throw new Error('Not connected'); + this.transaction = new this.driver.Transaction(this.pool); // + await this.transaction.begin(); + } + + async commitTransaction(): Promise { + if (!this.transaction) throw new Error('No active transaction'); + await this.transaction.commit(); + this.transaction = null; + } + + async rollbackTransaction(): Promise { + if (!this.transaction) throw new Error('No active transaction'); + await this.transaction.rollback(); + this.transaction = null; + } + + async query(sql: string): Promise<{ rows: QueryResultRow[]; rowCount: number }> { + if (!this.pool) throw new Error('Not connected'); + const request = this.transaction + ? new this.driver.Request(this.transaction) + : new this.driver.Request(this.pool); + + const result = await request.query(sql); + return { + rows: result.recordset, + rowCount: result.rowsAffected.reduce((sum, count) => sum + count, 0) + }; + } + + async *queryStream(sql: string): AsyncIterable { + if (!this.pool) throw new Error('Not connected'); + const request = this.transaction + ? new this.driver.Request(this.transaction) + : new this.driver.Request(this.pool); // + + const result = await request.query(sql); + for (const row of result.recordset) { + yield row; + } + } + + async getPrimaryKeys(table: TableConfig): Promise { + if (!this.pool) throw new Error('Not connected'); + const tableSchema = table.schema || 'dbo'; // Default schema in SQL Server + const sql = ` + SELECT column_name + FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE + WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 + AND table_name = '${table.name}' + AND table_schema = '${tableSchema}'`; + + const result = await this.query(sql); + return result.rows.map((row) => row.column_name); + } + + async getColumnNames(table: TableConfig): Promise { + if (!this.pool) throw new Error('Not connected'); + const tableSchema = table.schema || 'dbo'; // Default schema in SQL Server + const sql = ` + SELECT column_name + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = '${table.name}' + AND table_schema = '${tableSchema}' + ORDER BY ordinal_position`; + + const result = await this.query(sql); + return result.rows.map((row) => row.column_name); + } + + escapeIdentifier(identifier: string): string { + return `[${identifier}]`; + } + + getDatabaseInfo(): string { + if (this.config.connectionString) { + return `mssql://${this.config.connectionString}`; + } + const { host, port, database, user } = this.config; + return `mssql://${user}@${host}:${port}/${database}`; + } +} diff --git a/src/utils/database/postgresProvider.ts b/src/utils/database/postgresProvider.ts new file mode 100644 index 0000000..b8fcd30 --- /dev/null +++ b/src/utils/database/postgresProvider.ts @@ -0,0 +1,107 @@ +import pg from 'pg'; +import { QueryIterablePool } from 'pg-iterator'; +import { DatabaseProvider, DatabaseConfig } from './databaseProvider'; +import { TableConfig } from '../utils'; +import { QueryResultRow } from '../types'; + +export class PostgresProvider implements DatabaseProvider { + private pool: pg.Pool | null = null; + private client: pg.PoolClient | null = null; + private config: DatabaseConfig; + + constructor(config: DatabaseConfig) { + this.config = config; + } + + async connect(): Promise { + this.pool = new pg.Pool({ + host: this.config.host, + port: this.config.port, + user: this.config.user, + password: this.config.password, + database: this.config.database, + connectionString: this.config.connectionString + }); + this.client = await this.pool.connect(); + } + + async disconnect(): Promise { + if (this.client) { + this.client.release(); + this.client = null; + } + if (this.pool) { + await this.pool.end(); + this.pool = null; + } + } + + async beginTransaction(): Promise { + if (!this.client) throw new Error('Not connected'); + await this.client.query('BEGIN'); + } + + async commitTransaction(): Promise { + if (!this.client) throw new Error('Not connected'); + await this.client.query('COMMIT'); + } + + async rollbackTransaction(): Promise { + if (!this.client) throw new Error('Not connected'); + await this.client.query('ROLLBACK'); + } + + async query(sql: string): Promise<{ rows: QueryResultRow[]; rowCount: number }> { + if (!this.client) throw new Error('Not connected'); + const result = await this.client.query(sql); + return { + rows: result.rows, + rowCount: result.rowCount || 0 + }; + } + + async *queryStream(sql: string): AsyncIterable { + if (!this.pool) throw new Error('Not connected'); + const qs = new QueryIterablePool(this.pool); + try { + const rows = qs.query(sql); + for await (const row of rows) { + yield row as QueryResultRow; + } + } finally { + qs.release(); + } + } + + async getPrimaryKeys(table: TableConfig): Promise { + if (!this.pool) throw new Error('Not connected'); + const rawQuery = ` + SELECT c.column_name + FROM information_schema.table_constraints t + JOIN information_schema.constraint_column_usage c + ON c.constraint_name = t.constraint_name + WHERE t.constraint_type = 'PRIMARY KEY' AND c.table_name = '${table.name}'`; + const result = await this.pool.query(rawQuery); + return result.rows.map((row) => row.column_name); + } + + async getColumnNames(table: TableConfig): Promise { + if (!this.pool) throw new Error('Not connected'); + const rawQuery = ` + SELECT column_name + FROM information_schema.columns + WHERE table_name = '${table.name}' + ORDER BY ordinal_position`; + const result = await this.pool.query(rawQuery); + return result.rows.map((row) => row.column_name); + } + + escapeIdentifier(identifier: string): string { + return `"${identifier}"`; + } + + getDatabaseInfo(): string { + const { host, port, database, user } = this.config; + return `postgres://${user}@${host}:${port}/${database}`; + } +} diff --git a/src/utils/postgresProvider.ts b/src/utils/postgresProvider.ts new file mode 100644 index 0000000..a3f31c0 --- /dev/null +++ b/src/utils/postgresProvider.ts @@ -0,0 +1,107 @@ +import pg from 'pg'; +import { QueryIterablePool } from 'pg-iterator'; +import { DatabaseProvider, DatabaseConfig } from './database/databaseProvider'; +import { TableConfig } from './utils'; +import { QueryResultRow } from './types'; + +export class PostgresProvider implements DatabaseProvider { + private pool: pg.Pool | null = null; + private client: pg.PoolClient | null = null; + private config: DatabaseConfig; + + constructor(config: DatabaseConfig) { + this.config = config; + } + + async connect(): Promise { + this.pool = new pg.Pool({ + host: this.config.host, + port: this.config.port, + user: this.config.user, + password: this.config.password, + database: this.config.database, + connectionString: this.config.connectionString + }); + this.client = await this.pool.connect(); + } + + async disconnect(): Promise { + if (this.client) { + this.client.release(); + this.client = null; + } + if (this.pool) { + await this.pool.end(); + this.pool = null; + } + } + + async beginTransaction(): Promise { + if (!this.client) throw new Error('Not connected'); + await this.client.query('BEGIN'); + } + + async commitTransaction(): Promise { + if (!this.client) throw new Error('Not connected'); + await this.client.query('COMMIT'); + } + + async rollbackTransaction(): Promise { + if (!this.client) throw new Error('Not connected'); + await this.client.query('ROLLBACK'); + } + + async query(sql: string): Promise<{ rows: QueryResultRow[]; rowCount: number }> { + if (!this.client) throw new Error('Not connected'); + const result = await this.client.query(sql); + return { + rows: result.rows, + rowCount: result.rowCount || 0 + }; + } + + async *queryStream(sql: string): AsyncIterable { + if (!this.pool) throw new Error('Not connected'); + const qs = new QueryIterablePool(this.pool); + try { + const rows = qs.query(sql); + for await (const row of rows) { + yield row as QueryResultRow; + } + } finally { + qs.release(); + } + } + + async getPrimaryKeys(table: TableConfig): Promise { + if (!this.pool) throw new Error('Not connected'); + const rawQuery = ` + SELECT c.column_name + FROM information_schema.table_constraints t + JOIN information_schema.constraint_column_usage c + ON c.constraint_name = t.constraint_name + WHERE t.constraint_type = 'PRIMARY KEY' AND c.table_name = '${table.name}'`; + const result = await this.pool.query(rawQuery); + return result.rows.map((row) => row.column_name); + } + + async getColumnNames(table: TableConfig): Promise { + if (!this.pool) throw new Error('Not connected'); + const rawQuery = ` + SELECT column_name + FROM information_schema.columns + WHERE table_name = '${table.name}' + ORDER BY ordinal_position`; + const result = await this.pool.query(rawQuery); + return result.rows.map((row) => row.column_name); + } + + escapeIdentifier(identifier: string): string { + return `"${identifier}"`; + } + + getDatabaseInfo(): string { + const { host, port, database, user } = this.config; + return `postgres://${user}@${host}:${port}/${database}`; + } +} diff --git a/src/utils/query.ts b/src/utils/query.ts index bbf44d6..870b723 100644 --- a/src/utils/query.ts +++ b/src/utils/query.ts @@ -11,27 +11,38 @@ export const isInsertQuery = (rawQuery: string | undefined): boolean => { export const makeInsertQuery = ( table: TableConfig, tableDetail: TableDetail, - columnValues: { [key: string]: any } + values: any, + dbType: 'postgres' | 'mssql' = 'postgres' ): string => { - const isExistedCreatedAt = columnValues['created_at']; - const hasCreatedAtColumn = tableDetail.columns?.includes('created_at'); - if (!isExistedCreatedAt && hasCreatedAtColumn) { - columnValues['created_at'] = 'NOW()'; - } - - const isExistedUpdatedAt = columnValues['updated_at']; - const hasUpdatedAtColumn = tableDetail.columns?.includes('updated_at'); - if (!isExistedUpdatedAt && hasUpdatedAtColumn) { - columnValues['updated_at'] = 'NOW()'; - } + const columns = tableDetail.columns; + const columnsStr = columns.map((c) => (dbType === 'postgres' ? `"${c}"` : `[${c}]`)).join(', '); - const columns = Object.keys(columnValues).join(', '); - const values = Object.values(columnValues) - .map((value) => { - return escape(value); + // Handle different value escaping for different databases + const valuesStr = columns + .map((c) => { + const value = values[c]; + if (value === null || value === undefined) { + return 'NULL'; + } else if (typeof value === 'string') { + return `'${value.replace(/'/g, "''")}'`; + } else if (typeof value === 'object' && value instanceof Date) { + return dbType === 'postgres' ? `'${value.toISOString()}'::timestamp` : `'${value.toISOString()}'`; + } else if (typeof value === 'object') { + return dbType === 'postgres' ? `'${JSON.stringify(value)}'::jsonb` : `'${JSON.stringify(value)}'`; + } + return value; }) .join(', '); - return `INSERT INTO ${table.name} (${columns}) VALUES (${values});`; + + const tableIdentifier = table.schema + ? dbType === 'postgres' + ? `"${table.schema}"."${table.name}"` + : `[${table.schema}].[${table.name}]` + : dbType === 'postgres' + ? `"${table.name}"` + : `[${table.name}]`; + + return `INSERT INTO ${tableIdentifier} (${columnsStr}) VALUES (${valuesStr});`; }; export const isUpdateQuery = (rawQuery: string | undefined): boolean => { @@ -44,20 +55,72 @@ export const isUpdateQuery = (rawQuery: string | undefined): boolean => { export const makeUpdateQuery = ( table: TableConfig, tableDetail: TableDetail, - columnValues: { [key: string]: any } + values: any, + dbType: 'postgres' | 'mssql' = 'postgres' ): string => { - const values = Object.entries(columnValues) - .map(([column, value]) => { - if (tableDetail.primaryKeys?.includes(column)) { - return null; // Skip update field if is primary key + const columns = tableDetail.columns; + const primaryKeys = tableDetail.primaryKeys; + + if (!primaryKeys || primaryKeys.length === 0) { + throw new Error(`The table '${table.name}' does not have primary keys.`); + } + + // Escape identifiers based on database type + const escapeIdentifier = (id: string) => (dbType === 'postgres' ? `"${id}"` : `[${id}]`); + + // Create the SET clause + const setClause = columns + .filter((c) => !primaryKeys.includes(c)) + .map((c) => { + const value = values[c]; + let valueStr = 'NULL'; + + if (value !== null && value !== undefined) { + if (typeof value === 'string') { + valueStr = `'${value.replace(/'/g, "''")}'`; + } else if (typeof value === 'object' && value instanceof Date) { + valueStr = + dbType === 'postgres' ? `'${value.toISOString()}'::timestamp` : `'${value.toISOString()}'`; + } else if (typeof value === 'object') { + valueStr = + dbType === 'postgres' ? `'${JSON.stringify(value)}'::jsonb` : `'${JSON.stringify(value)}'`; + } else { + valueStr = `${value}`; + } } - return `${column}=${escape(value)}`; + + return `${escapeIdentifier(c)} = ${valueStr}`; }) - .filter(Boolean) .join(', '); - return `UPDATE ${table.name} SET ${values} WHERE ${tableDetail.primaryKeys - ?.map((p) => `${p}='${columnValues[p]}'`) - .join(' AND ')};`; + + // Create the WHERE clause for primary keys + const whereClause = primaryKeys + .map((pk) => { + const value = values[pk]; + let valueStr = 'NULL'; + + if (value === null || value === undefined) { + return `${escapeIdentifier(pk)} IS NULL`; + } else if (typeof value === 'string') { + valueStr = `'${value.replace(/'/g, "''")}'`; + } else if (typeof value === 'object' && value instanceof Date) { + valueStr = dbType === 'postgres' ? `'${value.toISOString()}'::timestamp` : `'${value.toISOString()}'`; + } else if (typeof value === 'object') { + valueStr = dbType === 'postgres' ? `'${JSON.stringify(value)}'::jsonb` : `'${JSON.stringify(value)}'`; + } else { + valueStr = `${value}`; + } + + return `${escapeIdentifier(pk)} = ${valueStr}`; + }) + .join(' AND '); + + // Generate the table identifier with schema if provided + const tableIdentifier = table.schema + ? `${escapeIdentifier(table.schema)}.${escapeIdentifier(table.name)}` + : escapeIdentifier(table.name); + + return `UPDATE ${tableIdentifier} SET ${setClause} WHERE ${whereClause};`; }; export const isDeleteQuery = (rawQuery: string | undefined): boolean => { @@ -70,9 +133,45 @@ export const isDeleteQuery = (rawQuery: string | undefined): boolean => { export const makeDeleteQuery = ( table: TableConfig, tableDetail: TableDetail, - columnValues: { [key: string]: any } + values: any, + dbType: 'postgres' | 'mssql' = 'postgres' ): string => { - return `DELETE FROM ${table.name} WHERE ${tableDetail.primaryKeys - ?.map((p) => `${p}='${columnValues[p]}'`) - .join(' AND ')};`; + const primaryKeys = tableDetail.primaryKeys; + + if (!primaryKeys || primaryKeys.length === 0) { + throw new Error(`The table '${table.name}' does not have primary keys.`); + } + + // Escape identifiers based on database type + const escapeIdentifier = (id: string) => (dbType === 'postgres' ? `"${id}"` : `[${id}]`); + + // Create the WHERE clause for primary keys + const whereClause = primaryKeys + .map((pk) => { + const value = values[pk]; + + if (value === null || value === undefined) { + return `${escapeIdentifier(pk)} IS NULL`; + } else if (typeof value === 'string') { + return `${escapeIdentifier(pk)} = '${value.replace(/'/g, "''")}'`; + } else if (typeof value === 'object' && value instanceof Date) { + return dbType === 'postgres' + ? `${escapeIdentifier(pk)} = '${value.toISOString()}'::timestamp` + : `${escapeIdentifier(pk)} = '${value.toISOString()}'`; + } else if (typeof value === 'object') { + return dbType === 'postgres' + ? `${escapeIdentifier(pk)} = '${JSON.stringify(value)}'::jsonb` + : `${escapeIdentifier(pk)} = '${JSON.stringify(value)}'`; + } else { + return `${escapeIdentifier(pk)} = ${value}`; + } + }) + .join(' AND '); + + // Generate the table identifier with schema if provided + const tableIdentifier = table.schema + ? `${escapeIdentifier(table.schema)}.${escapeIdentifier(table.name)}` + : escapeIdentifier(table.name); + + return `DELETE FROM ${tableIdentifier} WHERE ${whereClause};`; }; diff --git a/src/utils/types.ts b/src/utils/types.ts new file mode 100644 index 0000000..67001df --- /dev/null +++ b/src/utils/types.ts @@ -0,0 +1,3 @@ +export interface QueryResultRow { + [key: string]: any; +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 799d817..5dcecb3 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -5,6 +5,7 @@ import { Uri, window, workspace } from 'vscode'; import { constants } from './constants'; import { logger } from './logger'; import { SystemInfo } from './systemInfo'; +import { DatabaseConfig } from './database/databaseProvider'; export type TableDetail = { primaryKeys: string[]; @@ -52,8 +53,8 @@ export type MigrateConfig = { }; export type PatternConfig = { - source: PoolConfig; - target: PoolConfig; + source: DatabaseConfig; + target: DatabaseConfig; diff: { format?: boolean; tables: TableConfig[]; @@ -132,10 +133,13 @@ export const upperToPascal = (input: string): string => { return capitalizedWords.join(''); }; -export const getDatabaseInfo = (poolConfig: PoolConfig): string => { - const { user, host, port, database } = poolConfig; +export const getDatabaseInfo = (poolConfig: DatabaseConfig): string => { + const { user, host, port, database, type, connectionString } = poolConfig; const maskPassword = '*'.repeat(6); - return `postgres://${user}:${maskPassword}@${host}:${port}/${database}`; + if (connectionString) { + return `${type}://${connectionString}`; + } + return `{${type}}://${user}:${maskPassword}@${host}:${port}/${database}`; }; export const getTablePrimaryKey = (table: TableConfig, tableDetail: TableDetail, record: any): string => { @@ -172,7 +176,7 @@ export const getMigrationDirs = async (migrationRoot: string): Promise<{ name: s export const showInputPassword = async ( from: 'source' | 'target', - poolConfig: PoolConfig + poolConfig: DatabaseConfig ): Promise => { const passwordInput = await window.showInputBox({ title: `Please enter a password to use with ${from} database.`, diff --git a/tsconfig.json b/tsconfig.json index aabc391..4c3f722 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,15 @@ { "compilerOptions": { "module": "commonjs", - "target": "ES2020", + "target": "ES2022", "outDir": "dist", - "lib": ["es6", "WebWorker"], + "lib": ["es2022", "WebWorker"], "sourceMap": true, "rootDir": "src", "strict": true, - "esModuleInterop": true + "esModuleInterop": true, + "skipLibCheck": true }, + "exclude": ["node_modules", ".vscode-test", ".vscode-test-web"] } From dba843bcb96c640aa6526a80bc2c1ec1f065e745 Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Fri, 15 Aug 2025 23:15:49 +0200 Subject: [PATCH 02/10] fix: remove comment --- src/utils/database/mssqlProvider.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/database/mssqlProvider.ts b/src/utils/database/mssqlProvider.ts index 4b85d52..43695f7 100644 --- a/src/utils/database/mssqlProvider.ts +++ b/src/utils/database/mssqlProvider.ts @@ -29,10 +29,10 @@ export class MssqlProvider implements DatabaseProvider { trustedConnection: true }; this.pool = await new mssqlV8.ConnectionPool(parsed).connect(); - this.driver = mssqlV8; // <-- Set driver + this.driver = mssqlV8; } else { this.pool = await new mssql.ConnectionPool(this.config.connectionString).connect(); - this.driver = mssql; // <-- Set driver + this.driver = mssql; } } else { this.pool = await new mssql.ConnectionPool({ @@ -47,7 +47,7 @@ export class MssqlProvider implements DatabaseProvider { encrypt: this.config.mssqlOptions?.encrypt ?? true } }).connect(); - this.driver = mssql; // <-- Set driver + this.driver = mssql; } } From 406d5ca54ea789537b861158e2eda581e627efb8 Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Thu, 28 Aug 2025 07:57:29 +0200 Subject: [PATCH 03/10] fix: show the correct tooltip and label in ui using a connection string --- src/explorer/info-provider.ts | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/explorer/info-provider.ts b/src/explorer/info-provider.ts index 6da65c5..056047b 100644 --- a/src/explorer/info-provider.ts +++ b/src/explorer/info-provider.ts @@ -92,19 +92,44 @@ export class InfoProvider implements TreeDataProvider { db: PoolConfig, tables: TableConfig[] ): InfoTreeItem => { + const parsedConnectionString = db?.connectionString + ? Object.fromEntries( + db.connectionString + .split(';') + .filter(Boolean) + .map((pair) => { + const [key, ...rest] = pair.split('='); + return [key.trim(), rest.join('=').trim()]; + }) + ) + : {}; + + const database = db.database || parsedConnectionString.Database; + const host = db.host || parsedConnectionString.Server; + const port = db.port || parsedConnectionString.Port; + const user = + db.user || + parsedConnectionString.User || + parsedConnectionString['User Id'] || + parsedConnectionString['UserID']; return { // Built-in field - id: `${parent.id}/${from}/${db.database}`, - label: `${db.database}`, + id: `${parent.id}/${from}/${database || 'unknown'}`, + label: database || 'Unknown Database', description: `from ${from}, ${tables.length} table(s) defined`, - tooltip: [`Database: ${db.database}`, `Host: ${db.host}`, `Port: ${db.port}`, `User: ${db.user}`].join(EOL), + tooltip: [ + `Database: ${database || ''}`, + `Host: ${host || ''}`, + `Port: ${port || ''}`, + `User: ${user || ''}` + ].join(EOL), contextValue: 'database-context', iconPath: new ThemeIcon('database'), collapsibleState: tables.length > 0 ? TreeItemCollapsibleState.Collapsed : undefined, // Addition type: 'database', - databaseName: db.database, + databaseName: database, parent }; }; From dbf182e4fa96b4594188015ca7e3a2140eebbf94 Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Thu, 28 Aug 2025 07:59:10 +0200 Subject: [PATCH 04/10] fix: speed improvement turn identity insert on for the table once and remove it after all migrations for the specific table are done --- src/commands/migrateDataAsync.ts | 71 +++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/src/commands/migrateDataAsync.ts b/src/commands/migrateDataAsync.ts index a11e4fb..198475e 100644 --- a/src/commands/migrateDataAsync.ts +++ b/src/commands/migrateDataAsync.ts @@ -67,7 +67,7 @@ const extractInsertTargetTable = (sql: string): string | undefined => { // Normalize unquoted boolean literals to BIT (1/0) for SQL Server. // Skips single-quoted strings, bracketed identifiers, line and block comments. -const normalizeBooleanLiteralsForMssql = (sql: string): string => { +export const normalizeBooleanLiteralsForMssql = (sql: string): string => { let out = ''; let i = 0; const n = sql.length; @@ -197,7 +197,6 @@ const executeMigrateMssql = async (options: { }): Promise<{ insert: number; update: number; delete: number; error?: unknown }> => { const { poolConfig, migrateConfig, migrateUpLines } = options; - // Pick driver like MssqlProvider let driver: typeof mssql | typeof mssqlV8 = mssql; let pool: mssql.ConnectionPool | mssqlV8.ConnectionPool | undefined = undefined; let transaction: mssql.Transaction | mssqlV8.Transaction | undefined = undefined; @@ -215,7 +214,6 @@ const executeMigrateMssql = async (options: { const hasPassword = !!parsed.password; if (!hasUser || !hasPassword) { - // Switch to msnodesqlv8 (Windows Auth) parsed.driver = 'msnodesqlv8'; parsed.options = { ...parsed.options, @@ -229,7 +227,6 @@ const executeMigrateMssql = async (options: { driver = mssql; } } else { - // Standard config object pool = await new mssql.ConnectionPool({ server: poolConfig.host, port: poolConfig.port, @@ -253,6 +250,22 @@ const executeMigrateMssql = async (options: { const noAffectedQueries: { rawQuery: string; affected: number }[] = []; const multiAffectedQueries: { rawQuery: string; affected: number }[] = []; + // Group inserts by table for IDENTITY_INSERT optimization + const tableInsertMap = new Map(); + migrateUpLines.forEach((line, idx) => { + const trimmed = line.trim(); + if (isInsertQuery(trimmed)) { + const table = extractInsertTargetTable(trimmed); + if (table) { + if (!tableInsertMap.has(table)) tableInsertMap.set(table, []); + tableInsertMap.get(table)!.push(idx); + } + } + }); + + // Track which tables have IDENTITY_INSERT ON + const identityOnTables = new Set(); + for (let i = 0; i < migrateUpLines.length; i++) { const rawQuery = migrateUpLines[i].trim(); const isInsert = isInsertQuery(rawQuery); @@ -267,28 +280,24 @@ const executeMigrateMssql = async (options: { const preparedQuery = normalizeBooleanLiteralsForMssql(rawQuery); - logger.info(`Execute '${rawQuery}' to migrate...`); - let result: any; + let table: string | undefined = undefined; + try { + logger.info(`Execute '${rawQuery}' to migrate...`); result = await request.query(preparedQuery); } catch (err) { - // Retry INSERT with IDENTITY_INSERT if needed if (isInsert && isMssqlIdentityInsertError(err)) { - const target = extractInsertTargetTable(preparedQuery); - if (!target) throw err; - logger.info(`Retrying with IDENTITY_INSERT ON for ${target}...`); - await request.query(`SET IDENTITY_INSERT ${target} ON;`); - try { - result = await request.query(preparedQuery); - } finally { - // Best-effort turn OFF; let transaction rollback if this fails - try { - await request.query(`SET IDENTITY_INSERT ${target} OFF;`); - } catch { - /* noop */ - } + table = extractInsertTargetTable(preparedQuery); + if (!table) throw err; + + // Only enable IDENTITY_INSERT if not already ON for this table + if (!identityOnTables.has(table)) { + logger.info(`Enabling IDENTITY_INSERT ON for ${table}...`); + await request.query(`SET IDENTITY_INSERT ${table} ON;`); + identityOnTables.add(table); } + result = await request.query(preparedQuery); } else { throw err; } @@ -298,9 +307,15 @@ const executeMigrateMssql = async (options: { ? result.rowsAffected[0] : (result.rowsAffected as number); - if (isInsert) rowAffected.insert++; - if (isUpdate) rowAffected.update++; - if (isDelete) rowAffected.delete++; + if (isInsert) { + rowAffected.insert++; + } + if (isUpdate) { + rowAffected.update++; + } + if (isDelete) { + rowAffected.delete++; + } if ((rowCount ?? 0) <= 0) { noAffectedQueries.push({ rawQuery, affected: rowCount ?? 0 }); @@ -310,6 +325,16 @@ const executeMigrateMssql = async (options: { } logger.info(`The '${rawQuery}' was successful migrated!`); + + // If this is the last insert for the table, turn IDENTITY_INSERT OFF + if (isInsert && table) { + const insertIndexes = tableInsertMap.get(table); + if (insertIndexes && insertIndexes[insertIndexes.length - 1] === i && identityOnTables.has(table)) { + logger.info(`Disabling IDENTITY_INSERT ON for ${table}...`); + await request.query(`SET IDENTITY_INSERT ${table} OFF;`); + identityOnTables.delete(table); + } + } } if (noAffectedQueries.length > 0) { From 28c2a0715d5a667aad4f2518e3b3ac7f81f4d196 Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Thu, 28 Aug 2025 16:37:09 +0200 Subject: [PATCH 05/10] fix: remove duplicated code --- src/commands/migrateDataAsync.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/commands/migrateDataAsync.ts b/src/commands/migrateDataAsync.ts index 198475e..010f2cb 100644 --- a/src/commands/migrateDataAsync.ts +++ b/src/commands/migrateDataAsync.ts @@ -579,13 +579,6 @@ export const migrateDataAsync = async (migrateFilePath: string, systemInfo?: Sys return false; } - if (rowAffected.error) { - showErrorMessageWithDetail( - `Failed to migrate data. The data will be rollback successful!`, - rowAffected.error - ); - return false; - } showProgressReport( progress, `The data was successfully migrated with ${ From 94c7ea23fbb56abf43283432838d7264264857b5 Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Mon, 8 Sep 2025 21:30:25 +0200 Subject: [PATCH 06/10] fix: handle columns --- src/utils/query.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/utils/query.ts b/src/utils/query.ts index 870b723..1b9aa88 100644 --- a/src/utils/query.ts +++ b/src/utils/query.ts @@ -14,7 +14,7 @@ export const makeInsertQuery = ( values: any, dbType: 'postgres' | 'mssql' = 'postgres' ): string => { - const columns = tableDetail.columns; + const columns = tableDetail.columns ?? []; const columnsStr = columns.map((c) => (dbType === 'postgres' ? `"${c}"` : `[${c}]`)).join(', '); // Handle different value escaping for different databases @@ -61,6 +61,10 @@ export const makeUpdateQuery = ( const columns = tableDetail.columns; const primaryKeys = tableDetail.primaryKeys; + if (!Array.isArray(columns) || columns.length === 0) { + throw new Error(`The table '${table.name}' does not have columns defined.`); + } + if (!primaryKeys || primaryKeys.length === 0) { throw new Error(`The table '${table.name}' does not have primary keys.`); } From 039800a28b307837ccee040fe06f5575ebf88717 Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Mon, 8 Sep 2025 21:32:30 +0200 Subject: [PATCH 07/10] fix: escape sql --- src/utils/database/mssqlProvider.ts | 68 +++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/src/utils/database/mssqlProvider.ts b/src/utils/database/mssqlProvider.ts index 43695f7..992bf91 100644 --- a/src/utils/database/mssqlProvider.ts +++ b/src/utils/database/mssqlProvider.ts @@ -104,33 +104,63 @@ export class MssqlProvider implements DatabaseProvider { yield row; } } + private validateIdentifier(identifier: string): void { + if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) { + throw new Error(`Invalid SQL identifier: ${identifier}`); + } + } async getPrimaryKeys(table: TableConfig): Promise { - if (!this.pool) throw new Error('Not connected'); + if (!this.pool) { + throw new Error('Not connected'); + } const tableSchema = table.schema || 'dbo'; // Default schema in SQL Server + + // Validate identifiers to prevent injection + this.validateIdentifier(table.name); + this.validateIdentifier(tableSchema); + const sql = ` - SELECT column_name - FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE - WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 - AND table_name = '${table.name}' - AND table_schema = '${tableSchema}'`; - - const result = await this.query(sql); - return result.rows.map((row) => row.column_name); + SELECT column_name + FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE + WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 + AND table_name = @tableName + AND table_schema = @tableSchema`; + const request = this.transaction + ? new this.driver.Request(this.transaction) + : new this.driver.Request(this.pool); + + request.input('tableName', table.name); + request.input('tableSchema', tableSchema); + + const result = await request.query(sql); + return result.recordset.map((row) => row.column_name); } async getColumnNames(table: TableConfig): Promise { - if (!this.pool) throw new Error('Not connected'); - const tableSchema = table.schema || 'dbo'; // Default schema in SQL Server + if (!this.pool) { + throw new Error('Not connected'); + } + const tableSchema = table.schema || 'dbo'; + this.validateIdentifier(table.name); + this.validateIdentifier(tableSchema); + const sql = ` - SELECT column_name - FROM INFORMATION_SCHEMA.COLUMNS - WHERE table_name = '${table.name}' - AND table_schema = '${tableSchema}' - ORDER BY ordinal_position`; - - const result = await this.query(sql); - return result.rows.map((row) => row.column_name); + SELECT column_name + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = @tableName + AND table_schema = @tableSchema + ORDER BY ordinal_position`; + + const request = this.transaction + ? new this.driver.Request(this.transaction) + : new this.driver.Request(this.pool); + + request.input('tableName', table.name); + request.input('tableSchema', tableSchema); + + const result = await request.query(sql); + return result.recordset.map((row) => row.column_name); } escapeIdentifier(identifier: string): string { From 2f2ed490fcdebd10f2f3a3d08b5b21695ca59f9b Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Mon, 8 Sep 2025 21:40:05 +0200 Subject: [PATCH 08/10] fix: validate also postgress --- src/utils/database/mssqlProvider.ts | 14 +++++------- src/utils/database/postgresProvider.ts | 28 ++++++++++++++---------- src/utils/database/validateIdentifier.ts | 5 +++++ 3 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 src/utils/database/validateIdentifier.ts diff --git a/src/utils/database/mssqlProvider.ts b/src/utils/database/mssqlProvider.ts index 992bf91..77db183 100644 --- a/src/utils/database/mssqlProvider.ts +++ b/src/utils/database/mssqlProvider.ts @@ -4,6 +4,7 @@ import * as mssql from 'mssql'; import { DatabaseProvider, DatabaseConfig } from './databaseProvider'; import { TableConfig } from '../utils'; import { QueryResultRow } from '../types'; +import { validateIdentifier } from '../validateIdentifier'; export class MssqlProvider implements DatabaseProvider { private pool: mssql.ConnectionPool | null = null; @@ -104,11 +105,6 @@ export class MssqlProvider implements DatabaseProvider { yield row; } } - private validateIdentifier(identifier: string): void { - if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) { - throw new Error(`Invalid SQL identifier: ${identifier}`); - } - } async getPrimaryKeys(table: TableConfig): Promise { if (!this.pool) { @@ -117,8 +113,8 @@ export class MssqlProvider implements DatabaseProvider { const tableSchema = table.schema || 'dbo'; // Default schema in SQL Server // Validate identifiers to prevent injection - this.validateIdentifier(table.name); - this.validateIdentifier(tableSchema); + validateIdentifier(table.name); + validateIdentifier(tableSchema); const sql = ` SELECT column_name @@ -142,8 +138,8 @@ export class MssqlProvider implements DatabaseProvider { throw new Error('Not connected'); } const tableSchema = table.schema || 'dbo'; - this.validateIdentifier(table.name); - this.validateIdentifier(tableSchema); + validateIdentifier(table.name); + validateIdentifier(tableSchema); const sql = ` SELECT column_name diff --git a/src/utils/database/postgresProvider.ts b/src/utils/database/postgresProvider.ts index b8fcd30..b2d4452 100644 --- a/src/utils/database/postgresProvider.ts +++ b/src/utils/database/postgresProvider.ts @@ -3,6 +3,7 @@ import { QueryIterablePool } from 'pg-iterator'; import { DatabaseProvider, DatabaseConfig } from './databaseProvider'; import { TableConfig } from '../utils'; import { QueryResultRow } from '../types'; +import { validateIdentifier } from './validateIdentifier'; export class PostgresProvider implements DatabaseProvider { private pool: pg.Pool | null = null; @@ -75,24 +76,27 @@ export class PostgresProvider implements DatabaseProvider { async getPrimaryKeys(table: TableConfig): Promise { if (!this.pool) throw new Error('Not connected'); - const rawQuery = ` - SELECT c.column_name - FROM information_schema.table_constraints t + validateIdentifier(table.name); + const sql = ` + SELECT c.column_name + FROM information_schema.table_constraints t JOIN information_schema.constraint_column_usage c - ON c.constraint_name = t.constraint_name - WHERE t.constraint_type = 'PRIMARY KEY' AND c.table_name = '${table.name}'`; - const result = await this.pool.query(rawQuery); + ON c.constraint_name = t.constraint_name + WHERE t.constraint_type = 'PRIMARY KEY' + AND c.table_name = $1`; + const result = await this.pool.query(sql, [table.name]); return result.rows.map((row) => row.column_name); } async getColumnNames(table: TableConfig): Promise { if (!this.pool) throw new Error('Not connected'); - const rawQuery = ` - SELECT column_name - FROM information_schema.columns - WHERE table_name = '${table.name}' - ORDER BY ordinal_position`; - const result = await this.pool.query(rawQuery); + validateIdentifier(table.name); + const sql = ` + SELECT column_name + FROM information_schema.columns + WHERE table_name = $1 + ORDER BY ordinal_position`; + const result = await this.pool.query(sql, [table.name]); return result.rows.map((row) => row.column_name); } diff --git a/src/utils/database/validateIdentifier.ts b/src/utils/database/validateIdentifier.ts new file mode 100644 index 0000000..7b5f7c7 --- /dev/null +++ b/src/utils/database/validateIdentifier.ts @@ -0,0 +1,5 @@ +export function validateIdentifier(identifier: string): void { + if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) { + throw new Error(`Invalid SQL identifier: ${identifier}`); + } +} From 7b969ea8a742c06c97f159dd93fb521deb4721da Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Tue, 16 Sep 2025 17:57:46 +0200 Subject: [PATCH 09/10] feat: can handle multiline insert text and fix import --- src/commands/migrateDataAsync.ts | 236 +++++++++++++++++++++++++++- src/utils/database/mssqlProvider.ts | 2 +- 2 files changed, 233 insertions(+), 5 deletions(-) diff --git a/src/commands/migrateDataAsync.ts b/src/commands/migrateDataAsync.ts index 010f2cb..9952f05 100644 --- a/src/commands/migrateDataAsync.ts +++ b/src/commands/migrateDataAsync.ts @@ -65,6 +65,220 @@ const extractInsertTargetTable = (sql: string): string | undefined => { return m?.[1]; }; +// Split SQL script into executable statements, preserving newlines inside literals. +// - Uses ';' as a statement terminator (outside strings/comments) +// - For MSSQL also treats a standalone 'GO' line as a batch separator. +// - Understands: single-quoted strings ('' escape), "quoted identifiers", [bracket identifiers], +// line comments (-- ...), block comments (/* ... */), and Postgres dollar-quoted strings ($tag$ ... $tag$) +const splitSqlStatements = (input: string, dialect: 'mssql' | 'postgres'): string[] => { + const sql = input.replace(/\r\n/g, '\n'); // normalize + const out: string[] = []; + let buf: string[] = []; + + let inSingle = false; // 'string' + let inDoubleIdent = false; // "identifier" + let inBracketIdent = false; // [identifier] (MSSQL) + let inLineComment = false; // -- ... + let inBlockComment = false; // /* ... */ + let dollarTag: string | null = null; // $tag$ + + const n = sql.length; + let i = 0; + let atLineStart = true; + + const flush = () => { + const stmt = buf.join('').trim(); + if (stmt.length > 0) out.push(stmt); + buf = []; + }; + + while (i < n) { + const ch = sql[i]; + const next = i + 1 < n ? sql[i + 1] : ''; + + // Detect standalone GO (MSSQL) at start of a line when not inside any literal/comment + if ( + dialect === 'mssql' && + atLineStart && + !inSingle && + !inDoubleIdent && + !inBracketIdent && + !inLineComment && + !inBlockComment && + dollarTag === null + ) { + // Peek current line + let j = i; + while (j < n && sql[j] !== '\n') j++; + const line = sql.slice(i, j).trim(); + if (line.toUpperCase() === 'GO') { + // End of previous batch + flush(); + // Skip this GO line and its newline + i = j < n && sql[j] === '\n' ? j + 1 : j; + atLineStart = true; + continue; + } + } + + // Handle end of line for -- comments + if (inLineComment) { + buf.push(ch); + if (ch === '\n') { + inLineComment = false; + atLineStart = true; + } else if (!/\s/.test(ch)) { + atLineStart = false; + } + i++; + continue; + } + + // Handle block comment + if (inBlockComment) { + buf.push(ch); + if (ch === '*' && next === '/') { + buf.push(next); + i += 2; + inBlockComment = false; + continue; + } + if (ch === '\n') atLineStart = true; + else if (!/\s/.test(ch)) atLineStart = false; + i++; + continue; + } + + // Handle dollar-quoted string (Postgres) + if (dollarTag) { + // End tag matches exactly + if (sql.startsWith(dollarTag, i)) { + buf.push(dollarTag); + i += dollarTag.length; + dollarTag = null; + continue; + } + // Copy as-is + if (ch === '\n') atLineStart = true; + else if (!/\s/.test(ch)) atLineStart = false; + buf.push(ch); + i++; + continue; + } + + // Handle single-quoted string + if (inSingle) { + buf.push(ch); + if (ch === "'") { + // Escaped '' + if (next === "'") { + buf.push(next); + i += 2; + continue; + } + inSingle = false; + } + if (ch === '\n') atLineStart = true; + else if (!/\s/.test(ch)) atLineStart = false; + i++; + continue; + } + + // Handle quoted identifier " + if (inDoubleIdent) { + buf.push(ch); + if (ch === '"') inDoubleIdent = false; + if (ch === '\n') atLineStart = true; + else if (!/\s/.test(ch)) atLineStart = false; + i++; + continue; + } + + // Handle bracketed identifier [ + if (inBracketIdent) { + buf.push(ch); + if (ch === ']') inBracketIdent = false; + if (ch === '\n') atLineStart = true; + else if (!/\s/.test(ch)) atLineStart = false; + i++; + continue; + } + + // Outside any literal/comment: + // Start of comments + if (ch === '-' && next === '-') { + buf.push(ch, next); + i += 2; + inLineComment = true; + continue; + } + if (ch === '/' && next === '*') { + buf.push(ch, next); + i += 2; + inBlockComment = true; + continue; + } + + // Start of literals/identifiers + if (ch === "'") { + buf.push(ch); + inSingle = true; + i++; + continue; + } + if (ch === '"') { + buf.push(ch); + inDoubleIdent = true; + i++; + continue; + } + if (ch === '[') { + buf.push(ch); + inBracketIdent = true; + i++; + continue; + } + + // Postgres dollar-quote start: $tag$ + if (dialect === 'postgres' && ch === '$') { + let j = i + 1; + while (j < n && /[A-Za-z0-9_]/.test(sql[j])) j++; + if (j < n && sql[j] === '$') { + const tag = sql.slice(i, j + 1); // includes both '$' + buf.push(tag); + i = j + 1; + dollarTag = tag; + continue; + } + } + + // Statement terminator + if (ch === ';') { + // push ';' too, then flush (optional to include it) + buf.push(ch); + flush(); + i++; + atLineStart = true; + continue; + } + + // Track start-of-line + if (ch === '\n') { + atLineStart = true; + } else if (!/\s/.test(ch)) { + atLineStart = false; + } + + // Default: copy char + buf.push(ch); + i++; + } + + // Remaining buffer + flush(); + return out; +}; + // Normalize unquoted boolean literals to BIT (1/0) for SQL Server. // Skips single-quoted strings, bracketed identifiers, line and block comments. export const normalizeBooleanLiteralsForMssql = (sql: string): string => { @@ -281,20 +495,27 @@ const executeMigrateMssql = async (options: { const preparedQuery = normalizeBooleanLiteralsForMssql(rawQuery); let result: any; - let table: string | undefined = undefined; + const table: string | undefined = isInsert ? extractInsertTargetTable(preparedQuery) : undefined; try { logger.info(`Execute '${rawQuery}' to migrate...`); result = await request.query(preparedQuery); } catch (err) { if (isInsert && isMssqlIdentityInsertError(err)) { - table = extractInsertTargetTable(preparedQuery); if (!table) throw err; - // Only enable IDENTITY_INSERT if not already ON for this table if (!identityOnTables.has(table)) { logger.info(`Enabling IDENTITY_INSERT ON for ${table}...`); - await request.query(`SET IDENTITY_INSERT ${table} ON;`); + try { + await request.query(`SET IDENTITY_INSERT ${table} ON;`); + } catch (err: any) { + const msg = err?.message?.toLowerCase() || ''; + if (!msg.includes('identity_insert is already on for table')) { + throw err; + } else { + logger.warn(`IDENTITY_INSERT was already ON for ${table}.`); + } + } identityOnTables.add(table); } result = await request.query(preparedQuery); @@ -556,6 +777,13 @@ export const migrateDataAsync = async (migrateFilePath: string, systemInfo?: Sys showProgressReport(progress, `Starting migrate data...`); logger.info(`Migrate to target with db connection '${getDatabaseInfo(pattern.target)}'....`); const dbType = pattern.target.type; + // Parse statements per dialect + const migrateUpLines = splitSqlStatements(migrateUpContent, dbType === 'mssql' ? 'mssql' : 'postgres'); + if (migrateUpLines.length <= 0) { + window.showWarningMessage(`The migrate file ${migrateFilePath} does not contain any statements.`); + return false; + } + let rowAffected; if (dbType === 'mssql') { rowAffected = await executeMigrateMssql({ diff --git a/src/utils/database/mssqlProvider.ts b/src/utils/database/mssqlProvider.ts index 77db183..ab8824f 100644 --- a/src/utils/database/mssqlProvider.ts +++ b/src/utils/database/mssqlProvider.ts @@ -4,7 +4,7 @@ import * as mssql from 'mssql'; import { DatabaseProvider, DatabaseConfig } from './databaseProvider'; import { TableConfig } from '../utils'; import { QueryResultRow } from '../types'; -import { validateIdentifier } from '../validateIdentifier'; +import { validateIdentifier } from './validateIdentifier'; export class MssqlProvider implements DatabaseProvider { private pool: mssql.ConnectionPool | null = null; From e3da73da05b0e53d0a5e62b34b27445858bc621e Mon Sep 17 00:00:00 2001 From: Jacob Cofman Date: Wed, 8 Oct 2025 08:15:52 +0200 Subject: [PATCH 10/10] fix: make extension buildable --- .vscodeignore | 3 +- package-lock.json | 106 +++++++--------------------------------------- package.json | 4 +- webpack.config.js | 29 ++++++++++++- 4 files changed, 46 insertions(+), 96 deletions(-) diff --git a/.vscodeignore b/.vscodeignore index 3ca8f15..3f764b3 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -13,4 +13,5 @@ package-lock.json *.vsix dist/**/*.map tsconfig.json -webpack.config.json \ No newline at end of file +webpack.config.json +.history diff --git a/package-lock.json b/package-lock.json index f88c1e3..bd3a021 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "fs-extra": "^11.1.1", "lodash.groupby": "^4.6.0", "msnodesqlv8": "^5.1.1", + "mssql": "^12.0.0", "pg": "^8.11.1", "pg-iterator": "^0.3.0", "pg-promise": "^11.5.0" @@ -45,7 +46,6 @@ "eslint": "^8.45.0", "glob": "^10.2.6", "mocha": "^10.2.0", - "mssql": "^11.0.1", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "process": "^0.11.10", @@ -72,7 +72,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.0.tgz", "integrity": "sha512-KMVIPxG6ygcQ1M2hKHahF7eddKejYsWTjoLIfTWiqnaj42dBkYzj4+S8rK9xxmlOaEHKZHcMrRbm0NfN4kgwHw==", - "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -90,7 +89,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", - "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.6.2" @@ -103,7 +101,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.0.tgz", "integrity": "sha512-88Djs5vBvGbHQHf5ZZcaoNHo6Y8BKZkt3cw2iuJIQzLEgH4Ox6Tm4hjFhbqOxyYsgIG/eJbFEHpxRIfEEWv5Ow==", - "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -118,7 +115,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.0.tgz", "integrity": "sha512-O4aP3CLFNodg8eTHXECaH3B3CjicfzkxVtnrfLkOq0XNP7TIECGfHpK/C6vADZkWP75wzmdBnsIA8ksuJMk18g==", - "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -137,7 +133,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.0.tgz", "integrity": "sha512-qLQujmUypBBG0gxHd0j6/Jdmul6ttl24c8WGiLXIk7IHXdBlfoBqW27hyz3Xn6xbfdyVSarl1Ttbk0AwnZBYCw==", - "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -152,7 +147,6 @@ "version": "2.7.2", "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", - "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -168,7 +162,6 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", - "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.6.2" @@ -181,7 +174,6 @@ "version": "1.22.0", "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.0.tgz", "integrity": "sha512-OKHmb3/Kpm06HypvB3g6Q3zJuvyXcpxDpCS1PnU8OV6AJgSFaee/covXBcPbWc6XDDxtEPlbi3EMQ6nUiPaQtw==", - "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -200,7 +192,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.0.tgz", "integrity": "sha512-+XvmZLLWPe67WXNZo9Oc9CrPj/Tm8QnHR92fFAFdnbzwNdCH1h+7UdpaQgRSBsMY+oW1kHXNUZQLdZ1gHX3ROw==", - "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.6.2" @@ -213,7 +204,6 @@ "version": "1.13.0", "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.0.tgz", "integrity": "sha512-o0psW8QWQ58fq3i24Q1K2XfS/jYTxr7O1HRcyUE9bV9NttLU+kYOH82Ixj8DGlMTOWgxm1Sss2QAfKK5UkSPxw==", - "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -228,7 +218,6 @@ "version": "4.10.2", "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.10.2.tgz", "integrity": "sha512-Uth4vz0j+fkXCkbvutChUj03PDCokjbC6Wk9JT8hHEUtpy/EurNKAseb3+gO6Zi9VYBvwt61pgbzn1ovk942Qg==", - "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -251,7 +240,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", - "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", @@ -271,7 +259,6 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.10.0.tgz", "integrity": "sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==", - "dev": true, "license": "MIT", "dependencies": { "@azure-rest/core-client": "^2.3.3", @@ -295,7 +282,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", - "dev": true, "license": "MIT", "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", @@ -309,7 +295,6 @@ "version": "4.18.0", "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.18.0.tgz", "integrity": "sha512-esQwdtHHVkFJhcKWnysnCTchiKsy3dmNZGs8AckD9PO3t8Lp5VtY0xcrbCBC0JbttG/5w2/xukUQOsMpoUFKrg==", - "dev": true, "license": "MIT", "dependencies": { "@azure/msal-common": "15.9.0" @@ -322,7 +307,6 @@ "version": "15.9.0", "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.9.0.tgz", "integrity": "sha512-lbz/D+C9ixUG3hiZzBLjU79a0+5ZXCorjel3mwXluisKNH0/rOS/ajm8yi4yI9RP5Uc70CAcs9Ipd0051Oh/kA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" @@ -332,7 +316,6 @@ "version": "3.6.4", "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.6.4.tgz", "integrity": "sha512-jMeut9UQugcmq7aPWWlJKhJIse4DQ594zc/JaP6BIxg55XaX3aM/jcPuIQ4ryHnI4QSf03wUspy/uqAvjWKbOg==", - "dev": true, "license": "MIT", "dependencies": { "@azure/msal-common": "15.9.0", @@ -1416,7 +1399,6 @@ "version": "5.6.5", "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.6.5.tgz", "integrity": "sha512-3zwefSMwHpu8iVUW8YYz227sIv6UFqO31p1Bf1ZH/Vom7CmNyUsXjDBlnNzcuhmOL1XfxZ3nvND42kR23XlbcQ==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@manypkg/find-root": { @@ -1567,10 +1549,9 @@ } }, "node_modules/@tediousjs/connection-string": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.5.0.tgz", - "integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==", - "dev": true, + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.6.0.tgz", + "integrity": "sha512-GxlsW354Vi6QqbUgdPyQVcQjI7cZBdGV5vOYVYuCVDTylx2wl3WHR2HlhcxxHTrMigbelpXsdcZso+66uxPfow==", "license": "MIT" }, "node_modules/@tootallnate/once": { @@ -1712,7 +1693,6 @@ "version": "20.19.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -1759,7 +1739,6 @@ "version": "4.0.21", "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.21.tgz", "integrity": "sha512-19eKVv9tugr03IgfXlA9UVUVRbW6IuqRO5B92Dl4a6pT7K8uaGrNS0GkxiZD0BOk6PLuXl5FhWl//eX/pzYdTQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -1979,7 +1958,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.0.tgz", "integrity": "sha512-sOx1PKSuFwnIl7z4RN0Ls7N9AQawmR9r66eI5rFCzLDIs8HTIYrIpH9QjYWoX0lkgGrkLxXhi4QnK7MizPRrIg==", - "dev": true, "license": "MIT", "dependencies": { "http-proxy-agent": "^7.0.0", @@ -1994,7 +1972,6 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -2004,7 +1981,6 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -2018,7 +1994,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -2249,7 +2224,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" @@ -2546,7 +2520,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.1.tgz", "integrity": "sha512-yYc8UIHrd1ZTLgNBIE7JjMzUPZH+dec3q7nWkrSHEbtvkQ3h6WKC63W9K5jthcL5EXFyMuWYq+2pq5WMSIgFHw==", - "dev": true, "license": "MIT", "dependencies": { "@types/readable-stream": "^4.0.0", @@ -2559,7 +2532,6 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", @@ -2721,7 +2693,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, "funding": [ { "type": "github", @@ -2745,7 +2716,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/buffer-from": { @@ -2772,7 +2742,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "dev": true, "license": "MIT", "dependencies": { "run-applescript": "^7.0.0" @@ -3226,7 +3195,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -3307,7 +3275,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", - "dev": true, "license": "MIT", "dependencies": { "bundle-name": "^4.1.0", @@ -3324,7 +3291,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -3349,7 +3315,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3461,7 +3426,6 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -4074,7 +4038,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4084,7 +4047,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, "engines": { "node": ">=0.8.x" } @@ -5009,7 +4971,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, "license": "MIT", "bin": { "is-docker": "cli.js" @@ -5070,7 +5031,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, "license": "MIT", "dependencies": { "is-docker": "^3.0.0" @@ -5293,7 +5253,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "dev": true, "license": "MIT", "dependencies": { "is-inside-container": "^1.0.0" @@ -5386,7 +5345,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", - "dev": true, "license": "MIT" }, "node_modules/js-tokens": { @@ -5441,7 +5399,6 @@ "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "dev": true, "license": "MIT", "dependencies": { "jws": "^3.2.2", @@ -5512,7 +5469,6 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "dev": true, "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -5524,7 +5480,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dev": true, "license": "MIT", "dependencies": { "jwa": "^1.4.1", @@ -5622,42 +5577,36 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "dev": true, "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "dev": true, "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "dev": true, "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true, "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "dev": true, "license": "MIT" }, "node_modules/lodash.merge": { @@ -5670,7 +5619,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.startcase": { @@ -6238,8 +6186,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/msnodesqlv8": { "version": "5.1.1", @@ -6262,18 +6209,16 @@ } }, "node_modules/mssql": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-11.0.1.tgz", - "integrity": "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w==", - "dev": true, + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-12.0.0.tgz", + "integrity": "sha512-FcDQ1Gwe4g3Mhw25R1Onr8N+jmqBTWE/pmtcgxYnAUSIf/vBQMvJfMnyMY8ruOICtBch5+Wgbcfd3REDQSlWpA==", "license": "MIT", "dependencies": { - "@tediousjs/connection-string": "^0.5.0", + "@tediousjs/connection-string": "^0.6.0", "commander": "^11.0.0", "debug": "^4.3.3", - "rfdc": "^1.3.0", "tarn": "^3.0.2", - "tedious": "^18.2.1" + "tedious": "^19.0.0" }, "bin": { "mssql": "bin/mssql" @@ -6286,7 +6231,6 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=16" @@ -6314,7 +6258,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", "integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==", - "dev": true, "license": "MIT" }, "node_modules/natural-compare": { @@ -6462,7 +6405,6 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", - "dev": true, "license": "MIT", "dependencies": { "default-browser": "^5.2.1", @@ -7220,7 +7162,6 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, "engines": { "node": ">= 0.6.0" } @@ -7549,13 +7490,6 @@ "node": ">=0.10.0" } }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -7605,7 +7539,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -7691,8 +7624,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/schema-utils": { "version": "3.3.0", @@ -8441,17 +8373,15 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.0.0" } }, "node_modules/tedious": { - "version": "18.6.1", - "resolved": "https://registry.npmjs.org/tedious/-/tedious-18.6.1.tgz", - "integrity": "sha512-9AvErXXQTd6l7TDd5EmM+nxbOGyhnmdbp/8c3pw+tjaiSXW9usME90ET/CRG1LN1Y9tPMtz/p83z4Q97B4DDpw==", - "dev": true, + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-19.0.0.tgz", + "integrity": "sha512-nmxNBAT72mMVCIYp0Ts0Zzd5+LBQjoXlqigCrIjSo2OERSi04vr3EHq3qJxv/zgrSkg7si03SoIIfekTAadA7w==", "license": "MIT", "dependencies": { "@azure/core-auth": "^1.7.2", @@ -8466,14 +8396,13 @@ "sprintf-js": "^1.1.3" }, "engines": { - "node": ">=18" + "node": ">=18.17" } }, "node_modules/tedious/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -8486,7 +8415,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/term-size": { @@ -8711,7 +8639,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/tty-table": { @@ -9017,7 +8944,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/universalify": { @@ -9089,7 +9015,6 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, "license": "MIT", "bin": { "uuid": "dist/bin/uuid" @@ -9468,7 +9393,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", - "dev": true, "license": "MIT", "dependencies": { "is-wsl": "^3.1.0" diff --git a/package.json b/package.json index 2e176c6..618c7a4 100644 --- a/package.json +++ b/package.json @@ -308,7 +308,7 @@ "vscode:prepublish": "npm run vscode-desktop:publish && npm run vscode-web:publish", "vscode-desktop:publish": "npm run esbuild-base -- --minify", "vscode-web:publish": "npm run compile-web -- --mode production --devtool false", - "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node", + "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=dist/extension.js --external:vscode --format=cjs --platform=node --loader:.node=file", "esbuild": "npm run esbuild-base -- --sourcemap", "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch", "compile": "tsc -p ./", @@ -357,7 +357,6 @@ "eslint": "^8.45.0", "glob": "^10.2.6", "mocha": "^10.2.0", - "mssql": "^11.0.1", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "process": "^0.11.10", @@ -375,6 +374,7 @@ "fs-extra": "^11.1.1", "lodash.groupby": "^4.6.0", "msnodesqlv8": "^5.1.1", + "mssql": "^12.0.0", "pg": "^8.11.1", "pg-iterator": "^0.3.0", "pg-promise": "^11.5.0" diff --git a/webpack.config.js b/webpack.config.js index 143ec6a..ca91af9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -52,7 +52,7 @@ const webExtensionConfig = { fs: false, net: false, tls: false, - dns: false, + dns: false } }, module: { @@ -88,4 +88,29 @@ const webExtensionConfig = { devtool: 'nosources-source-map' // create a source map that points to the original source file }; -module.exports = [webExtensionConfig]; +const nodeExtensionConfig = { + ...webExtensionConfig, + target: 'node', + output: { + filename: '[name].js', + path: path.join(__dirname, './dist'), + libraryTarget: 'commonjs2' + }, + // Node build doesn't need browser fallbacks + resolve: { + mainFields: ['module', 'main'], + extensions: ['.ts', '.js'] + }, + // allow fs and other Node core modules in the node build + externalsPresets: { node: true }, + // keep externals for vscode + externals: { + vscode: 'commonjs vscode', + msnodesqlv8: 'commonjs msnodesqlv8', // do not bundle, require at runtime + mssql: 'commonjs mssql', // do not bundle, require at runtime + tedious: 'commonjs tedious' // do not bundle, require at runtime + }, + devtool: 'nosources-source-map' +}; + +module.exports = [nodeExtensionConfig];