botbuilder-azure-blobs
Advanced tools
Comparing version 4.12.0-dev-preview.20201204.119d40c4f11e to 4.12.0-dev-preview.20201207.e073a6e77c25
@@ -19,3 +19,3 @@ import { StoragePipelineOptions } from '@azure/storage-blob'; | ||
private readonly _concurrency; | ||
private _initializePromise; | ||
private _initializePromise?; | ||
/** | ||
@@ -22,0 +22,0 @@ * Constructs a BlobsStorage instance. |
@@ -24,3 +24,3 @@ import { Activity, PagedResult, TranscriptInfo, TranscriptStore } from 'botbuilder-core'; | ||
private readonly _concurrency; | ||
private _initializePromise; | ||
private _initializePromise?; | ||
/** | ||
@@ -27,0 +27,0 @@ * Constructs a BlobsStorage instance. |
@@ -19,3 +19,3 @@ import { StoragePipelineOptions } from '@azure/storage-blob'; | ||
private readonly _concurrency; | ||
private _initializePromise; | ||
private _initializePromise?; | ||
/** | ||
@@ -22,0 +22,0 @@ * Constructs a BlobsStorage instance. |
@@ -32,3 +32,4 @@ "use strict"; | ||
const storage_blob_1 = require("@azure/storage-blob"); | ||
const assert_1 = require("./assert"); | ||
const botbuilder_core_1 = require("botbuilder-core"); | ||
const botbuilder_stdlib_1 = require("botbuilder-stdlib"); | ||
const ignoreError_1 = require("./ignoreError"); | ||
@@ -49,6 +50,4 @@ const sanitizeBlobKey_1 = require("./sanitizeBlobKey"); | ||
this._concurrency = Infinity; | ||
assert_1.assert(typeof connectionString === 'string', '`connectionString` must be a string'); | ||
assert_1.assert(connectionString, '`connectionString` must be non-empty', Error); | ||
assert_1.assert(typeof containerName === 'string', '`containerName` must be a string'); | ||
assert_1.assert(containerName, '`containerName` must be non-empty', Error); | ||
botbuilder_stdlib_1.assert.string(connectionString, ['connectionString']); | ||
botbuilder_stdlib_1.assert.string(containerName, ['containerName']); | ||
this._containerClient = new storage_blob_1.ContainerClient(connectionString, containerName, options === null || options === void 0 ? void 0 : options.storagePipelineOptions); | ||
@@ -74,13 +73,18 @@ // At most one promise at a time to be friendly to local emulator users | ||
return __awaiter(this, void 0, void 0, function* () { | ||
assert_1.assert(Array.isArray(keys), '`keys` must be an array'); | ||
botbuilder_stdlib_1.assert.arrayOfString(keys, ['keys']); | ||
yield this._initialize(); | ||
return (yield p_map_1.default(keys, (key) => __awaiter(this, void 0, void 0, function* () { | ||
const result = { key, value: undefined }; | ||
const blob = yield ignoreError_1.ignoreError(this._containerClient.getBlobClient(sanitizeBlobKey_1.sanitizeBlobKey(key)).download(), ignoreError_1.isStatusCodeError(404)); | ||
if (!blob) { | ||
return { key, value: null }; | ||
return result; | ||
} | ||
const { etag: eTag, readableStreamBody: stream } = blob; | ||
if (!stream) { | ||
return result; | ||
} | ||
const contents = yield get_stream_1.default(stream); | ||
const parsed = JSON.parse(contents); | ||
return { key, value: Object.assign(Object.assign({}, parsed), { eTag }) }; | ||
result.value = Object.assign(Object.assign({}, parsed), { eTag }); | ||
return result; | ||
}), { | ||
@@ -99,4 +103,3 @@ concurrency: this._concurrency, | ||
return __awaiter(this, void 0, void 0, function* () { | ||
assert_1.assert(changes, '`changes` must not be null or undefined'); | ||
assert_1.assert(typeof changes === 'object', '`changes` must be an object'); | ||
botbuilder_core_1.assertStoreItems(changes, ['changes']); | ||
yield this._initialize(); | ||
@@ -123,3 +126,3 @@ yield p_map_1.default(Object.entries(changes), (_a) => { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
assert_1.assert(Array.isArray(keys), '`keys` must be an array'); | ||
botbuilder_stdlib_1.assert.arrayOfString(keys, ['keys']); | ||
yield this._initialize(); | ||
@@ -126,0 +129,0 @@ yield p_map_1.default(keys, (key) => ignoreError_1.ignoreError(this._containerClient.deleteBlob(sanitizeBlobKey_1.sanitizeBlobKey(key)), ignoreError_1.isStatusCodeError(404)), { |
@@ -24,3 +24,3 @@ import { Activity, PagedResult, TranscriptInfo, TranscriptStore } from 'botbuilder-core'; | ||
private readonly _concurrency; | ||
private _initializePromise; | ||
private _initializePromise?; | ||
/** | ||
@@ -27,0 +27,0 @@ * Constructs a BlobsStorage instance. |
@@ -20,4 +20,5 @@ "use strict"; | ||
const p_map_1 = __importDefault(require("p-map")); | ||
const assert_1 = require("./assert"); | ||
const maybeCast_1 = require("./maybeCast"); | ||
const botbuilder_core_1 = require("botbuilder-core"); | ||
const botbuilder_stdlib_1 = require("botbuilder-stdlib"); | ||
const maybeCast_1 = require("botbuilder-stdlib/lib/maybeCast"); | ||
const sanitizeBlobKey_1 = require("./sanitizeBlobKey"); | ||
@@ -42,2 +43,3 @@ const storage_blob_1 = require("@azure/storage-blob"); | ||
function getBlobKey(activity) { | ||
botbuilder_stdlib_1.assert.date(activity.timestamp, ['activity', 'timestamp']); | ||
return sanitizeBlobKey_1.sanitizeBlobKey([activity.channelId, activity.conversation.id, `${formatTicks(activity.timestamp)}-${activity.id}.json`].join('/')); | ||
@@ -65,6 +67,4 @@ } | ||
this._concurrency = Infinity; | ||
assert_1.assert(typeof connectionString === 'string', '`connectionString` must be a string'); | ||
assert_1.assert(connectionString, '`connectionString` must be non-empty'); | ||
assert_1.assert(typeof containerName === 'string', '`containerName` must be a string'); | ||
assert_1.assert(containerName, '`containerName` must be non-empty'); | ||
botbuilder_stdlib_1.assert.string(connectionString, ['connectionString']); | ||
botbuilder_stdlib_1.assert.string(containerName, ['containerName']); | ||
this._containerClient = new storage_blob_1.ContainerClient(connectionString, containerName, options === null || options === void 0 ? void 0 : options.storagePipelineOptions); | ||
@@ -93,8 +93,6 @@ // At most one promise at a time to be friendly to local emulator users | ||
getTranscriptActivities(channelId, conversationId, continuationToken, startDate) { | ||
var _a, _b, _c; | ||
var _a, _b, _c, _d; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
assert_1.assert(typeof channelId === 'string', '`channelId` must be a string'); | ||
assert_1.assert(channelId, '`channelId` must be non-empty'); | ||
assert_1.assert(typeof conversationId === 'string', '`conversationId` must be a string'); | ||
assert_1.assert(conversationId, '`conversationId` must be non-empty'); | ||
botbuilder_stdlib_1.assert.string(channelId, ['channelId']); | ||
botbuilder_stdlib_1.assert.string(conversationId, ['conversationId']); | ||
yield this._initialize(); | ||
@@ -114,14 +112,18 @@ const iter = this._containerClient | ||
const fromIdx = startDate != null | ||
? blobItems.findIndex((blobItem) => new Date(blobItem.metadata.timestamp) >= startDate) | ||
? blobItems.findIndex((blobItem) => { var _a; return ((_a = blobItem === null || blobItem === void 0 ? void 0 : blobItem.metadata) === null || _a === void 0 ? void 0 : _a.timestamp) && new Date(blobItem.metadata.timestamp) >= startDate; }) | ||
: 0; | ||
if (fromIdx !== -1) { | ||
const activities = yield p_map_1.default(blobItems.slice(fromIdx), (blobItem) => __awaiter(this, void 0, void 0, function* () { | ||
const blob = yield this._containerClient.getBlobClient(blobItem.name).download(); | ||
const { readableStreamBody: stream } = blob; | ||
if (!stream) { | ||
return null; | ||
} | ||
const contents = yield get_stream_1.default(stream); | ||
const activity = JSON.parse(contents); | ||
return Object.assign(Object.assign({}, activity), { timestamp: new Date(activity.timestamp) }); | ||
}), { concurrency: this._concurrency }); | ||
return { | ||
continuationToken: response === null || response === void 0 ? void 0 : response.continuationToken, | ||
items: yield p_map_1.default(blobItems.slice(fromIdx), (blobItem) => __awaiter(this, void 0, void 0, function* () { | ||
const blob = yield this._containerClient.getBlobClient(blobItem.name).download(); | ||
const { readableStreamBody: stream } = blob; | ||
const contents = yield get_stream_1.default(stream); | ||
const activity = JSON.parse(contents); | ||
return Object.assign(Object.assign({}, activity), { timestamp: new Date(activity.timestamp) }); | ||
}), { concurrency: this._concurrency }), | ||
continuationToken: (_d = response === null || response === void 0 ? void 0 : response.continuationToken) !== null && _d !== void 0 ? _d : '', | ||
items: activities.reduce((acc, activity) => (activity ? acc.concat(activity) : acc), []), | ||
}; | ||
@@ -143,6 +145,5 @@ } | ||
listTranscripts(channelId, continuationToken) { | ||
var _a, _b, _c; | ||
var _a, _b, _c, _d; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
assert_1.assert(typeof channelId === 'string', '`channelId` must be a string'); | ||
assert_1.assert(channelId, '`channelId` must be non-empty'); | ||
botbuilder_stdlib_1.assert.string(channelId, ['channelId']); | ||
yield this._initialize(); | ||
@@ -159,10 +160,8 @@ const page = yield this._containerClient | ||
return { | ||
continuationToken: response === null || response === void 0 ? void 0 : response.continuationToken, | ||
continuationToken: (_d = response === null || response === void 0 ? void 0 : response.continuationToken) !== null && _d !== void 0 ? _d : '', | ||
items: blobItems.map((blobItem) => { | ||
const [, conversationId] = decodeURIComponent(blobItem.name).split('/'); | ||
return { | ||
channelId, | ||
id: conversationId, | ||
created: new Date(blobItem.metadata.timestamp), | ||
}; | ||
var _a; | ||
const [, id] = decodeURIComponent(blobItem.name).split('/'); | ||
const created = ((_a = blobItem.metadata) === null || _a === void 0 ? void 0 : _a.timestamp) ? new Date(blobItem.metadata.timestamp) : new Date(); | ||
return { channelId, created, id }; | ||
}), | ||
@@ -182,6 +181,4 @@ }; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
assert_1.assert(typeof channelId === 'string', '`channelId` must be a string'); | ||
assert_1.assert(channelId, '`channelId` must be non-empty'); | ||
assert_1.assert(typeof conversationId === 'string', '`conversationId` must be a string'); | ||
assert_1.assert(conversationId, '`conversationId` must be non-empty'); | ||
botbuilder_stdlib_1.assert.string(channelId, ['channelId']); | ||
botbuilder_stdlib_1.assert.string(conversationId, ['conversationId']); | ||
yield this._initialize(); | ||
@@ -215,15 +212,17 @@ const iter = this._containerClient | ||
return __awaiter(this, void 0, void 0, function* () { | ||
assert_1.assert(activity, '`activity` must not be null or undefined'); | ||
assert_1.assert(typeof activity === 'object', '`activity` must be an object'); | ||
botbuilder_core_1.assertActivity(activity, ['activity']); | ||
yield this._initialize(); | ||
const blob = this._containerClient.getBlockBlobClient(getBlobKey(activity)); | ||
const serialized = JSON.stringify(activity); | ||
yield blob.upload(serialized, serialized.length, { | ||
metadata: { | ||
Id: activity.id, | ||
FromId: activity.from.id, | ||
RecipientId: activity.recipient.id, | ||
Timestamp: activity.timestamp.toJSON(), | ||
}, | ||
}); | ||
const metadata = { | ||
FromId: activity.from.id, | ||
RecipientId: activity.recipient.id, | ||
}; | ||
if (activity.id) { | ||
metadata.Id = activity.id; | ||
} | ||
if (activity.timestamp) { | ||
metadata.Timestamp = activity.timestamp.toJSON(); | ||
} | ||
yield blob.upload(serialized, serialized.length, { metadata }); | ||
}); | ||
@@ -230,0 +229,0 @@ } |
@@ -49,3 +49,3 @@ "use strict"; | ||
return function (err) { | ||
return err instanceof storage_blob_1.RestError && ignoredCodes.has(err.statusCode); | ||
return err instanceof storage_blob_1.RestError && err.statusCode != null && ignoredCodes.has(err.statusCode); | ||
}; | ||
@@ -52,0 +52,0 @@ } |
@@ -5,3 +5,3 @@ { | ||
"description": "BotBuilder storage bindings for Azure Blobs services", | ||
"version": "4.12.0-dev-preview.20201204.119d40c4f11e", | ||
"version": "4.12.0-dev-preview.20201207.e073a6e77c25", | ||
"preview": true, | ||
@@ -33,3 +33,4 @@ "license": "MIT", | ||
"@azure/storage-blob": "^12.2.1", | ||
"botbuilder-core": "4.12.0-dev-20201204.119d40c4f11e", | ||
"botbuilder-core": "4.12.0-dev-20201207.e073a6e77c25", | ||
"botbuilder-stdlib": "4.12.0-dev-20201207.e073a6e77c25", | ||
"get-stream": "^6.0.0", | ||
@@ -39,4 +40,3 @@ "p-map": "^4.0.0" | ||
"devDependencies": { | ||
"sinon": "^9.2.0", | ||
"ts-essentials": "^7.0.1" | ||
"sinon": "^9.2.0" | ||
}, | ||
@@ -43,0 +43,0 @@ "scripts": { |
@@ -7,4 +7,4 @@ // Copyright (c) Microsoft Corporation. | ||
import { ContainerClient, StoragePipelineOptions } from '@azure/storage-blob'; | ||
import { Storage, StoreItems } from 'botbuilder-core'; | ||
import { assert } from './assert'; | ||
import { Storage, StoreItems, assertStoreItems } from 'botbuilder-core'; | ||
import { assert } from 'botbuilder-stdlib'; | ||
import { ignoreError, isStatusCodeError } from './ignoreError'; | ||
@@ -30,3 +30,3 @@ import { sanitizeBlobKey } from './sanitizeBlobKey'; | ||
private readonly _concurrency = Infinity; | ||
private _initializePromise: Promise<unknown>; | ||
private _initializePromise?: Promise<unknown>; | ||
@@ -41,8 +41,5 @@ /** | ||
constructor(connectionString: string, containerName: string, options?: BlobsStorageOptions) { | ||
assert(typeof connectionString === 'string', '`connectionString` must be a string'); | ||
assert(connectionString, '`connectionString` must be non-empty', Error); | ||
assert.string(connectionString, ['connectionString']); | ||
assert.string(containerName, ['containerName']); | ||
assert(typeof containerName === 'string', '`containerName` must be a string'); | ||
assert(containerName, '`containerName` must be non-empty', Error); | ||
this._containerClient = new ContainerClient(connectionString, containerName, options?.storagePipelineOptions); | ||
@@ -70,3 +67,3 @@ | ||
async read(keys: string[]): Promise<StoreItems> { | ||
assert(Array.isArray(keys), '`keys` must be an array'); | ||
assert.arrayOfString(keys, ['keys']); | ||
@@ -79,2 +76,4 @@ await this._initialize(); | ||
async (key) => { | ||
const result = { key, value: undefined }; | ||
const blob = await ignoreError( | ||
@@ -86,11 +85,15 @@ this._containerClient.getBlobClient(sanitizeBlobKey(key)).download(), | ||
if (!blob) { | ||
return { key, value: null }; | ||
return result; | ||
} | ||
const { etag: eTag, readableStreamBody: stream } = blob; | ||
if (!stream) { | ||
return result; | ||
} | ||
const contents = await getStream(stream); | ||
const parsed = JSON.parse(contents); | ||
result.value = { ...parsed, eTag }; | ||
return { key, value: { ...parsed, eTag } }; | ||
return result; | ||
}, | ||
@@ -111,4 +114,3 @@ { | ||
async write(changes: StoreItems): Promise<void> { | ||
assert(changes, '`changes` must not be null or undefined'); | ||
assert(typeof changes === 'object', '`changes` must be an object'); | ||
assertStoreItems(changes, ['changes']); | ||
@@ -140,3 +142,3 @@ await this._initialize(); | ||
async delete(keys: string[]): Promise<void> { | ||
assert(Array.isArray(keys), '`keys` must be an array'); | ||
assert.arrayOfString(keys, ['keys']); | ||
@@ -143,0 +145,0 @@ await this._initialize(); |
@@ -6,5 +6,5 @@ // Copyright (c) Microsoft Corporation. | ||
import pmap from 'p-map'; | ||
import { Activity, PagedResult, TranscriptInfo, TranscriptStore } from 'botbuilder-core'; | ||
import { assert } from './assert'; | ||
import { maybeCast } from './maybeCast'; | ||
import { Activity, PagedResult, TranscriptInfo, TranscriptStore, assertActivity } from 'botbuilder-core'; | ||
import { assert } from 'botbuilder-stdlib'; | ||
import { maybeCast } from 'botbuilder-stdlib/lib/maybeCast'; | ||
import { sanitizeBlobKey } from './sanitizeBlobKey'; | ||
@@ -38,2 +38,4 @@ | ||
function getBlobKey(activity: Activity): string { | ||
assert.date(activity.timestamp, ['activity', 'timestamp']); | ||
return sanitizeBlobKey( | ||
@@ -71,3 +73,3 @@ [activity.channelId, activity.conversation.id, `${formatTicks(activity.timestamp)}-${activity.id}.json`].join( | ||
private readonly _concurrency = Infinity; | ||
private _initializePromise: Promise<unknown>; | ||
private _initializePromise?: Promise<unknown>; | ||
@@ -82,8 +84,5 @@ /** | ||
constructor(connectionString: string, containerName: string, options?: BlobsTranscriptStoreOptions) { | ||
assert(typeof connectionString === 'string', '`connectionString` must be a string'); | ||
assert(connectionString, '`connectionString` must be non-empty'); | ||
assert.string(connectionString, ['connectionString']); | ||
assert.string(containerName, ['containerName']); | ||
assert(typeof containerName === 'string', '`containerName` must be a string'); | ||
assert(containerName, '`containerName` must be non-empty'); | ||
this._containerClient = new ContainerClient(connectionString, containerName, options?.storagePipelineOptions); | ||
@@ -120,8 +119,5 @@ | ||
): Promise<PagedResult<Activity>> { | ||
assert(typeof channelId === 'string', '`channelId` must be a string'); | ||
assert(channelId, '`channelId` must be non-empty'); | ||
assert.string(channelId, ['channelId']); | ||
assert.string(conversationId, ['conversationId']); | ||
assert(typeof conversationId === 'string', '`conversationId` must be a string'); | ||
assert(conversationId, '`conversationId` must be non-empty'); | ||
await this._initialize(); | ||
@@ -145,19 +141,32 @@ | ||
startDate != null | ||
? blobItems.findIndex((blobItem) => new Date(blobItem.metadata.timestamp) >= startDate) | ||
? blobItems.findIndex( | ||
(blobItem) => | ||
blobItem?.metadata?.timestamp && new Date(blobItem.metadata.timestamp) >= startDate | ||
) | ||
: 0; | ||
if (fromIdx !== -1) { | ||
const activities = await pmap( | ||
blobItems.slice(fromIdx), | ||
async (blobItem) => { | ||
const blob = await this._containerClient.getBlobClient(blobItem.name).download(); | ||
const { readableStreamBody: stream } = blob; | ||
if (!stream) { | ||
return null; | ||
} | ||
const contents = await getStream(stream); | ||
const activity = JSON.parse(contents); | ||
return { ...activity, timestamp: new Date(activity.timestamp) } as Activity; | ||
}, | ||
{ concurrency: this._concurrency } | ||
); | ||
return { | ||
continuationToken: response?.continuationToken, | ||
items: await pmap( | ||
blobItems.slice(fromIdx), | ||
async (blobItem) => { | ||
const blob = await this._containerClient.getBlobClient(blobItem.name).download(); | ||
const { readableStreamBody: stream } = blob; | ||
const contents = await getStream(stream); | ||
const activity = JSON.parse(contents); | ||
return { ...activity, timestamp: new Date(activity.timestamp) } as Activity; | ||
}, | ||
{ concurrency: this._concurrency } | ||
continuationToken: response?.continuationToken ?? '', | ||
items: activities.reduce<Activity[]>( | ||
(acc, activity) => (activity ? acc.concat(activity) : acc), | ||
[] | ||
), | ||
@@ -182,4 +191,3 @@ }; | ||
async listTranscripts(channelId: string, continuationToken?: string): Promise<PagedResult<TranscriptInfo>> { | ||
assert(typeof channelId === 'string', '`channelId` must be a string'); | ||
assert(channelId, '`channelId` must be non-empty'); | ||
assert.string(channelId, ['channelId']); | ||
@@ -200,11 +208,9 @@ await this._initialize(); | ||
return { | ||
continuationToken: response?.continuationToken, | ||
continuationToken: response?.continuationToken ?? '', | ||
items: blobItems.map((blobItem) => { | ||
const [, conversationId] = decodeURIComponent(blobItem.name).split('/'); | ||
const [, id] = decodeURIComponent(blobItem.name).split('/'); | ||
return { | ||
channelId, | ||
id: conversationId, | ||
created: new Date(blobItem.metadata.timestamp), | ||
}; | ||
const created = blobItem.metadata?.timestamp ? new Date(blobItem.metadata.timestamp) : new Date(); | ||
return { channelId, created, id }; | ||
}), | ||
@@ -222,8 +228,5 @@ }; | ||
async deleteTranscript(channelId: string, conversationId: string): Promise<void> { | ||
assert(typeof channelId === 'string', '`channelId` must be a string'); | ||
assert(channelId, '`channelId` must be non-empty'); | ||
assert.string(channelId, ['channelId']); | ||
assert.string(conversationId, ['conversationId']); | ||
assert(typeof conversationId === 'string', '`conversationId` must be a string'); | ||
assert(conversationId, '`conversationId` must be non-empty'); | ||
await this._initialize(); | ||
@@ -260,4 +263,3 @@ | ||
async logActivity(activity: Activity): Promise<void> { | ||
assert(activity, '`activity` must not be null or undefined'); | ||
assert(typeof activity === 'object', '`activity` must be an object'); | ||
assertActivity(activity, ['activity']); | ||
@@ -269,11 +271,17 @@ await this._initialize(); | ||
await blob.upload(serialized, serialized.length, { | ||
metadata: { | ||
Id: activity.id, | ||
FromId: activity.from.id, | ||
RecipientId: activity.recipient.id, | ||
Timestamp: activity.timestamp.toJSON(), | ||
}, | ||
}); | ||
const metadata: Record<string, string> = { | ||
FromId: activity.from.id, | ||
RecipientId: activity.recipient.id, | ||
}; | ||
if (activity.id) { | ||
metadata.Id = activity.id; | ||
} | ||
if (activity.timestamp) { | ||
metadata.Timestamp = activity.timestamp.toJSON(); | ||
} | ||
await blob.upload(serialized, serialized.length, { metadata }); | ||
} | ||
} |
@@ -40,4 +40,4 @@ // Copyright (c) Microsoft Corporation. | ||
return function (err) { | ||
return err instanceof RestError && ignoredCodes.has(err.statusCode); | ||
return err instanceof RestError && err.statusCode != null && ignoredCodes.has(err.statusCode); | ||
}; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1
68607
5
32
1156
+ Addedbotbuilder-core@4.12.0-dev-20201207.e073a6e77c25(transitive)
+ Addedbotbuilder-stdlib@4.12.0-dev-20201207.e073a6e77c25(transitive)
+ Addedbotframework-connector@4.12.0-dev-20201207.e073a6e77c25(transitive)
+ Addedbotframework-schema@4.12.0-dev-20201207.e073a6e77c25(transitive)
- Removedbotbuilder-core@4.12.0-dev-20201204.119d40c4f11e(transitive)
- Removedbotframework-connector@4.12.0-dev-20201204.119d40c4f11e(transitive)
- Removedbotframework-schema@4.12.0-dev-20201204.119d40c4f11e(transitive)