diff --git a/Composer/packages/client/__tests__/components/CreationFlow/index.test.tsx b/Composer/packages/client/__tests__/components/CreationFlow/index.test.tsx index 2a1206494e..8117a34751 100644 --- a/Composer/packages/client/__tests__/components/CreationFlow/index.test.tsx +++ b/Composer/packages/client/__tests__/components/CreationFlow/index.test.tsx @@ -5,9 +5,15 @@ import * as React from 'react'; import { render, fireEvent, act } from '@botframework-composer/test-utils'; import { createHistory, createMemorySource, LocationProvider } from '@reach/router'; import { RecoilRoot } from 'recoil'; +import { getDefaultFeatureFlags } from '@bfc/shared'; import CreationFlow from '../../../src/components/CreationFlow/CreationFlow'; -import { focusedStorageFolderState, creationFlowStatusState, dispatcherState } from '../../../src/recoilModel'; +import { + focusedStorageFolderState, + creationFlowStatusState, + dispatcherState, + featureFlagsState, +} from '../../../src/recoilModel'; import { CreationFlowStatus } from '../../../src/constants'; describe('', () => { @@ -26,7 +32,7 @@ describe('', () => { saveTemplateId: jest.fn(), }); set(creationFlowStatusState, CreationFlowStatus.NEW_FROM_TEMPLATE); - + set(featureFlagsState, getDefaultFeatureFlags()); set(focusedStorageFolderState, { name: 'Desktop', parent: '/test-folder', diff --git a/Composer/packages/client/__tests__/components/home.test.tsx b/Composer/packages/client/__tests__/components/home.test.tsx index e379b8959d..3f4d035646 100644 --- a/Composer/packages/client/__tests__/components/home.test.tsx +++ b/Composer/packages/client/__tests__/components/home.test.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { fireEvent, render } from '@botframework-composer/test-utils'; -import { ProjectTemplate } from '@bfc/shared'; +import { BotTemplate } from '@bfc/shared'; import { RecentBotList } from '../../src/pages/home/RecentBotList'; import { ExampleList } from '../../src/pages/home/ExampleList'; @@ -32,7 +32,7 @@ describe('', () => { const templates = [ { description: 'echo bot', id: 'EchoBot', name: 'Echo Bot' }, { description: 'empty bot', id: 'EmptyBot', name: 'Empty Bot' }, - ] as ProjectTemplate[]; + ] as BotTemplate[]; const onClickTemplate = jest.fn((item) => item); const { container, getByText } = render(); expect(container).toHaveTextContent('Echo Bot'); diff --git a/Composer/packages/client/src/components/CreationFlow/CreateOptions.tsx b/Composer/packages/client/src/components/CreationFlow/CreateOptions.tsx index e4c6a586b2..184bc6a9b4 100644 --- a/Composer/packages/client/src/components/CreationFlow/CreateOptions.tsx +++ b/Composer/packages/client/src/components/CreationFlow/CreateOptions.tsx @@ -20,7 +20,7 @@ import { DetailsRow, } from 'office-ui-fabric-react/lib/DetailsList'; import { Sticky, StickyPositionType } from 'office-ui-fabric-react/lib/Sticky'; -import { ProjectTemplate } from '@bfc/shared'; +import { BotTemplate } from '@bfc/shared'; import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; import { NeutralColors } from '@uifabric/fluent-theme'; import { RouteComponentProps } from '@reach/router'; @@ -105,7 +105,7 @@ const optionKeys = { // -------------------- CreateOptions -------------------- // type CreateOptionsProps = { - templates: ProjectTemplate[]; + templates: BotTemplate[]; onDismiss: () => void; onNext: (data: string) => void; } & RouteComponentProps<{}>; @@ -119,7 +119,7 @@ export function CreateOptions(props: CreateOptionsProps) { const selection = useMemo(() => { return new Selection({ onSelectionChanged: () => { - const t = selection.getSelection()[0] as ProjectTemplate; + const t = selection.getSelection()[0] as BotTemplate; if (t) { setCurrentTemplate(t.id); } diff --git a/Composer/packages/client/src/pages/home/ExampleList.tsx b/Composer/packages/client/src/pages/home/ExampleList.tsx index 6054e32c05..5409d82002 100644 --- a/Composer/packages/client/src/pages/home/ExampleList.tsx +++ b/Composer/packages/client/src/pages/home/ExampleList.tsx @@ -6,7 +6,7 @@ import { jsx } from '@emotion/core'; import React from 'react'; import { ScrollablePane, ScrollbarVisibility } from 'office-ui-fabric-react/lib/ScrollablePane'; import { List } from 'office-ui-fabric-react/lib/List'; -import { ProjectTemplate } from '@bfc/shared'; +import { BotTemplate } from '@bfc/shared'; import * as exampleIcons from '../../images/samples'; @@ -20,7 +20,7 @@ import { } from './styles'; interface ExampleListProps { - examples: ProjectTemplate[]; + examples: BotTemplate[]; onClick: (templateId: string) => void; } @@ -35,7 +35,7 @@ const resolveIcon = (exampleId: string): string => { export const ExampleList: React.FC = (props) => { const { onClick, examples } = props; - function onRenderCell(item?: ProjectTemplate): React.ReactNode { + function onRenderCell(item?: BotTemplate): React.ReactNode { if (!item) { return; } diff --git a/Composer/packages/client/src/recoilModel/atoms/appState.ts b/Composer/packages/client/src/recoilModel/atoms/appState.ts index a409ccbd7b..c9d812051a 100644 --- a/Composer/packages/client/src/recoilModel/atoms/appState.ts +++ b/Composer/packages/client/src/recoilModel/atoms/appState.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { atom, atomFamily } from 'recoil'; -import { FormDialogSchemaTemplate, FeatureFlagMap, ProjectTemplate, UserSettings } from '@bfc/shared'; +import { FormDialogSchemaTemplate, FeatureFlagMap, BotTemplate, UserSettings } from '@bfc/shared'; import { ExtensionMetadata } from '@bfc/extension-client'; import { @@ -52,7 +52,7 @@ export const recentProjectsState = atom({ default: [], }); -export const templateProjectsState = atom({ +export const templateProjectsState = atom({ key: getFullyQualifiedKey('templateProjects'), default: [], }); diff --git a/Composer/packages/client/src/recoilModel/selectors/projectTemplates.ts b/Composer/packages/client/src/recoilModel/selectors/projectTemplates.ts index d3b8a8124d..5567474c85 100644 --- a/Composer/packages/client/src/recoilModel/selectors/projectTemplates.ts +++ b/Composer/packages/client/src/recoilModel/selectors/projectTemplates.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import { BotTemplate } from '@bfc/shared'; import { selector } from 'recoil'; import { featureFlagsState, templateProjectsState } from '../atoms/appState'; @@ -11,13 +12,20 @@ export const filteredTemplatesSelector = selector({ const templates = get(templateProjectsState); const featureFlags = get(featureFlagsState); - const filteredTemplates = [...templates]; + let filteredTemplates = [...templates]; if (!featureFlags?.VA_CREATION?.enabled) { const vaTemplateIndex = filteredTemplates.findIndex((template) => template.id === 'va-core'); if (vaTemplateIndex !== -1) { filteredTemplates.splice(vaTemplateIndex, 1); } } + if (!featureFlags.REMOTE_TEMPLATE_CREATION_EXPERIENCE.enabled) { + filteredTemplates = filteredTemplates.filter((template: BotTemplate) => { + if (template.path) { + return template; + } + }); + } return filteredTemplates; }, }); diff --git a/Composer/packages/lib/shared/src/featureFlagUtils/index.ts b/Composer/packages/lib/shared/src/featureFlagUtils/index.ts index 62507fa2f8..a624678f6a 100644 --- a/Composer/packages/lib/shared/src/featureFlagUtils/index.ts +++ b/Composer/packages/lib/shared/src/featureFlagUtils/index.ts @@ -14,7 +14,7 @@ export type FeatureFlag = { enabled: boolean; }; -export type FeatureFlagKey = 'VA_CREATION' | 'FORM_DIALOG'; +export type FeatureFlagKey = 'VA_CREATION' | 'FORM_DIALOG' | 'REMOTE_TEMPLATE_CREATION_EXPERIENCE'; export type FeatureFlagMap = Record; @@ -22,7 +22,7 @@ export const getDefaultFeatureFlags = (): FeatureFlagMap => ({ VA_CREATION: { displayName: formatMessage('VA Creation'), description: formatMessage('VA template made available in new bot flow.'), - isHidden: false, + isHidden: true, enabled: false, }, FORM_DIALOG: { @@ -31,4 +31,12 @@ export const getDefaultFeatureFlags = (): FeatureFlagMap => ({ isHidden: true, enabled: false, }, + REMOTE_TEMPLATE_CREATION_EXPERIENCE: { + displayName: formatMessage('Remote templates'), + description: formatMessage( + 'If turned on then externally stored templates will be selectable in the new bot flow template list' + ), + isHidden: true, + enabled: false, + }, }); diff --git a/Composer/packages/server/package.json b/Composer/packages/server/package.json index d421c48b75..d7441653a5 100644 --- a/Composer/packages/server/package.json +++ b/Composer/packages/server/package.json @@ -62,6 +62,7 @@ "@bfc/lg-languageserver": "*", "@bfc/lu-languageserver": "*", "@bfc/shared": "*", + "@microsoft/bf-dialog": "4.11.0-dev.20201025.69cf2b9", "@microsoft/bf-dispatcher": "^4.11.0-beta.20201016.393c6b2", "@microsoft/bf-generate-library": "^4.10.0-daily.20201026.178799", "@microsoft/bf-lu": "^4.11.0-rc.20201030.a9f9b96", diff --git a/Composer/packages/server/src/controllers/__tests__/eject.test.ts b/Composer/packages/server/src/controllers/__tests__/eject.test.ts index eb621a17d9..d7ffe661d8 100644 --- a/Composer/packages/server/src/controllers/__tests__/eject.test.ts +++ b/Composer/packages/server/src/controllers/__tests__/eject.test.ts @@ -51,6 +51,7 @@ beforeAll(async () => { name: 'C#', startCommand: 'dotnet run --project azurewebapp', path: './', + identifyManifest: jest.fn(), eject: jest.fn(), build: jest.fn(), run: jest.fn(), diff --git a/Composer/packages/server/src/controllers/project.ts b/Composer/packages/server/src/controllers/project.ts index 33c1410db3..6fdae39dd1 100644 --- a/Composer/packages/server/src/controllers/project.ts +++ b/Composer/packages/server/src/controllers/project.ts @@ -6,6 +6,7 @@ import * as fs from 'fs'; import { Request, Response } from 'express'; import { Archiver } from 'archiver'; import { ExtensionContext } from '@bfc/extension'; +import { SchemaMerger } from '@microsoft/bf-dialog/lib/library/schemaMerger'; import log from '../logger'; import { BotProjectService } from '../services/project'; @@ -54,6 +55,33 @@ async function createProject(req: Request, res: Response) { await AssetService.manager.copyBoilerplate(currentProject.dataDir, currentProject.fileStorage); if (currentProject !== undefined) { + if (currentProject.settings?.runtime?.customRuntime === true) { + const runtime = ExtensionContext.getRuntimeByProject(currentProject); + const runtimePath = currentProject.settings.runtime.path; + + if (!fs.existsSync(runtimePath)) { + await runtime.eject(currentProject, currentProject.fileStorage); + } + + // install all dependencies and build the app + await runtime.build(runtimePath, currentProject); + + const manifestFile = runtime.identifyManifest(runtimePath); + + // run the merge command to merge all package dependencies from the template to the bot project + const realMerge = new SchemaMerger( + [manifestFile], + Path.join(currentProject.dataDir, 'schemas/sdk'), + Path.join(currentProject.dataDir, 'dialogs/imported'), + false, + false, + console.log, + console.warn, + console.error + ); + + await realMerge.merge(); + } await currentProject.updateBotInfo(name, description, preserveRoot); if (schemaUrl) { await currentProject.saveSchemaToProject(schemaUrl, locationRef.path); @@ -367,7 +395,7 @@ async function checkBoilerplateVersion(req: Request, res: Response) { const currentProject = await BotProjectService.getProjectById(projectId, user); if (currentProject !== undefined) { - const latestVersion = AssetService.manager.getBoilerplateCurrentVersion(); + const latestVersion = await AssetService.manager.getBoilerplateCurrentVersion(); const currentVersion = await AssetService.manager.getBoilerplateVersionFromProject(currentProject); const updateRequired = (latestVersion && currentVersion && latestVersion > currentVersion) || // versions are present in both locations, latest is newer diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index b02415fd27..29c2613422 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -41,6 +41,9 @@ "a_valid_url_should_start_with_http_or_https_327b1a30": { "message": "A valid URL should start with http:// or https://" }, + "a_valid_url_should_start_with_http_or_https_d24b3591": { + "message": "A valid url should start with http:// or https://" + }, "about_70c18bba": { "message": "About" }, @@ -128,6 +131,9 @@ "add_a_welcome_message_9e1480b2": { "message": "Add a welcome message" }, + "add_additional_url_bdfac25d": { + "message": "Add additional URL" + }, "add_alternative_phrasing_17e0304c": { "message": "+ Add alternative phrasing" }, @@ -227,6 +233,9 @@ "any_string_f22dc2e1": { "message": "any string" }, + "append_choices_35c45a2d": { + "message": "Append choices" + }, "application_language_87691b6": { "message": "Application Language" }, @@ -611,6 +620,9 @@ "could_not_connect_to_storage_50411de0": { "message": "Could not connect to storage." }, + "could_not_init_plugin_1f1c29cd": { + "message": "Could not init plugin" + }, "couldn_t_complete_the_update_a337a359": { "message": "Couldn''t complete the update:" }, @@ -683,6 +695,9 @@ "create_kb_from_url_or_file_49ad6671": { "message": "Create KB from URL or file" }, + "create_knowledge_base_db6d66c4": { + "message": "Create knowledge base" + }, "create_knowledge_base_from_scratch_afe4d2a2": { "message": "Create knowledge base from scratch" }, @@ -1268,6 +1283,9 @@ "if_this_problem_persists_please_file_an_issue_on_6fbc8e2b": { "message": "If this problem persists, please file an issue on" }, + "if_turned_on_then_externally_stored_templates_will_3f487651": { + "message": "If turned on then externally stored templates will be selectable in the new bot flow template list" + }, "if_you_already_have_a_luis_account_provide_the_inf_bede07a4": { "message": "If you already have a LUIS account, provide the information below. If you do not have an account yet, create a (free) account first." }, @@ -1403,6 +1421,9 @@ "learn_more_about_knowledge_base_sources_24369b09": { "message": "Learn more about knowledge base sources. " }, + "learn_more_about_qna_maker_skus_998c567": { + "message": "Learn more about QnA Maker SKUs." + }, "learn_more_about_title_d1d3edbe": { "message": "Learn more about { title }" }, @@ -1799,6 +1820,9 @@ "open_notification_panel_5796edb3": { "message": "Open notification panel" }, + "open_skills_page_for_configuration_details_a2a484ea": { + "message": "Open Skills page for configuration details" + }, "optional_221bcc9d": { "message": "Optional" }, @@ -1889,6 +1913,9 @@ "please_select_an_activity_type_92f4a8a1": { "message": "Please select an activity type" }, + "populate_your_knowledge_base_bb2d3605": { + "message": "Populate your Knowledge Base" + }, "press_enter_to_add_this_item_or_tab_to_move_to_the_6beb8a14": { "message": "press Enter to add this item or Tab to move to the next interactive element" }, @@ -2069,6 +2096,9 @@ "regex_intent_is_already_defined_df095c1f": { "message": "RegEx { intent } is already defined" }, + "regular_expression_855557bf": { + "message": "Regular Expression" + }, "regular_expression_recognizer_44664557": { "message": "Regular expression recognizer" }, @@ -2078,6 +2108,9 @@ "reloading_49d2f661": { "message": "Reloading" }, + "remote_templates_a23c242d": { + "message": "Remote templates" + }, "remove_f47dc62a": { "message": "Remove" }, @@ -2315,6 +2348,9 @@ "skills_49cccd6a": { "message": "Skills" }, + "skip_this_step_to_add_questions_and_answers_manual_ed1b9f80": { + "message": "Skip this step to add questions and answers manually after creation. The number of sources and file size you can add depends on the QnA service SKU you choose. " + }, "sorry_something_went_wrong_with_connecting_bot_run_7d6785e3": { "message": "Sorry, something went wrong with connecting bot runtime" }, @@ -2510,6 +2546,9 @@ "this_trigger_type_is_not_supported_by_the_regex_re_dc3eefa2": { "message": "This trigger type is not supported by the RegEx recognizer. To ensure this trigger is fired, change the recognizer type." }, + "this_url_is_duplicated_a0768f44": { + "message": "This url is duplicated" + }, "this_version_of_the_content_is_out_of_date_and_you_5e878f29": { "message": "This version of the content is out of date, and your last change was rejected. The content will be automatically refreshed." }, @@ -2621,6 +2660,9 @@ "typing_activity_6b634ae": { "message": "Typing activity" }, + "unable_to_determine_recognizer_type_from_data_valu_2960f526": { + "message": "Unable to determine recognizer type from data: { value }" + }, "undo_a7be8fef": { "message": "Undo" }, @@ -2672,6 +2714,9 @@ "updating_scripts_e17a5722": { "message": "Updating scripts... " }, + "url_22a5f3b8": { + "message": "URL" + }, "url_8c4ff7d2": { "message": "Url" }, diff --git a/Composer/packages/server/src/models/asset/assetManager.ts b/Composer/packages/server/src/models/asset/assetManager.ts index 76a614fb20..a5fa019605 100644 --- a/Composer/packages/server/src/models/asset/assetManager.ts +++ b/Composer/packages/server/src/models/asset/assetManager.ts @@ -3,9 +3,13 @@ import fs from 'fs'; import path from 'path'; +import { exec } from 'child_process'; +import { promisify } from 'util'; import find from 'lodash/find'; -import { UserIdentity, ExtensionContext } from '@bfc/extension'; +import { UserIdentity, ExtensionContext, BotTemplate, FileExtensions } from '@bfc/extension'; +import { mkdirs, readFile } from 'fs-extra'; +import rimraf from 'rimraf'; import log from '../../logger'; import { LocalDiskStorage } from '../storage/localDiskStorage'; @@ -16,6 +20,9 @@ import StorageService from '../../services/storage'; import { IFileStorage } from '../storage/interface'; import { BotProject } from '../bot/botProject'; +const execAsync = promisify(exec); +const removeDirAndFiles = promisify(rimraf); + export class AssetManager { public templateStorage: LocalDiskStorage; private _botProjectFileTemplate; @@ -52,13 +59,61 @@ export class AssetManager { return ref; } + private async getRemoteTemplate(template: BotTemplate, destinationPath: string) { + // install package + if (template.package) { + const { stderr: initErr } = await execAsync( + `dotnet new -i ${template.package.packageName}::${template.package.packageVersion}` + ); + if (initErr) { + throw new Error(initErr); + } + const { stderr: initErr2 } = await execAsync(`dotnet new ${template.id}`, { + cwd: destinationPath, + }); + if (initErr2) { + throw new Error(initErr2); + } + } else { + throw new Error('selected template has no local or external address'); + } + } + private async copyDataFilesTo(templateId: string, dstDir: string, dstStorage: IFileStorage, locale?: string) { const template = find(ExtensionContext.extensions.botTemplates, { id: templateId }); - if (template === undefined || template.path === undefined) { + if (template === undefined || (template.path === undefined && template.package === undefined)) { throw new Error(`no such template with id ${templateId}`); } - // copy Composer data files - await copyDir(template.path, this.templateStorage, dstDir, dstStorage); + + let templateSrcPath = template.path; + const isHostedTemplate = !templateSrcPath; + if (isHostedTemplate) { + // create empty temp directory on server for holding externally hosted template src + templateSrcPath = path.resolve(__dirname, '../../../temp'); + if (fs.existsSync(templateSrcPath)) { + await removeDirAndFiles(templateSrcPath); + } + await mkdirs(templateSrcPath, (err) => { + if (err) { + throw new Error('Error creating temp directory for external template storage'); + } + }); + await this.getRemoteTemplate(template, templateSrcPath); + } + + if (templateSrcPath) { + // copy Composer data files + await copyDir(templateSrcPath, this.templateStorage, dstDir, dstStorage); + + if (isHostedTemplate) { + try { + await removeDirAndFiles(templateSrcPath); + } catch (err) { + throw new Error('Issue deleting temp generated file for external template assets'); + } + } + } + // if we have a locale override, copy those files over too if (locale != null) { const localePath = path.join(__dirname, '..', '..', '..', 'schemas', `sdk.${locale}.schema`); @@ -76,7 +131,7 @@ export class AssetManager { public async copyBoilerplate(dstDir: string, dstStorage: IFileStorage) { for (const boilerplate of ExtensionContext.extensions.baseTemplates) { const boilerplatePath = boilerplate.path; - if (await this.templateStorage.exists(boilerplatePath)) { + if (boilerplatePath && (await this.templateStorage.exists(boilerplatePath))) { await copyDir(boilerplatePath, this.templateStorage, dstDir, dstStorage); } } @@ -104,24 +159,27 @@ export class AssetManager { // return the current version of the boilerplate content, if one exists so specified // this is based off of the first boilerplate template added to the app. - public getBoilerplateCurrentVersion(): string | undefined { + public async getBoilerplateCurrentVersion(): Promise { if (!ExtensionContext.extensions.baseTemplates.length) { return undefined; } const boilerplate = ExtensionContext.extensions.baseTemplates[0]; - const location = Path.join(boilerplate.path, 'scripts', 'package.json'); - try { - if (fs.existsSync(location)) { - const raw = fs.readFileSync(location, 'utf8'); - const json = JSON.parse(raw); - if (json && json.version) { - return json.version; - } else { - return undefined; + if (boilerplate.path) { + const location = Path.join(boilerplate.path, 'scripts', 'package.json'); + try { + if (fs.existsSync(location)) { + const raw = await readFile(location, 'utf8'); + + const json = JSON.parse(raw); + if (json && json.version) { + return json.version; + } else { + return undefined; + } } + } catch (err) { + return undefined; } - } catch (err) { - return undefined; } } @@ -131,15 +189,17 @@ export class AssetManager { } const boilerplate = ExtensionContext.extensions.botTemplates[0]; - const location = Path.join(boilerplate.path, `${boilerplate.id}.botproj`); - try { - if (fs.existsSync(location)) { - const raw = fs.readFileSync(location, 'utf8'); - const json = JSON.parse(raw); - return json; + if (boilerplate.path) { + const location = Path.join(boilerplate.path, `${boilerplate.id + FileExtensions.BotProject}`); + try { + if (fs.existsSync(location)) { + const raw = fs.readFileSync(location, 'utf8'); + const json = JSON.parse(raw); + return json; + } + } catch (err) { + return ''; } - } catch (err) { - return ''; } } } diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index 27ae3fbf26..b73383275d 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -205,6 +205,15 @@ export class BotProject implements IBotProject { public getEnvSettings = async (obfuscate: boolean) => { const settings = await this.settingManager.get(obfuscate); + // Resolve relative path for custom runtime if the path is relative + if (settings?.runtime?.customRuntime && settings.runtime.path && !Path.isAbsolute(settings.runtime.path)) { + const absolutePath = Path.resolve(this.dir, 'settings', settings.runtime.path); + if (fs.existsSync(absolutePath)) { + settings.runtime.path = absolutePath; + await this.updateEnvSettings(settings); + } + } + // fix old bot have no language settings if (!settings?.defaultLanguage) { settings.defaultLanguage = defaultLanguage; diff --git a/Composer/packages/types/src/runtime.ts b/Composer/packages/types/src/runtime.ts index 3ba1d7dee3..1da0fe28aa 100644 --- a/Composer/packages/types/src/runtime.ts +++ b/Composer/packages/types/src/runtime.ts @@ -37,11 +37,17 @@ export type BotTemplate = { name: string; description: string; /* absolute path */ - path: string; + path?: string; /* tags for further grouping and search secenario */ tags?: string[]; /* list of supported runtime versions */ support?: string[]; + package?: { + packageName: string; + packageSource: string; + packageVersion: string; + }; + index?: number; }; export type RuntimeTemplate = { @@ -53,6 +59,8 @@ export type RuntimeTemplate = { run: (project: IBotProject, localDisk?: any) => Promise; + identifyManifest: (runtimePath: string) => string; + /** build for deploy method */ buildDeploy: ( runtimePath: string, diff --git a/Composer/packages/types/src/server.ts b/Composer/packages/types/src/server.ts index 3dd18f023b..08a3bed1b7 100644 --- a/Composer/packages/types/src/server.ts +++ b/Composer/packages/types/src/server.ts @@ -4,18 +4,6 @@ import { Skill, FileInfo } from './indexers'; import { IDiagnostic } from './diagnostic'; import { DialogSetting } from './settings'; -export type ProjectTemplate = { - id: string; - name: string; - description: string; - /* absolute path */ - path: string; - /* tags for further grouping and search secenario */ - tags?: string[]; - /* list of supported runtime versions */ - support?: string[]; -}; - export type IBotProject = { fileStorage: any; dir: string; diff --git a/Composer/yarn.lock b/Composer/yarn.lock index dce1056d3f..29479ab637 100644 --- a/Composer/yarn.lock +++ b/Composer/yarn.lock @@ -7,6 +7,15 @@ resolved "https://botbuilder.myget.org/F/botframework-cli/npm/7zip-bin/-/7zip-bin-5.0.3.tgz#bc5b5532ecafd923a61f2fb097e3b108c0106a3f" integrity sha1-vFtVMuyv2SOmHy+wl+OxCMAQaj8= +"@apidevtools/json-schema-ref-parser@^9.0.1": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz#5d9000a3ac1fd25404da886da6b266adcd99cf1c" + integrity sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg== + dependencies: + "@jsdevtools/ono" "^7.1.3" + call-me-maybe "^1.0.1" + js-yaml "^3.13.1" + "@azure/cognitiveservices-luis-authoring@4.0.0-preview.1": version "4.0.0-preview.1" resolved "https://botbuilder.myget.org/F/botframework-cli/npm/@azure/cognitiveservices-luis-authoring/-/@azure/cognitiveservices-luis-authoring-4.0.0-preview.1.tgz#79de764893dc997d95713bb6a0487d887dc78f40" @@ -2965,6 +2974,26 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + +"@microsoft/bf-cli-command@4.11.0-dev.20201025.69cf2b9": + version "4.11.0-dev.20201025.69cf2b9" + resolved "https://registry.yarnpkg.com/@microsoft/bf-cli-command/-/bf-cli-command-4.11.0-dev.20201025.69cf2b9.tgz#286b9dfef6483a55562baf4ebf7a0d99a43075e4" + integrity sha512-jhcpVBrFrFepevPZlP9iGJrUPh30E/L8yHr5wvX6jcSt/7RSVD+A7DIShDO+b8n45ii+8ZxVGqEGv7aN5ggp6g== + dependencies: + "@oclif/command" "~1.5.19" + "@oclif/config" "~1.13.3" + "@oclif/errors" "~1.2.2" + applicationinsights "^1.0.8" + chalk "2.4.1" + cli-ux "~4.9.3" + debug "^4.1.1" + fs-extra "^7.0.1" + tslib "^2.0.3" + "@microsoft/bf-cli-command@^4.10.0-preview.141651": version "4.10.0-preview.141651" resolved "https://botbuilder.myget.org/F/botframework-cli/npm/@microsoft/bf-cli-command/-/@microsoft/bf-cli-command-4.10.0-preview.141651.tgz#680875f716285fb8658da8098a0ee524b07c5765" @@ -2980,6 +3009,33 @@ fs-extra "^7.0.1" tslib "~1.10.0" +"@microsoft/bf-dialog@4.11.0-dev.20201025.69cf2b9": + version "4.11.0-dev.20201025.69cf2b9" + resolved "https://registry.yarnpkg.com/@microsoft/bf-dialog/-/bf-dialog-4.11.0-dev.20201025.69cf2b9.tgz#7f33e2b4d52e355d1f5888b9d1f341ae32fe90c3" + integrity sha512-gzBRwKSEkHXgPN9bB5ttuEORRbK7o6Py+7P9JKYQjDrytt2C7ZSqKn7UtVm8/OBO8BDwu9EMONpfq2Ep3FPUtw== + dependencies: + "@apidevtools/json-schema-ref-parser" "^9.0.1" + "@microsoft/bf-cli-command" "4.11.0-dev.20201025.69cf2b9" + "@oclif/command" "~1.5.19" + "@oclif/config" "~1.13.3" + "@oclif/errors" "~1.2.2" + "@snyk/nuget-semver" "~1.3.0" + "@types/lru-cache" "^5.1.0" + "@types/xml2js" "^0.4.4" + ajv "^6.12.2" + chalk "^2.4.2" + clone "^2.1.2" + fs-extra "^8.1.0" + get-uri "~3.0.2" + globby "^11.0.0" + json-ptr "~1.3.0" + json-schema-merge-allof "~0.7.0" + os "~0.1.1" + path "^0.12.7" + seedrandom "~3.0.5" + tslib "^2.0.3" + xml2js "^0.4.19" + "@microsoft/bf-dispatcher@^4.11.0-beta.20201016.393c6b2": version "4.11.0-beta.20201016.393c6b2" resolved "https://registry.yarnpkg.com/@microsoft/bf-dispatcher/-/bf-dispatcher-4.11.0-beta.20201016.393c6b2.tgz#7dab414752f8711fed37ae5625c38fdd0192eddb" @@ -3243,6 +3299,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@snyk/nuget-semver@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@snyk/nuget-semver/-/nuget-semver-1.3.0.tgz#a74ef5340dcada3bc4b3a019b7950738df51199c" + integrity sha512-1CL4BzQKFPwml+BBefKuM0v9UfsFOgSKzrTfYpUkiSNkUVsMxXK37LlT3HtG7zGpMxXiG+XXfPopo/96Z0wfNg== + "@svgr/babel-plugin-add-jsx-attribute@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.0.0.tgz#5acf239cd2747b1a36ec7e708de05d914cb9b948" @@ -3416,6 +3477,11 @@ "@testing-library/dom" "^7.2.2" "@types/testing-library__react" "^10.0.1" +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" @@ -4068,6 +4134,13 @@ dependencies: "@types/node" "*" +"@types/xml2js@^0.4.4": + version "0.4.5" + resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.5.tgz#d21759b056f282d9c7066f15bbf5c19b908f22fa" + integrity sha512-yohU3zMn0fkhlape1nxXG2bLEGZRc1FeqF80RoHaYXJN7uibaauXfhzhOJr1Xh36sn+/tx21QAOf07b/xYVk1w== + dependencies: + "@types/node" "*" + "@types/xmldom@^0.1.29": version "0.1.29" resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/@types/xmldom/-/@types/xmldom-0.1.29.tgz#c4428b0ca86d3b881475726fd94980b38a27c381" @@ -8107,6 +8180,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" + integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -8140,6 +8218,13 @@ debug@3.1.0, debug@=3.1.0: dependencies: ms "2.0.0" +debug@4: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -9839,6 +9924,11 @@ file-loader@4.2.0: loader-utils "^1.2.3" schema-utils "^2.0.0" +file-uri-to-path@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba" + integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg== + filelist@^1.0.1: version "1.0.1" resolved "https://botbuilder.myget.org/F/botframework-cli/npm/filelist/-/filelist-1.0.1.tgz#f10d1a3ae86c1694808e8f20906f43d4c9132dbb" @@ -10275,6 +10365,14 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +ftp@^0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -10389,6 +10487,18 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-uri@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" + integrity sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg== + dependencies: + "@tootallnate/once" "1" + data-uri-to-buffer "3" + debug "4" + file-uri-to-path "2" + fs-extra "^8.1.0" + ftp "^0.3.10" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -12561,6 +12671,13 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-ptr@~1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/json-ptr/-/json-ptr-1.3.2.tgz#17f45b322a843b1f2fbcc9b45132bd9b3ba8cd38" + integrity sha512-tFH40YQ+lG7mgYYM1kGZOhQngO4SbOEHZJlA4W+NtetWZ20EUU3BPU+30uWRKumuAJoSo5eqrsXD2h72ioS8ew== + dependencies: + tslib "^2.0.0" + json-schema-compare@^0.2.2: version "0.2.2" resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/json-schema-compare/-/json-schema-compare-0.2.2.tgz#dd601508335a90c7f4cfadb6b2e397225c908e56" @@ -12584,6 +12701,15 @@ json-schema-merge-allof@^0.6.0: json-schema-compare "^0.2.2" lodash "^4.17.4" +json-schema-merge-allof@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/json-schema-merge-allof/-/json-schema-merge-allof-0.7.0.tgz#84d3e8c3e03d3060014286958eb8834fa9d76304" + integrity sha512-kvsuSVnl1n5xnNEu5ed4o8r8ujSA4/IgRtHmpgfMfa7FOMIRAzN4F9qbuklouTn5J8bi83y6MQ11n+ERMMTXZg== + dependencies: + compute-lcm "^1.1.0" + json-schema-compare "^0.2.2" + lodash "^4.17.4" + json-schema-ref-parser@^7.1.0, json-schema-ref-parser@^7.1.3: version "7.1.4" resolved "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/json-schema-ref-parser/-/json-schema-ref-parser-7.1.4.tgz#abb3f2613911e9060dc2268477b40591753facf0" @@ -13827,6 +13953,11 @@ ms@2.1.1, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" @@ -14560,6 +14691,11 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= +os@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/os/-/os-0.1.1.tgz#208845e89e193ad4d971474b93947736a56d13f3" + integrity sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M= + osenv@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" @@ -16421,6 +16557,16 @@ read-text-file@^1.1.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readable-stream@^3.0.6, readable-stream@^3.1.1: version "3.2.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d" @@ -20148,6 +20294,11 @@ xmlhttprequest@1: resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= + xregexp@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" diff --git a/extensions/runtimes/src/index.ts b/extensions/runtimes/src/index.ts index 43e3ced362..a67fa2a38e 100644 --- a/extensions/runtimes/src/index.ts +++ b/extensions/runtimes/src/index.ts @@ -39,6 +39,9 @@ export default async (composer: any): Promise => { } composer.log('FINISHED BUILDING!'); }, + identifyManifest: (runtimePath: string): string => { + return path.join(runtimePath, 'azurewebapp', 'Microsoft.BotFramework.Composer.WebApp.csproj'); + }, run: async (project: any, localDisk: IFileStorage) => { composer.log('RUN THIS C# PROJECT!'); }, @@ -182,6 +185,9 @@ export default async (composer: any): Promise => { } composer.log('BUILD COMPLETE'); }, + identifyManifest: (runtimePath: string): string => { + return path.join(runtimePath, 'Microsoft.BotFramework.Composer.WebApp.csproj'); + }, run: async (project: any, localDisk: IFileStorage) => { // do stuff }, diff --git a/extensions/samples/package.json b/extensions/samples/package.json index 9305660613..ea180714dc 100644 --- a/extensions/samples/package.json +++ b/extensions/samples/package.json @@ -11,6 +11,7 @@ "author": "", "license": "ISC", "dependencies": { + "@botframework-composer/types": "file:../../Composer/packages/types", "path": "^0.12.7", "portfinder": "^1.0.26", "rimraf": "^3.0.2", diff --git a/extensions/samples/src/index.ts b/extensions/samples/src/index.ts index 75e1175527..bdab71868e 100644 --- a/extensions/samples/src/index.ts +++ b/extensions/samples/src/index.ts @@ -3,6 +3,7 @@ import path from 'path'; import fs from 'fs'; +import { BotTemplate } from '@botframework-composer/types'; const samplesDir = path.resolve(__dirname, '../assets/projects'); const boilerplateDir = path.resolve(__dirname, '../assets/shared'); @@ -68,9 +69,22 @@ const samplesRegitry = { }, }; -function getSamples(): any[] { +function getRemoteSamples(): BotTemplate[] { + return [{ + id: 'conversationalcore', + name: 'Conversational Core', + description: 'A hosted template that provides a root bot extended by common .lg packages', + package: { + packageName: 'Microsoft.ConversationalCore.Template', + packageSource: 'nuget', + packageVersion: '0.0.1-preview5' + } + }] +} + +function getSamples(): BotTemplate[] { const subPaths = fs.readdirSync(samplesDir); - const samples = []; + let samples: BotTemplate[] = []; for (const subPath of subPaths) { const fullPath = samplesDir + '/' + subPath; if (!fs.statSync(fullPath).isDirectory()) { @@ -79,12 +93,15 @@ function getSamples(): any[] { // only looking for directories const dirname = subPath; - let sample = { id: dirname, name: dirname, description: dirname, path: fullPath, ...samplesRegitry['*'] }; + let sample: BotTemplate = { id: dirname, name: dirname, description: dirname, path: fullPath, ...samplesRegitry['*'] }; if (samplesRegitry[sample.id]) { sample = { ...sample, ...samplesRegitry[sample.id] }; } samples.push(sample); } + + samples = samples.concat(getRemoteSamples()); + samples.sort((a, b) => { if (a.index && b.index) { return a.index - b.index; diff --git a/extensions/samples/yarn.lock b/extensions/samples/yarn.lock index 5e8e3282ee..17ee65b0d8 100644 --- a/extensions/samples/yarn.lock +++ b/extensions/samples/yarn.lock @@ -2,11 +2,79 @@ # yarn lockfile v1 +"@botframework-composer/types@file:../../Composer/packages/types": + version "0.0.2" + dependencies: + "@types/express" "^4.16.1" + json-schema "^0.2.5" + +"@types/body-parser@*": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.33" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" + integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== + dependencies: + "@types/node" "*" + +"@types/express-serve-static-core@*": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz#d9af025e925fc8b089be37423b8d1eac781be084" + integrity sha512-RgDi5a4nuzam073lRGKTUIaL3eF2+H7LJvJ8eUnCI0wA6SNjXc44DCmWNiTLs/AZ7QlsFWZiw/gTG3nSQGL0fA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@^4.16.1": + version "4.17.8" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" + integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/mime@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" + integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q== + +"@types/node@*": + version "14.14.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.3.tgz#e1c09064121f894baaad2bd9f12ce4a41bffb274" + integrity sha512-33/L34xS7HVUx23e0wOT2V1qPF1IrHgQccdJVm9uXGTB9vFBrrzBtkQymT8VskeKOxjz55MSqMv0xuLq+u98WQ== + "@types/node@^14.11.8": version "14.11.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.8.tgz#fe2012f2355e4ce08bca44aeb3abbb21cf88d33f" integrity sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw== +"@types/qs@*": + version "6.9.5" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.5.tgz#434711bdd49eb5ee69d90c1d67c354a9a8ecb18b" + integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + +"@types/serve-static@*": + version "1.13.6" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.6.tgz#866b1b8dec41c36e28c7be40ac725b88be43c5c1" + integrity sha512-nuRJmv7jW7VmCVTn+IgYDkkbbDGyIINOeu/G0d74X3lm6E5KfMeQPJhxIt1ayQeQB3cSxvYs1RA/wipYoFB4EA== + dependencies: + "@types/mime" "*" + "@types/node" "*" + async@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" @@ -74,6 +142,11 @@ inherits@2.0.3: resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +json-schema@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.5.tgz#97997f50972dd0500214e208c407efa4b5d7063b" + integrity sha512-gWJOWYFrhQ8j7pVm0EM8Slr+EPVq1Phf6lvzvD/WCeqkrx/f2xBI0xOsRRS9xCn3I4vKtP519dvs3TP09r24wQ== + lodash@^4.17.14: version "4.17.17" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.17.tgz#d9018b3acc57a95c9dcf4a45c6b63b877b6c2d45"