Skip to content

Commit 6751c41

Browse files
authored
feat: Add support for Node 22 (#4808)
* Use tsup to compile ESM-only filenamify package * Update yamls to include node 22 * Throw error on crypto.createDecipher for node 22 * Add NODE_NO_WARNINGS flag to bf dialogs:merge exec * Apply feedback * Update unit tests
1 parent 89ada0e commit 6751c41

File tree

16 files changed

+95
-95
lines changed

16 files changed

+95
-95
lines changed

.github/workflows/depcheck.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121
steps:
2222
- uses: actions/checkout@v2
2323

24-
- name: use node 20.x
24+
- name: use node 22.x
2525
uses: actions/setup-node@v2-beta
2626
with:
27-
node-version: 20.x
27+
node-version: 22.x
2828

2929
- name: yarn cache dir
3030
id: yarn-cache-dir
@@ -33,7 +33,7 @@ jobs:
3333
- uses: actions/cache@v2
3434
with:
3535
path: ${{ steps.yarn-cache-dir.outputs.dir }}
36-
key: ${{ runner.os }}-node20.x-yarn-${{ hashFiles('**/yarn.lock') }}
36+
key: ${{ runner.os }}-node22.x-yarn-${{ hashFiles('**/yarn.lock') }}
3737

3838
- name: yarn
3939
run: yarn --frozen-lockfile

.github/workflows/lint.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121
steps:
2222
- uses: actions/checkout@v2
2323

24-
- name: use node 20.x
24+
- name: use node 22.x
2525
uses: actions/setup-node@v2-beta
2626
with:
27-
node-version: 20.x
27+
node-version: 22.x
2828

2929
- name: yarn cache dir
3030
id: yarn-cache-dir
@@ -33,7 +33,7 @@ jobs:
3333
- uses: actions/cache@v2
3434
with:
3535
path: ${{ steps.yarn-cache-dir.outputs.dir }}
36-
key: ${{ runner.os }}-node20.x-yarn-${{ hashFiles('**/yarn.lock') }}
36+
key: ${{ runner.os }}-node22.x-yarn-${{ hashFiles('**/yarn.lock') }}
3737

3838
- name: yarn
3939
run: yarn --frozen-lockfile

.github/workflows/test-compat.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121
steps:
2222
- uses: actions/checkout@v2
2323

24-
- name: use node 20.x
24+
- name: use node 22.x
2525
uses: actions/setup-node@v2-beta
2626
with:
27-
node-version: 20.x
27+
node-version: 22.x
2828

2929
- name: yarn cache dir
3030
id: yarn-cache-dir
@@ -33,7 +33,7 @@ jobs:
3333
- uses: actions/cache@v2
3434
with:
3535
path: ${{ steps.yarn-cache-dir.outputs.dir }}
36-
key: ${{ runner.os }}-node20.x-yarn-${{ hashFiles('**/yarn.lock') }}
36+
key: ${{ runner.os }}-node22.x-yarn-${{ hashFiles('**/yarn.lock') }}
3737

3838
- name: yarn
3939
run: yarn --frozen-lockfile

.github/workflows/test-consumer.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121
steps:
2222
- uses: actions/checkout@v2
2323

24-
- name: use node 20.x
24+
- name: use node 22.x
2525
uses: actions/setup-node@v2-beta
2626
with:
27-
node-version: 20.x
27+
node-version: 22.x
2828

2929
- name: yarn cache dir
3030
id: yarn-cache-dir
@@ -33,7 +33,7 @@ jobs:
3333
- uses: actions/cache@v2
3434
with:
3535
path: ${{ steps.yarn-cache-dir.outputs.dir }}
36-
key: ${{ runner.os }}-node20.x-yarn-${{ hashFiles('**/yarn.lock') }}
36+
key: ${{ runner.os }}-node22.x-yarn-${{ hashFiles('**/yarn.lock') }}
3737

3838
- name: yarn
3939
run: yarn --frozen-lockfile

.github/workflows/test-repoutils.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121
steps:
2222
- uses: actions/checkout@v2
2323

24-
- name: use node 20.x
24+
- name: use node 22.x
2525
uses: actions/setup-node@v2-beta
2626
with:
27-
node-version: 20.x
27+
node-version: 22.x
2828

2929
- name: yarn cache dir
3030
id: yarn-cache-dir
@@ -33,7 +33,7 @@ jobs:
3333
- uses: actions/cache@v2
3434
with:
3535
path: ${{ steps.yarn-cache-dir.outputs.dir }}
36-
key: ${{ runner.os }}-node20.x-yarn-${{ hashFiles('**/yarn.lock') }}
36+
key: ${{ runner.os }}-node22.x-yarn-${{ hashFiles('**/yarn.lock') }}
3737

3838
- name: yarn
3939
run: yarn --frozen-lockfile

.github/workflows/test-schemas.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121
steps:
2222
- uses: actions/checkout@v2
2323

24-
- name: use node 20.x
24+
- name: use node 22.x
2525
uses: actions/setup-node@v2-beta
2626
with:
27-
node-version: 20.x
27+
node-version: 22.x
2828

2929
- name: yarn cache dir
3030
id: yarn-cache-dir
@@ -33,7 +33,7 @@ jobs:
3333
- uses: actions/cache@v2
3434
with:
3535
path: ${{ steps.yarn-cache-dir.outputs.dir }}
36-
key: ${{ runner.os }}-node20.x-yarn-${{ hashFiles('**/yarn.lock') }}
36+
key: ${{ runner.os }}-node22.x-yarn-${{ hashFiles('**/yarn.lock') }}
3737

3838
- name: npm install @microsoft/botframework-cli@next --global
3939
run: npm install @microsoft/botframework-cli@next --global

.github/workflows/tests.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
strategy:
2222
matrix:
2323
os: [ubuntu, windows]
24-
node-version: [18.x, 20.x]
24+
node-version: [18.x, 20.x, 22.x]
2525

2626
steps:
2727
- uses: actions/checkout@v2
@@ -50,7 +50,7 @@ jobs:
5050
run: yarn test:github
5151

5252
- name: coveralls
53-
if: matrix.node-version == '20.x'
53+
if: matrix.node-version == '22.x'
5454
uses: coverallsapp/[email protected]
5555
with:
5656
github-token: ${{ secrets.GITHUB_TOKEN }}

build/yaml/botbuilder-js-daily.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pool:
66
vmImage: 'ubuntu-latest'
77

88
variables:
9-
NodeVersion: 20.x
9+
NodeVersion: 22.x
1010
Packaging.EnableSBOMSigning: true
1111
YARN_CACHE_FOLDER: $(Pipeline.Workspace)/.yarn
1212
# SDK_JS_org_registry_Url: define this in Azure

libraries/botbuilder-dialogs-declarative/tests/schemaMergeTest.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ async function runCommand(command, envObject) {
1414
// We need to combine our process.env with envObject so,
1515
// 1) We can use existing env vars (like from CI), and
1616
// 2) npx doesn't like to install without the existing APPDATA windows env var.
17-
const env = { ...process.env, ...envObject };
17+
const env = { ...process.env, ...envObject, NODE_NO_WARNINGS: 1 };
1818
const { stdout, stderr } = await exec(command, { env });
1919

2020
if (stderr) {

libraries/botbuilder/package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
"filenamify": "^6.0.0",
4141
"fs-extra": "^11.2.0",
4242
"htmlparser2": "^9.0.1",
43-
"import-sync": "^2.2.2",
4443
"uuid": "^10.0.0",
4544
"zod": "^3.23.8"
4645
},
@@ -54,9 +53,10 @@
5453
"build": "tsc -b",
5554
"build-docs": "typedoc --theme markdown --entryPoint botbuilder --excludePrivate --includeDeclarations --ignoreCompilerErrors --module amd --out ..\\..\\doc\\botbuilder .\\lib\\index.d.ts ..\\botbuilder-core\\lib\\index.d.ts ..\\botframework-schema\\lib\\index.d.ts --hideGenerator --name \"Bot Builder SDK\" --readme none",
5655
"build:rollup": "yarn clean && yarn build && api-extractor run --verbose --local",
57-
"clean": "rimraf _ts3.4 lib tsconfig.tsbuildinfo",
56+
"clean": "rimraf _ts3.4 lib vendors tsconfig.tsbuildinfo",
5857
"depcheck": "depcheck --config ../../.depcheckrc",
5958
"lint": "eslint .",
59+
"prebuild": "tsup ./node_modules/filenamify/*.js --format cjs --dts --out-dir vendors/filenamify --clean --sourcemap",
6060
"postbuild": "downlevel-dts lib _ts3.4/lib --checksum",
6161
"test": "npm-run-all build test:mocha",
6262
"test:compat": "api-extractor run --verbose",
@@ -70,6 +70,7 @@
7070
"files": [
7171
"_ts3.4",
7272
"lib",
73-
"src"
73+
"src",
74+
"vendors"
7475
]
75-
}
76+
}

libraries/botbuilder/src/fileTranscriptStore.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
import { join, parse } from 'path';
99
import { mkdirp, pathExists, readdir, readFile, remove, writeFile } from 'fs-extra';
1010
import { Activity, PagedResult, TranscriptInfo, TranscriptStore } from 'botbuilder-core';
11-
import importSync from 'import-sync';
12-
13-
const filenamify = importSync('filenamify').default;
11+
import filenamify from '../vendors/filenamify/index';
1412

1513
/**
1614
* @private

libraries/botframework-config/src/botConfiguration.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,10 @@ export class BotConfiguration extends BotConfigurationBase {
304304
}
305305
}
306306
}
307-
} catch {
307+
} catch (legacyErr) {
308+
if (legacyErr.message.includes('Node.js versions')) {
309+
throw legacyErr;
310+
}
308311
throw err;
309312
}
310313
}

libraries/botframework-config/src/encrypt.ts

+22-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import crypto from 'crypto';
9+
import { version as nodeVersion } from 'process';
910

1011
/**
1112
* @private
@@ -100,10 +101,26 @@ export function decryptString(encryptedValue: string, secret: string): string {
100101
* @param secret
101102
*/
102103
export function legacyDecrypt(encryptedValue: string, secret: string): string {
103-
// LEGACY for pre standardized SHA256 encryption, this uses some undocumented nodejs MD5 hash internally and is deprecated
104-
const decipher: crypto.Decipher = crypto.createDecipher('aes192', secret);
105-
let value: string = decipher.update(encryptedValue, 'hex', 'utf8');
106-
value += decipher.final('utf8');
104+
const UNSUPPORTED_VERSION = 'v22.0.0';
105+
if (!isNodeCompatible(nodeVersion, UNSUPPORTED_VERSION)) {
106+
throw new Error(`This method is not available for Node.js versions over ${UNSUPPORTED_VERSION}.`);
107+
} else {
108+
// LEGACY for pre standardized SHA256 encryption, this uses some undocumented nodejs MD5 hash internally and is deprecated
109+
const decipher: crypto.Decipher = crypto.createDecipher('aes192', secret);
110+
let value: string = decipher.update(encryptedValue, 'hex', 'utf8');
111+
value += decipher.final('utf8');
112+
113+
return value;
114+
}
115+
}
107116

108-
return value;
117+
/**
118+
* private
119+
*
120+
* @param currentVersion The current version of Node.js.
121+
* @param minVersion The minimum unsupported version.
122+
* @returns true if the current version of Node is lower than the unsupported version.
123+
*/
124+
function isNodeCompatible(currentVersion: string, minVersion: string): boolean {
125+
return minVersion.localeCompare(currentVersion) > 0;
109126
}

libraries/botframework-config/tests/loadAndSave.test.js

+33-18
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const testBotPath = require.resolve('./test.bot');
88
const govTestBotPath = require.resolve('./govTest.bot');
99
const legacyBotPath = require.resolve('./legacy.bot');
1010
const saveBotPath = testBotPath.replace('test.bot', 'save.bot');
11+
const UNSUPPORTED_VERSION = 'v22.0.0';
1112

1213
describe('LoadAndSaveTests', function () {
1314
it('DeserializeBotFile', async function () {
@@ -89,12 +90,19 @@ describe('LoadAndSaveTests', function () {
8990
const config = await bf.BotConfiguration.load(testBotPath);
9091
await config.saveAs(saveBotPath, secret);
9192

92-
await assert.rejects(
93-
bf.BotConfiguration.load(saveBotPath),
94-
new Error(
95-
'You are attempting to perform an operation which needs access to the secret and --secret is missing'
96-
)
97-
);
93+
if (UNSUPPORTED_VERSION.localeCompare(process.version) < 1) {
94+
await assert.rejects(
95+
bf.BotConfiguration.load(saveBotPath),
96+
new Error(`This method is not available for Node.js versions over ${UNSUPPORTED_VERSION}.`),
97+
);
98+
} else {
99+
await assert.rejects(
100+
bf.BotConfiguration.load(saveBotPath),
101+
new Error(
102+
'You are attempting to perform an operation which needs access to the secret and --secret is missing'
103+
),
104+
);
105+
}
98106
});
99107

100108
it('LoadAndVerifyChannelServiceSync', async function () {
@@ -354,17 +362,24 @@ describe('LoadAndSaveTests', function () {
354362
});
355363

356364
it('LegacyEncryption', async function () {
357-
let config = await bf.BotConfiguration.load(legacyBotPath, 'password');
358-
assert.equal(config.services[0].appPassword, 'xyzpdq', 'value should be unencrypted');
359-
assert.ok(config.padlock != null, 'padlock should exist');
360-
assert.ok(!config.secretKey, 'secretKey should not exist');
361-
362-
const secret = bf.BotConfiguration.generateKey();
363-
await config.saveAs(saveBotPath, secret);
364-
config = await bf.BotConfiguration.load(saveBotPath, secret);
365-
fs.unlinkSync(saveBotPath);
366-
assert.ok(config.padlock != null, 'padlock should exist');
367-
assert.ok(config.padlock.length > 0, 'padlock should not be empty');
368-
assert.ok(!config.secretKey, 'secretKey should not exist');
365+
if (UNSUPPORTED_VERSION.localeCompare(process.version) < 1) {
366+
await assert.rejects(
367+
bf.BotConfiguration.load(legacyBotPath, 'password'),
368+
new Error(`This method is not available for Node.js versions over ${UNSUPPORTED_VERSION}.`),
369+
);
370+
} else {
371+
let config = await bf.BotConfiguration.load(legacyBotPath, 'password');
372+
assert.equal(config.services[0].appPassword, 'xyzpdq', 'value should be unencrypted');
373+
assert.ok(config.padlock != null, 'padlock should exist');
374+
assert.ok(!config.secretKey, 'secretKey should not exist');
375+
376+
const secret = bf.BotConfiguration.generateKey();
377+
await config.saveAs(saveBotPath, secret);
378+
config = await bf.BotConfiguration.load(saveBotPath, secret);
379+
fs.unlinkSync(saveBotPath);
380+
assert.ok(config.padlock != null, 'padlock should exist');
381+
assert.ok(config.padlock.length > 0, 'padlock should not be empty');
382+
assert.ok(!config.secretKey, 'secretKey should not exist');
383+
}
369384
});
370385
});

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
"generators/generator-botbuilder/generators/app/templates/*"
1515
],
1616
"nohoist": [
17-
"**/@types/selenium-webdriver"
17+
"**/@types/selenium-webdriver",
18+
"botbuilder/filenamify"
1819
],
1920
"nohoistComments": {
20-
"**/@types/selenium-webdriver": "This package is excluded from the root @types folder as it requires ES2015+, whereas some BotBuilder libraries support ES5+."
21+
"**/@types/selenium-webdriver": "This package is excluded from the root @types folder as it requires ES2015+, whereas some BotBuilder libraries support ES5+.",
22+
"botbuilder/filenamify": "This package is excluded because it's compiled as CJS by tsup as it's ESM-only."
2123
}
2224
},
2325
"scripts": {

0 commit comments

Comments
 (0)