diff --git a/angular.json b/angular.json index 01b33b1ad..d8c5b6582 100644 --- a/angular.json +++ b/angular.json @@ -1,42 +1,46 @@ { - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": ".", - "projects": { - "angularfire": { - "projectType": "library", - "root": "src", - "sourceRoot": "src", - "prefix": "angularfire", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "tsconfig.json", - "project": "src/package.json" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "src/test.ts", - "tsConfig": "tsconfig.spec.json", - "karmaConfig": "karma.conf.js" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "tsconfig.json", - "tsconfig.spec.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": ".", + "projects": { + "angularfire": { + "projectType": "library", + "root": "src", + "sourceRoot": "src", + "prefix": "angularfire", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "tsconfig.json", + "project": "src/package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "tsconfig.json", + "tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**" + ] } } - }}, - "defaultProject": "angularfire" - } \ No newline at end of file + } + } + }, + "defaultProject": "angularfire", + "cli": { + "analytics": "86795b8f-9036-4a53-929c-a7303453d677" + } +} \ No newline at end of file diff --git a/docs/deploy/getting-started.md b/docs/deploy/getting-started.md index f35d4eff2..1158989ae 100644 --- a/docs/deploy/getting-started.md +++ b/docs/deploy/getting-started.md @@ -1,6 +1,10 @@ -# Deploy your application on Firebase Hosting +# Deploy your application on Firebase Hosting & Functions -In this guide, we'll look at how to use `@angular/fire` to automatically deploy an Angular application to Firebase hosting by using the Angular CLI. +In this guide, we'll look at how to use `@angular/fire` to automatically deploy an Angular application to Firebase hosting or functions by using the Angular CLI. + +`@angular/fire` uses Firebase functions to deploy your Angular universal projects, with server-side rendering enabled. + +**Angular Universal deployments work with `@nguniversal/*` version 9.0.0 and above**. ## Step 1: add `@angular/fire` to your project @@ -12,7 +16,9 @@ ng add @angular/fire *Note that the command above assumes you have global Angular CLI installed. To install Angular CLI globally run `npm i -g @angular/cli`.* -The command above will trigger the `@angular/fire` `ng-add` schematics. The schematics will open a web browser and guide you through the Firebase authentication flow (if you're not signed in already). After you authenticate, you'll see a prompt to select a Firebase hosting project. +First, the command above will check if you have an Angular universal project. It'll do so by looking at your `angular.json` project, looking for a `server` target for the specified project. If it finds one, it'll ask you if you want to deploy the project in a firebase function. + +After that it will trigger the `@angular/fire` `ng-add` schematics. The schematics will open a web browser and guide you through the Firebase authentication flow (if you're not signed in already). After you authenticate, you'll see a prompt to select a Firebase hosting project. The schematics will do the following: @@ -22,7 +28,7 @@ The schematics will do the following: In the end, your `angular.json` project will look like below: -```json +```js { "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, @@ -32,7 +38,9 @@ In the end, your `angular.json` project will look like below: // ... "deploy": { "builder": "@angular/fire:deploy", - "options": {} + "options": {} // Here you may find an "ssr": true option if you've + // selected that you want to deploy your Angular universal project + // as a firebase function. } } } @@ -53,14 +61,30 @@ ng add @angular/fire --project=[PROJECT_NAME] As the second step, to deploy your project run: ``` -ng run [ANGULAR_PROJECT_NAME]:deploy +ng deploy --project=[PROJECT_NAME] ``` +*The `--project` option is optional. Learn more [here](https://angular.io/cli/deploy).* + The command above will trigger: 1. Production build of your application 2. Deployment of the produced assets to the firebase hosting project you selected during `ng add` +If you've specified that you want a server-side rendering enabled deployment in a firebase function, the command will also: + +1. Create a firebase function in `dist`, which directly consumes `main.js` from your server output directory. +2. Create `package.json` for the firebase function with the required dependencies. +3. Deploy the static assets to firebase hosting and your universal server as a Firebase function. + +If you want to preview your Angular Universal project before we deploy it as a Firebase Function you can run: + +``` +ng deploy --preview +``` + +We'll create the function and a `package.json` in your project output directory. This way, you can later run `firebase serve` in your project root so you can test everything before deploying. + ## Step 3: customization To customize the deployment flow, you can use the configuration files you're already familiar with from `firebase-tools`. You can find more in the [firebase documentation](https://firebase.google.com/docs/hosting/full-config). diff --git a/package.json b/package.json index 80f4910e8..29610a122 100644 --- a/package.json +++ b/package.json @@ -44,12 +44,16 @@ "@angular/platform-browser-dynamic": "^9.0.0-0 || ^9.0.0 || ^10.0.0-0", "@angular/router": "^9.0.0-0 || ^9.0.0 || ^10.0.0-0", "firebase": "^7.8.0", + "firebase-admin": "^8.9.2", + "firebase-functions": "^3.3.0", "firebase-tools": "^7.12.1", + "fs-extra": "^8.0.1", "fuzzy": "^0.1.3", "inquirer": "^6.2.2", "inquirer-autocomplete-prompt": "^1.0.1", "rxfire": "^3.9.7", "rxjs": "^6.5.3", + "semver": "^7.1.3", "tslib": "^1.10.0", "ws": "^7.2.1", "xhr2": "^0.1.4", @@ -72,10 +76,11 @@ "@types/jasmine": "^3.3.13", "@types/node": "^12.6.2", "@types/request": "0.0.30", + "@types/semver": "^7.1.0", "codelyzer": "^5.0.0", "concurrently": "^2.2.0", "conventional-changelog-cli": "^1.2.0", - "fs-extra": "^8.0.1", + "firebase-functions-test": "^0.1.7", "gzip-size": "^5.1.1", "jasmine": "^3.4.0", "jasmine-core": "^3.4.0", diff --git a/src/core/collection.json b/src/core/collection.json index c2a03eab2..ccbc08d77 100644 --- a/src/core/collection.json +++ b/src/core/collection.json @@ -5,9 +5,9 @@ "description": "Add firebase deploy schematic", "factory": "./schematics/public_api#ngAdd" }, - "ng-add-setup-firebase-deploy": { + "ng-add-setup-project": { "description": "Setup ng deploy", - "factory": "./schematics/public_api#setupNgDeploy" + "factory": "./schematics/public_api#ngAddSetupProject" } } } diff --git a/src/schematics/deploy/actions.jasmine.ts b/src/schematics/deploy/actions.jasmine.ts index 294cb8144..5a2cd3987 100644 --- a/src/schematics/deploy/actions.jasmine.ts +++ b/src/schematics/deploy/actions.jasmine.ts @@ -1,59 +1,79 @@ -import { JsonObject, logging } from '@angular-devkit/core'; +import {experimental, JsonObject, logging} from '@angular-devkit/core'; import { BuilderContext, BuilderRun, ScheduleOptions, Target, } from '@angular-devkit/architect'; -import { FirebaseTools, FirebaseDeployConfig } from '../interfaces'; -import deploy from './actions'; +import {FirebaseTools, FirebaseDeployConfig, BuildTarget, FSHost} from '../interfaces'; +import deploy, {deployToFunction} from './actions'; let context: BuilderContext; let firebaseMock: FirebaseTools; +let fsHost: FSHost; const FIREBASE_PROJECT = 'ikachu-aa3ef'; const PROJECT = 'pirojok-project'; -const BUILD_TARGET = `${PROJECT}:build:production`; +const BUILD_TARGET: BuildTarget = { + name: `${PROJECT}:build:production` +}; + +const projectTargets: experimental.workspace.WorkspaceTool = { + build: { + options: { + outputPath: 'dist/browser' + } + }, + server: { + options: { + outputPath: 'dist/server' + } + } +}; describe('Deploy Angular apps', () => { beforeEach(() => initMocks()); it('should call login', async () => { const spy = spyOn(firebaseMock, 'login'); - await deploy(firebaseMock, context, 'host', BUILD_TARGET, FIREBASE_PROJECT); + await deploy(firebaseMock, context, projectTargets, [BUILD_TARGET], FIREBASE_PROJECT, false, false); expect(spy).toHaveBeenCalled(); }); it('should invoke the builder', async () => { const spy = spyOn(context, 'scheduleTarget').and.callThrough(); - await deploy(firebaseMock, context, 'host', BUILD_TARGET, FIREBASE_PROJECT); + await deploy(firebaseMock, context, projectTargets, [BUILD_TARGET], FIREBASE_PROJECT, false, false); expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalledWith({ target: 'build', configuration: 'production', project: PROJECT - }); + }, undefined); }); it('should allow the buildTarget to be specified', async () => { - const buildTarget = `${PROJECT}:prerender`; + const buildTarget = { + name: `${PROJECT}:prerender`, + options: {} + }; const spy = spyOn(context, 'scheduleTarget').and.callThrough(); - await deploy(firebaseMock, context, 'host', buildTarget, FIREBASE_PROJECT); + await deploy(firebaseMock, context, projectTargets, [buildTarget], FIREBASE_PROJECT, false, false); expect(spy).toHaveBeenCalled(); - expect(spy).toHaveBeenCalledWith({ target: 'prerender', project: PROJECT }); + expect(spy).toHaveBeenCalledWith({ target: 'prerender', project: PROJECT }, {}); }); it('should invoke firebase.deploy', async () => { const spy = spyOn(firebaseMock, 'deploy').and.callThrough(); - await deploy(firebaseMock, context, 'host', BUILD_TARGET, FIREBASE_PROJECT); + await deploy(firebaseMock, context, projectTargets, [BUILD_TARGET], FIREBASE_PROJECT, false, false); expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalledWith({ - cwd: 'host', only: 'hosting:' + PROJECT + cwd: 'cwd', + only: 'hosting:' + PROJECT }); }); describe('error handling', () => { it('throws if there is no firebase project', async () => { try { - await deploy(firebaseMock, context, 'host', BUILD_TARGET) - fail(); + await deploy(firebaseMock, context, projectTargets, [BUILD_TARGET], undefined, false, false); } catch (e) { + console.log(e); expect(e.message).toMatch(/Cannot find firebase project/); } }); @@ -61,8 +81,7 @@ describe('Deploy Angular apps', () => { it('throws if there is no target project', async () => { context.target = undefined; try { - await deploy(firebaseMock, context, 'host', BUILD_TARGET, FIREBASE_PROJECT) - fail(); + await deploy(firebaseMock, context, projectTargets, [BUILD_TARGET], FIREBASE_PROJECT, false, false) } catch (e) { expect(e.message).toMatch(/Cannot execute the build target/); } @@ -70,7 +89,60 @@ describe('Deploy Angular apps', () => { }); }); +describe('universal deployment', () => { + beforeEach(() => initMocks()); + + it('should create a firebase function', async () => { + const spy = spyOn(fsHost, 'writeFileSync'); + await deployToFunction(firebaseMock, context, '/home/user', projectTargets, false, fsHost); + + expect(spy).toHaveBeenCalledTimes(2); + + const packageArgs = spy.calls.argsFor(0); + const functionArgs = spy.calls.argsFor(1); + + expect(packageArgs[0]).toBe('dist/package.json'); + expect(functionArgs[0]).toBe('dist/index.js'); + }); + + it('should rename the index.html file in the nested dist', async () => { + const spy = spyOn(fsHost, 'renameSync'); + await deployToFunction(firebaseMock, context, '/home/user', projectTargets, false, fsHost); + + expect(spy).toHaveBeenCalledTimes(1); + + const packageArgs = spy.calls.argsFor(0); + + expect(packageArgs).toEqual([ + 'dist/dist/browser/index.html', + 'dist/dist/browser/index.original.html' + ]); + }); + + it('should invoke firebase.deploy', async () => { + const spy = spyOn(firebaseMock, 'deploy'); + await deployToFunction(firebaseMock, context, '/home/user', projectTargets, false, fsHost); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should not deploy if the command is invoked with --preview', async () => { + const spy = spyOn(firebaseMock, 'deploy'); + await deployToFunction(firebaseMock, context, '/home/user', projectTargets, true, fsHost); + expect(spy).not.toHaveBeenCalled(); + }); +}); + const initMocks = () => { + fsHost = { + moveSync(_: string, __: string) { + }, + renameSync(_: string, __: string) { + }, + writeFileSync(_: string, __: string) { + } + }; + firebaseMock = { login: () => Promise.resolve(), projects: { diff --git a/src/schematics/deploy/actions.ts b/src/schematics/deploy/actions.ts index 44ae23151..13489bff2 100644 --- a/src/schematics/deploy/actions.ts +++ b/src/schematics/deploy/actions.ts @@ -1,17 +1,147 @@ -import { BuilderContext, targetFromTargetString } from "@angular-devkit/architect"; -import { FirebaseTools } from "../interfaces"; +import { + BuilderContext, + targetFromTargetString +} from "@angular-devkit/architect"; +import { BuildTarget, FirebaseTools, FSHost } from "../interfaces"; +import { writeFileSync, renameSync, existsSync, readFileSync } from "fs"; +import { copySync, removeSync } from "fs-extra"; +import { join, dirname } from "path"; +import { + defaultFunction, + defaultPackage, + NodeVersion +} from "./functions-templates"; +import { experimental } from "@angular-devkit/core"; +import { SchematicsException } from "@angular-devkit/schematics"; +import { satisfies } from "semver"; -export default async function deploy( +const moveSync = (src: string, dest: string) => { + copySync(src, dest); + removeSync(src); +}; + +const deployToHosting = ( firebaseTools: FirebaseTools, context: BuilderContext, - projectRoot: string, - buildTarget: string, - firebaseProject?: string, -) { - if (!firebaseProject) { - throw new Error("Cannot find firebase project for your app in .firebaserc"); + workspaceRoot: string +) => { + return firebaseTools.deploy({ + // tslint:disable-next-line:no-non-null-assertion + only: "hosting:" + context.target!.project, + cwd: workspaceRoot + }); +}; + +const defaultFsHost: FSHost = { + moveSync, + writeFileSync, + renameSync +}; + +const getVersionRange = (v: number) => `^${v}.0.0`; + +const getPackageJson = (workspaceRoot: string) => { + const versions = { + 'firebase-admin': 'latest', + 'firebase-functions': 'latest', + 'firebase-functions-test': 'latest' + }; + if (existsSync(join(workspaceRoot, 'package.json'))) { + try { + const content = JSON.parse(readFileSync(join(workspaceRoot, 'package.json')).toString()); + Object.keys(versions).forEach((p: string) => { + versions[p] = content.devDependencies[p] || content.dependencies[p] || versions[p]; + }); + } catch {} + } + return defaultPackage(versions["firebase-admin"], versions["firebase-functions"], versions["firebase-functions-test"]); +}; + +export const deployToFunction = async ( + firebaseTools: FirebaseTools, + context: BuilderContext, + workspaceRoot: string, + project: experimental.workspace.WorkspaceTool, + preview: boolean, + fsHost: FSHost = defaultFsHost +) => { + if (!satisfies(process.versions.node, getVersionRange(NodeVersion))) { + context.logger.warn( + `⚠️ Your Node.js version (${process.versions.node}) does not match the Firebase Functions runtime (${NodeVersion}).` + ); + } + + if ( + !project || + !project.build || + !project.build.options || + !project.build.options.outputPath + ) { + throw new SchematicsException( + `Cannot read the output path (architect.build.options.outputPath) of the Angular project in angular.json` + ); + } + + if ( + !project || + !project.server || + !project.server.options || + !project.server.options.outputPath + ) { + throw new SchematicsException( + `Cannot read the output path (architect.server.options.outputPath) of the Angular project in angular.json` + ); + } + + const staticOut = project.build.options.outputPath; + const serverOut = project.server.options.outputPath; + const newClientPath = join(dirname(staticOut), staticOut); + const newServerPath = join(dirname(serverOut), serverOut); + + // This is needed because in the server output there's a hardcoded dependency on $cwd/dist/browser, + // This assumes that we've deployed our application dist directory and we're running the server + // in the parent directory. To have this precondition, we move dist/browser to dist/dist/browser + // since the firebase function runs the server from dist. + fsHost.moveSync(staticOut, newClientPath); + fsHost.moveSync(serverOut, newServerPath); + + fsHost.writeFileSync( + join(dirname(serverOut), "package.json"), + getPackageJson(workspaceRoot) + ); + fsHost.writeFileSync( + join(dirname(serverOut), "index.js"), + defaultFunction(serverOut) + ); + + fsHost.renameSync( + join(newClientPath, "index.html"), + join(newClientPath, "index.original.html") + ); + + context.logger.info("Deploying your Angular Universal application..."); + + if (preview) { + context.logger.info( + "Your Universal application is now ready for preview. Use `firebase serve` in the output directory of your workspace to test the setup." + ); + return Promise.resolve(); + } else { + return firebaseTools.deploy({ + cwd: workspaceRoot + }); } +}; +export default async function deploy( + firebaseTools: FirebaseTools, + context: BuilderContext, + projectTargets: experimental.workspace.WorkspaceTool, + buildTargets: BuildTarget[], + firebaseProject: string, + ssr: boolean, + preview: boolean +) { await firebaseTools.login(); if (!context.target) { @@ -20,8 +150,13 @@ export default async function deploy( context.logger.info(`📦 Building "${context.target.project}"`); - const run = await context.scheduleTarget(targetFromTargetString(buildTarget)); - await run.result; + for (const target of buildTargets) { + const run = await context.scheduleTarget( + targetFromTargetString(target.name), + target.options + ); + await run.result; + } try { await firebaseTools.use(firebaseProject, { project: firebaseProject }); @@ -30,16 +165,32 @@ export default async function deploy( } try { - const success = await firebaseTools.deploy({ - only: "hosting:" + context.target.project, - cwd: projectRoot - }); - context.logger.info( - `🚀 Your application is now available at https://${ - success.hosting.split("/")[1] - }.firebaseapp.com/` - ); + let success: { hosting: string }; + + if (ssr) { + success = await deployToFunction( + firebaseTools, + context, + context.workspaceRoot, + projectTargets, + preview + ); + } else { + success = await deployToHosting( + firebaseTools, + context, + context.workspaceRoot + ); + } + + if (!preview) { + context.logger.info( + `🚀 Your application is now available at https://${ + success.hosting.split("/")[1] + }.firebaseapp.com/` + ); + } } catch (e) { - context.logger.error(e); + context.logger.error(e.message || e); } } diff --git a/src/schematics/deploy/builder.ts b/src/schematics/deploy/builder.ts index 667c4ae23..19d328bdb 100644 --- a/src/schematics/deploy/builder.ts +++ b/src/schematics/deploy/builder.ts @@ -2,13 +2,12 @@ import { BuilderContext, BuilderOutput, createBuilder -} from "@angular-devkit/architect"; -import { NodeJsSyncHost } from "@angular-devkit/core/node"; -import deploy from "./actions"; -import { experimental, normalize, json } from "@angular-devkit/core"; -import { DeployBuilderSchema } from '../interfaces'; -import * as path from "path"; -import { getFirebaseProjectName } from "../utils"; +} from '@angular-devkit/architect'; +import { NodeJsSyncHost } from '@angular-devkit/core/node'; +import deploy from './actions'; +import { experimental, normalize, json } from '@angular-devkit/core'; +import {BuildTarget, DeployBuilderSchema} from '../interfaces'; +import { getFirebaseProjectName } from '../utils'; type DeployBuilderOptions = DeployBuilderSchema & json.JsonObject; @@ -23,32 +22,50 @@ export default createBuilder( new NodeJsSyncHost() ); await workspace - .loadWorkspaceFromHost(normalize("angular.json")) + .loadWorkspaceFromHost(normalize('angular.json')) .toPromise(); if (!context.target) { - throw new Error("Cannot deploy the application without a target"); + throw new Error('Cannot deploy the application without a target'); } - const project = workspace.getProject(context.target.project); + const projectTargets = workspace.getProjectTargets(context.target.project); const firebaseProject = getFirebaseProjectName( context.workspaceRoot, context.target.project ); + if (!firebaseProject) { + throw new Error('Cannot find firebase project for your app in .firebaserc'); + } + const buildTarget = options.buildTarget || `${context.target.project}:build:production`; + const targets: BuildTarget[] = [{ + name: buildTarget + }]; + if (options.ssr) { + targets.push({ + name: options.universalBuildTarget || `${context.target.project}:server:production`, + options: { + bundleDependencies: 'all' + } + }); + } + try { await deploy( - require("firebase-tools"), + require('firebase-tools'), context, - path.join(context.workspaceRoot, project.root), - buildTarget, - firebaseProject + projectTargets, + targets, + firebaseProject, + !!options.ssr, + !!options.preview ); } catch (e) { - console.error("Error when trying to deploy: "); + console.error('Error when trying to deploy: '); console.error(e.message); return { success: false }; } diff --git a/src/schematics/deploy/functions-templates.ts b/src/schematics/deploy/functions-templates.ts new file mode 100644 index 000000000..47a7f2b18 --- /dev/null +++ b/src/schematics/deploy/functions-templates.ts @@ -0,0 +1,37 @@ +export const NodeVersion = 10; + +export const defaultPackage = ( + firebaseAdminVersion: string, + firebaseFunctionsVersion: string, + firebaseFunctionsTestVersion: string +) => `{ + "name": "functions", + "description": "Angular Universal Application", + "scripts": { + "lint": "", + "serve": "firebase serve --only functions", + "shell": "firebase functions:shell", + "start": "npm run shell", + "deploy": "firebase deploy --only functions", + "logs": "firebase functions:log" + }, + "engines": { + "node": "${NodeVersion}" + }, + "dependencies": { + "firebase-admin": "${firebaseAdminVersion}", + "firebase-functions": "${firebaseFunctionsVersion}" + }, + "devDependencies": { + "firebase-functions-test": "${firebaseFunctionsTestVersion}" + }, + "private": true +} +`; + +export const defaultFunction = ( + path: string +) => `const functions = require('firebase-functions'); + +exports.ssr = functions.https.onRequest(require('./${path}/main').app()); +`; diff --git a/src/schematics/deploy/schema.json b/src/schematics/deploy/schema.json index bcc2e088b..a48deed07 100644 --- a/src/schematics/deploy/schema.json +++ b/src/schematics/deploy/schema.json @@ -8,6 +8,10 @@ "type": "string", "description": "Target to build.", "pattern": "^[^:\\s]+:[^:\\s]+(:[^\\s]+)?$" + }, + "preview": { + "type": "boolean", + "description": "Do not deploy the application, just set up the Firebase Function in the project output directory. Can be used for testing the Firebase Function with `firebase serve`." } } } diff --git a/src/schematics/interfaces.ts b/src/schematics/interfaces.ts index 6fc6642fa..20dad60f3 100644 --- a/src/schematics/interfaces.ts +++ b/src/schematics/interfaces.ts @@ -3,7 +3,7 @@ export interface Project { projectNumber: string; displayName: string; name: string; - resources: { [key:string]: string } + resources: { [key: string]: string }; } export interface FirebaseDeployConfig { @@ -12,11 +12,11 @@ export interface FirebaseDeployConfig { } export interface FirebaseTools { - login(): Promise; - projects: { list(): Promise; - } + }; + + login(): Promise; deploy(config: FirebaseDeployConfig): Promise; @@ -25,11 +25,12 @@ export interface FirebaseTools { export interface FirebaseHostingRewrite { source: string; - destination: string; + destination?: string; + function?: string; } export interface FirebaseHostingConfig { - public: string; + public?: string; ignore: string[]; target: string; rewrites: FirebaseHostingRewrite[]; @@ -49,4 +50,18 @@ export interface FirebaseRc { export interface DeployBuilderSchema { buildTarget?: string; + preview?: boolean; + universalBuildTarget?: string; + ssr?: boolean; +} + +export interface BuildTarget { + name: string; + options?: {[name: string]: any}; +} + +export interface FSHost { + moveSync(src: string, dest: string): void; + writeFileSync(src: string, data: string): void; + renameSync(src: string, dest: string): void; } diff --git a/src/schematics/ng-add-common.ts b/src/schematics/ng-add-common.ts new file mode 100644 index 000000000..12900bcbc --- /dev/null +++ b/src/schematics/ng-add-common.ts @@ -0,0 +1,107 @@ +import { SchematicsException, Tree } from '@angular-devkit/schematics'; + +import { FirebaseRc } from './interfaces'; + +export interface NgAddOptions { + firebaseProject: string; + project?: string; +} + +export interface NgAddNormalizedOptions { + firebaseProject: string; + project: string; +} + +export interface DeployOptions { + project: string; +} + +export const stringifyFormatted = (obj: any) => JSON.stringify(obj, null, 2); + +export const overwriteIfExists = ( + tree: Tree, + path: string, + content: string +) => { + if (tree.exists(path)) { + tree.overwrite(path, content); + } else { + tree.create(path, content); + } +}; + +function emptyFirebaseRc() { + return { + targets: {} + }; +} + +function generateFirebaseRcTarget(firebaseProject: string, project: string) { + return { + hosting: { + [project]: [ + // TODO(kirjs): Generally site name is consistent with the project name, but there are edge cases. + firebaseProject + ] + } + }; +} + +export function generateFirebaseRc( + tree: Tree, + path: string, + firebaseProject: string, + project: string +) { + const firebaseRc: FirebaseRc = tree.exists(path) + ? safeReadJSON(path, tree) + : emptyFirebaseRc(); + + firebaseRc.targets = firebaseRc.targets || {}; + + if (firebaseProject in firebaseRc.targets) { + throw new SchematicsException( + `Firebase project ${firebaseProject} already defined in .firebaserc` + ); + } + + firebaseRc.targets[firebaseProject] = generateFirebaseRcTarget( + firebaseProject, + project + ); + + overwriteIfExists(tree, path, stringifyFormatted(firebaseRc)); +} + +export function safeReadJSON(path: string, tree: Tree) { + try { + return JSON.parse(tree.read(path)!.toString()); + } catch (e) { + throw new SchematicsException(`Error when parsing ${path}: ${e.message}`); + } +} + +export const addDependencies = ( + host: Tree, + deps: { [name: string]: { dev?: boolean, version: string } }, +) => { + const packageJson = + host.exists('package.json') && safeReadJSON('package.json', host); + + if (packageJson === undefined) { + throw new SchematicsException('Could not locate package.json'); + } + + Object.keys(deps).forEach(depName => { + const dep = deps[depName]; + if (dep.dev) { + packageJson.devDependencies[depName] = + packageJson.devDependencies[depName] || dep.version; + } else { + packageJson.dependencies[depName] = + packageJson.dependencies[depName] || deps.version; + } + }); + + overwriteIfExists(host, 'package.json', stringifyFormatted(packageJson)); +}; diff --git a/src/schematics/ng-add-ssr.ts b/src/schematics/ng-add-ssr.ts new file mode 100644 index 000000000..13f3b238a --- /dev/null +++ b/src/schematics/ng-add-ssr.ts @@ -0,0 +1,138 @@ +import { SchematicsException, Tree } from '@angular-devkit/schematics'; +import { experimental } from '@angular-devkit/core'; +import { + generateFirebaseRc, + safeReadJSON, + overwriteIfExists, + stringifyFormatted, + addDependencies, NgAddNormalizedOptions +} from './ng-add-common'; +import { FirebaseJSON, FirebaseHostingConfig } from './interfaces'; + +import { default as defaultDependencies, firebaseFunctions as firebaseFunctionsDependencies } from './versions.json'; +import {dirname, join} from 'path'; + +// We consider a project to be a universal project if it has a `server` architect +// target. If it does, it knows how to build the application's server. +export const isUniversalApp = ( + project: experimental.workspace.WorkspaceProject +) => project.architect && project.architect.server; + +function emptyFirebaseJson(source: string) { + return { + hosting: [], + functions: { + source + } + }; +} + +function generateHostingConfig(project: string, dist: string) { + return { + target: project, + public: join(dirname(dist), dist), + ignore: ['firebase.json', '**/.*', '**/node_modules/**'], + rewrites: [ + { + source: '**', + function: 'ssr' + } + ] + }; +} + +export function generateFirebaseJson( + tree: Tree, + path: string, + project: string, + dist: string, + serverOutput: string +) { + const firebaseJson: FirebaseJSON = tree.exists(path) + ? safeReadJSON(path, tree) + : emptyFirebaseJson(dirname(serverOutput)); + + if ( + firebaseJson.hosting && + ((Array.isArray(firebaseJson.hosting) && + firebaseJson.hosting.find(config => config.target === project)) || + (firebaseJson.hosting).target === project) + ) { + throw new SchematicsException( + `Target ${project} already exists in firebase.json` + ); + } + + const newConfig = generateHostingConfig(project, dist); + if (firebaseJson.hosting === undefined) { + firebaseJson.hosting = newConfig; + } else if (Array.isArray(firebaseJson.hosting)) { + firebaseJson.hosting.push(newConfig); + } else { + firebaseJson.hosting = [firebaseJson.hosting!, newConfig]; + } + + overwriteIfExists(tree, path, stringifyFormatted(firebaseJson)); +} + +export const addFirebaseFunctionsDependencies = (tree: Tree) => { + addDependencies( + tree, + {...defaultDependencies, ...firebaseFunctionsDependencies}, + ); +}; + +export const setupUniversalDeployment = (config: { + project: experimental.workspace.WorkspaceProject; + options: NgAddNormalizedOptions; + workspacePath: string; + workspace: experimental.workspace.WorkspaceSchema; + tree: Tree; +}) => { + const { tree, workspacePath, workspace, options } = config; + const project = workspace.projects[options.project]; + + if ( + !project.architect || + !project.architect.build || + !project.architect.build.options || + !project.architect.build.options.outputPath + ) { + throw new SchematicsException( + `Cannot read the output path (architect.build.options.outputPath) of the Angular project "${options.project}" in angular.json` + ); + } + + if ( + !project.architect || + !project.architect.server || + !project.architect.server.options || + !project.architect.server.options.outputPath + ) { + throw new SchematicsException( + `Cannot read the output path (architect.server.options.outputPath) of the Angular project "${options.project}" in angular.json` + ); + } + + const staticOutput = project.architect.build.options.outputPath; + const serverOutput = project.architect.server.options.outputPath; + + project.architect.deploy = { + builder: '@angular/fire:deploy', + options: { + ssr: true + } + }; + + tree.overwrite(workspacePath, JSON.stringify(workspace, null, 2)); + + generateFirebaseJson(tree, 'firebase.json', options.project, staticOutput, serverOutput); + generateFirebaseRc( + tree, + '.firebaserc', + options.firebaseProject, + options.project + ); + + return tree; +}; diff --git a/src/schematics/ng-add-static.ts b/src/schematics/ng-add-static.ts new file mode 100644 index 000000000..b8ca7442c --- /dev/null +++ b/src/schematics/ng-add-static.ts @@ -0,0 +1,114 @@ +import { SchematicsException, Tree } from '@angular-devkit/schematics'; +import { experimental } from '@angular-devkit/core'; +import { + generateFirebaseRc, + overwriteIfExists, + safeReadJSON, + stringifyFormatted, + addDependencies, NgAddNormalizedOptions +} from './ng-add-common'; +import { FirebaseJSON, FirebaseHostingConfig } from './interfaces'; + +import { + default as defaultDependencies +} from './versions.json'; + +function emptyFirebaseJson() { + return { + hosting: [] + }; +} + +function generateHostingConfig(project: string, dist: string) { + return { + target: project, + public: dist, + ignore: ['firebase.json', '**/.*', '**/node_modules/**'], + rewrites: [ + { + source: '**', + destination: '/index.html' + } + ] + }; +} + +export function generateFirebaseJson( + tree: Tree, + path: string, + project: string, + dist: string +) { + const firebaseJson: FirebaseJSON = tree.exists(path) + ? safeReadJSON(path, tree) + : emptyFirebaseJson(); + + if ( + firebaseJson.hosting && + ((Array.isArray(firebaseJson.hosting) && + firebaseJson.hosting.find(config => config.target === project)) || + (firebaseJson.hosting as FirebaseHostingConfig).target === project) + ) { + throw new SchematicsException( + `Target ${project} already exists in firebase.json` + ); + } + + const newConfig = generateHostingConfig(project, dist); + if (firebaseJson.hosting === undefined) { + firebaseJson.hosting = newConfig; + } else if (Array.isArray(firebaseJson.hosting)) { + firebaseJson.hosting.push(newConfig); + } else { + firebaseJson.hosting = [firebaseJson.hosting, newConfig]; + } + + overwriteIfExists(tree, path, stringifyFormatted(firebaseJson)); +} + +export const addFirebaseHostingDependencies = (tree: Tree) => { + addDependencies( + tree, + defaultDependencies + ); +}; + +export const setupStaticDeployment = (config: { + project: experimental.workspace.WorkspaceProject; + options: NgAddNormalizedOptions; + workspacePath: string; + workspace: experimental.workspace.WorkspaceSchema; + tree: Tree; +}) => { + const { tree, workspacePath, workspace, options } = config; + const project = workspace.projects[options.project]; + + if ( + !project.architect || + !project.architect.build || + !project.architect.build.options || + !project.architect.build.options.outputPath + ) { + throw new SchematicsException( + `Cannot read the output path (architect.build.options.outputPath) of the Angular project "${options.project}" in angular.json` + ); + } + + const outputPath = project.architect.build.options.outputPath; + + project.architect.deploy = { + builder: '@angular/fire:deploy', + options: {} + }; + + tree.overwrite(workspacePath, JSON.stringify(workspace, null, 2)); + generateFirebaseJson(tree, 'firebase.json', options.project, outputPath); + generateFirebaseRc( + tree, + '.firebaserc', + options.firebaseProject, + options.project + ); + + return tree; +}; diff --git a/src/schematics/ng-add.jasmine.ts b/src/schematics/ng-add.jasmine.ts index 437e73731..f8f68b1ad 100644 --- a/src/schematics/ng-add.jasmine.ts +++ b/src/schematics/ng-add.jasmine.ts @@ -1,196 +1,266 @@ -import { Tree } from "@angular-devkit/schematics"; -import { setupFirebaseProject } from "./ng-add"; +import {Tree} from '@angular-devkit/schematics'; +import {setupProject} from './ng-add'; -const PROJECT_NAME = "pie-ka-chu"; -const PROJECT_ROOT = "pirojok"; -const FIREBASE_PROJECT = "pirojok-111e3"; +const PROJECT_NAME = 'pie-ka-chu'; +const PROJECT_ROOT = 'pirojok'; +const FIREBASE_PROJECT = 'pirojok-111e3'; -const OTHER_PROJECT_NAME = "pi-catch-you"; -const OTHER_FIREBASE_PROJECT_NAME = "bi-catch-you-77e7e"; +const OTHER_PROJECT_NAME = 'pi-catch-you'; +const OTHER_FIREBASE_PROJECT_NAME = 'bi-catch-you-77e7e'; -describe("ng-add", () => { - describe("generating files", () => { +describe('ng-add', () => { + describe('generating files', () => { let tree: Tree; beforeEach(() => { tree = Tree.empty(); - tree.create("angular.json", JSON.stringify(generateAngularJson())); + tree.create('angular.json', JSON.stringify(generateAngularJson())); }); it('generates new files if starting from scratch', async () => { - const result = setupFirebaseProject(tree, {firebaseProject: FIREBASE_PROJECT, project: PROJECT_NAME}); + const result = await setupProject(tree, { + firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, + project: PROJECT_NAME + }); expect(result.read('firebase.json')!.toString()).toEqual(initialFirebaseJson); expect(result.read('.firebaserc')!.toString()).toEqual(initialFirebaserc); expect(result.read('angular.json')!.toString()).toEqual(initialAngularJson); }); it('uses default project', async () => { - const result = setupFirebaseProject(tree, {firebaseProject: FIREBASE_PROJECT}); + const result = await setupProject(tree, { + firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, + project: undefined + }); expect(result.read('firebase.json')!.toString()).toEqual(overwriteFirebaseJson); expect(result.read('.firebaserc')!.toString()).toEqual(overwriteFirebaserc); expect(result.read('angular.json')!.toString()).toEqual(overwriteAngularJson); }); it('overrides existing files', async () => { - const tempTree = setupFirebaseProject(tree, {firebaseProject: FIREBASE_PROJECT, project: PROJECT_NAME}); - const result = setupFirebaseProject(tempTree, {firebaseProject: OTHER_FIREBASE_PROJECT_NAME, project: OTHER_PROJECT_NAME}); + const tempTree = await setupProject(tree, { + firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME + }); + const result = await setupProject(tempTree, { + firebaseProject: OTHER_FIREBASE_PROJECT_NAME, + project: OTHER_PROJECT_NAME, + isUniversalProject: false + }); expect(result.read('firebase.json')!.toString()).toEqual(projectFirebaseJson); expect(result.read('.firebaserc')!.toString()).toEqual(projectFirebaserc); expect(result.read('angular.json')!.toString()).toEqual(projectAngularJson); }); }); - describe("error handling", () => { - it("fails if project not defined", () => { + describe('error handling', () => { + it('fails if project not defined', () => { const tree = Tree.empty(); const angularJSON = generateAngularJson(); delete angularJSON.defaultProject; - tree.create("angular.json", JSON.stringify(angularJSON)); + tree.create('angular.json', JSON.stringify(angularJSON)); expect(() => - setupFirebaseProject(tree, { + setupProject(tree, { firebaseProject: FIREBASE_PROJECT, - project: "" + isUniversalProject: false, + project: undefined }) ).toThrowError( /No Angular project selected and no default project in the workspace/ ); }); - it("Should throw if angular.json not found", async () => { + it('Should throw if angular.json not found', async () => { expect(() => - setupFirebaseProject(Tree.empty(), { + setupProject(Tree.empty(), { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }) ).toThrowError(/Could not find angular.json/); }); - it("Should throw if angular.json can not be parsed", async () => { + it('Should throw if angular.json can not be parsed', async () => { const tree = Tree.empty(); - tree.create("angular.json", "hi"); + tree.create('angular.json', 'hi'); expect(() => - setupFirebaseProject(tree, { + setupProject(tree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }) ).toThrowError(/Could not parse angular.json/); }); - it("Should throw if specified project does not exist ", async () => { + it('Should throw if specified project does not exist ', async () => { const tree = Tree.empty(); - tree.create("angular.json", JSON.stringify({ projects: {} })); + tree.create('angular.json', JSON.stringify({projects: {}})); expect(() => - setupFirebaseProject(tree, { + setupProject(tree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }) ).toThrowError(/The specified Angular project is not defined in this workspace/); }); - it("Should throw if specified project is not application", async () => { + it('Should throw if specified project is not application', async () => { const tree = Tree.empty(); tree.create( - "angular.json", + 'angular.json', JSON.stringify({ - projects: { [PROJECT_NAME]: { projectType: "pokemon" } } + projects: {[PROJECT_NAME]: {projectType: 'pokemon'}} }) ); expect(() => - setupFirebaseProject(tree, { + setupProject(tree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }) ).toThrowError(/Deploy requires an Angular project type of "application" in angular.json/); }); - it("Should throw if app does not have architect configured", async () => { + it('Should throw if app does not have architect configured', async () => { const tree = Tree.empty(); tree.create( - "angular.json", + 'angular.json', JSON.stringify({ - projects: { [PROJECT_NAME]: { projectType: "application" } } + projects: {[PROJECT_NAME]: {projectType: 'application'}} }) ); expect(() => - setupFirebaseProject(tree, { + setupProject(tree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }) ).toThrowError(/Cannot read the output path/); }); - it("Should throw if firebase.json has the project already", async () => { + it('Should throw if firebase.json has the project already', async () => { const tree = Tree.empty(); - tree.create("angular.json", JSON.stringify(generateAngularJson())); - const tempTree = setupFirebaseProject(tree, { + tree.create('angular.json', JSON.stringify(generateAngularJson())); + const tempTree = await setupProject(tree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }); expect(() => - setupFirebaseProject(tempTree, { + setupProject(tempTree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }) ).toThrowError(/already exists in firebase.json/); }); - it("Should throw if firebase.json is broken", async () => { + it('Should throw if firebase.json is broken', async () => { const tree = Tree.empty(); - tree.create("angular.json", JSON.stringify(generateAngularJson())); - tree.create("firebase.json", "I'm broken 😔"); + tree.create('angular.json', JSON.stringify(generateAngularJson())); + tree.create('firebase.json', `I'm broken 😔`); expect(() => - setupFirebaseProject(tree, { + setupProject(tree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }) ).toThrowError(/firebase.json: Unexpected token/); }); - it("Should throw if .firebaserc is broken", async () => { + it('Should throw if .firebaserc is broken', async () => { const tree = Tree.empty(); - tree.create("angular.json", JSON.stringify(generateAngularJson())); - tree.create(".firebaserc", "I'm broken 😔"); + tree.create('angular.json', JSON.stringify(generateAngularJson())); + tree.create('.firebaserc', `I'm broken 😔`); expect(() => - setupFirebaseProject(tree, { + setupProject(tree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }) ).toThrowError(/.firebaserc: Unexpected token/); }); - it("Should throw if firebase.json has the project already", async () => { + it('Should throw if firebase.json has the project already', async () => { const tree = Tree.empty(); - tree.create("angular.json", JSON.stringify(generateAngularJson())); - const tempTree = setupFirebaseProject(tree, { + tree.create('angular.json', JSON.stringify(generateAngularJson())); + const tempTree = await setupProject(tree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }); expect(() => - setupFirebaseProject(tempTree, { + setupProject(tempTree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: OTHER_PROJECT_NAME }) ).toThrowError(/ already defined in .firebaserc/); }); - it("Should throw if firebase.json is broken", async () => { + it('Should throw if firebase.json is broken', async () => { const tree = Tree.empty(); - tree.create("angular.json", JSON.stringify(generateAngularJson())); + tree.create('angular.json', JSON.stringify(generateAngularJson())); - const tempTree = setupFirebaseProject(tree, { + const tempTree = await setupProject(tree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: PROJECT_NAME }); expect(() => - setupFirebaseProject(tempTree, { + setupProject(tempTree, { firebaseProject: FIREBASE_PROJECT, + isUniversalProject: false, project: OTHER_PROJECT_NAME }) ).toThrowError(/ already defined in .firebaserc/); }); + + describe('universal app', () => { + it('should fail without a server project', async () => { + const tree = Tree.empty(); + tree.create('angular.json', JSON.stringify(generateAngularJson())); + + expect(() => setupProject(tree, { + firebaseProject: FIREBASE_PROJECT, + isUniversalProject: true, + project: PROJECT_NAME + })).toThrowError(/\(architect.server.options.outputPath\) of the Angular project "pie-ka-chu" in angular.json/); + }); + + it('should add a @angular/fire builder', async () => { + const tree = Tree.empty(); + tree.create('angular.json', JSON.stringify(generateAngularJsonWithServer())); + + const result = await setupProject(tree, { + firebaseProject: FIREBASE_PROJECT, + isUniversalProject: true, + project: PROJECT_NAME + }); + + const workspace = JSON.parse((await result.read('angular.json'))!.toString()); + expect(workspace.projects['pie-ka-chu'].architect.deploy.options.ssr).toBeTrue(); + }); + + it('should configure firebase.json', async () => { + const tree = Tree.empty(); + tree.create('angular.json', JSON.stringify(generateAngularJsonWithServer())); + + const result = await setupProject(tree, { + firebaseProject: FIREBASE_PROJECT, + isUniversalProject: true, + project: PROJECT_NAME + }); + + const firebaseJson = JSON.parse((await result.read('firebase.json'))!.toString()); + expect(firebaseJson).toEqual(universalFirebaseJson); + }); + }); }); }); @@ -199,23 +269,63 @@ function generateAngularJson() { defaultProject: PROJECT_NAME, projects: { [PROJECT_NAME]: { - projectType: "application", + projectType: 'application', + root: PROJECT_ROOT, + architect: { + build: { + options: { + outputPath: 'dist/ikachu' + } + } + } + }, + [OTHER_PROJECT_NAME]: { + projectType: 'application', + root: PROJECT_ROOT, + architect: { + build: { + options: { + outputPath: 'dist/ikachu' + } + } + } + } + } + }; +} + +function generateAngularJsonWithServer() { + return { + defaultProject: PROJECT_NAME, + projects: { + [PROJECT_NAME]: { + projectType: 'application', root: PROJECT_ROOT, architect: { build: { options: { - outputPath: "dist/ikachu" + outputPath: 'dist/ikachu' + } + }, + server: { + options: { + outputPath: 'dist/server' } } } }, [OTHER_PROJECT_NAME]: { - projectType: "application", + projectType: 'application', root: PROJECT_ROOT, architect: { build: { options: { - outputPath: "dist/ikachu" + outputPath: 'dist/ikachu' + } + }, + server: { + options: { + outputPath: 'dist/server' } } } @@ -441,3 +551,24 @@ const projectAngularJson = `{ } } }`; + +const universalFirebaseJson = { + hosting: [{ + target: 'pie-ka-chu', + public: 'dist/dist/ikachu', + ignore: [ + 'firebase.json', + '**/.*', + '**/node_modules/**' + ], + rewrites: [ + { + source: '**', + function: 'ssr' + } + ] + }], + functions: { + source: 'dist' + } +}; diff --git a/src/schematics/ng-add.ts b/src/schematics/ng-add.ts index 8d18ce8f1..de9136b13 100644 --- a/src/schematics/ng-add.ts +++ b/src/schematics/ng-add.ts @@ -1,128 +1,32 @@ -import { SchematicsException, Tree, SchematicContext } from '@angular-devkit/schematics'; -import { NodePackageInstallTask, RunSchematicTask } from '@angular-devkit/schematics/tasks'; -import { FirebaseJSON, FirebaseRc, FirebaseHostingConfig } from './interfaces'; +import { + SchematicsException, + Tree, + SchematicContext, + chain, + mergeWith +} from '@angular-devkit/schematics'; +import { + NodePackageInstallTask, + RunSchematicTask +} from '@angular-devkit/schematics/tasks'; import { experimental, JsonParseMode, parseJson } from '@angular-devkit/core'; -import { from } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; -import { Project } from './interfaces'; -import { listProjects, projectPrompt } from './utils'; -import requiredDependencies from './versions.json'; - -const stringifyFormatted = (obj: any) => JSON.stringify(obj, null, 2); - -function emptyFirebaseJson() { - return { - hosting: [] - }; -} - -function emptyFirebaseRc() { - return { - targets: {} - }; -} - -function generateHostingConfig(project: string, dist: string) { - return { - target: project, - public: dist, - ignore: ['firebase.json', '**/.*', '**/node_modules/**'], - rewrites: [ - { - source: '**', - destination: '/index.html' - } - ] - }; -} - -function safeReadJSON(path: string, tree: Tree) { - try { - return JSON.parse(tree.read(path)!.toString()); - } catch (e) { - throw new SchematicsException(`Error when parsing ${path}: ${e.message}`); - } -} - -function generateFirebaseJson( - tree: Tree, - path: string, - project: string, - dist: string -) { - let firebaseJson: FirebaseJSON = tree.exists(path) - ? safeReadJSON(path, tree) - : emptyFirebaseJson(); - - if (firebaseJson.hosting && - (Array.isArray(firebaseJson.hosting) && - firebaseJson.hosting.find(config => config.target === project) || - (firebaseJson.hosting).target === project - )) { - throw new SchematicsException( - `Target ${project} already exists in firebase.json` - ); - } - - const newConfig = generateHostingConfig(project, dist); - if (firebaseJson.hosting === undefined) { - firebaseJson.hosting = newConfig; - } else if (Array.isArray(firebaseJson.hosting)) { - firebaseJson.hosting.push(newConfig); - } else { - firebaseJson.hosting = [firebaseJson.hosting!, newConfig]; - } - - overwriteIfExists(tree, path, stringifyFormatted(firebaseJson)); -} - -function generateFirebaseRcTarget(firebaseProject: string, project: string) { - return { - hosting: { - [project]: [ - // TODO(kirjs): Generally site name is consistent with the project name, but there are edge cases. - firebaseProject - ] - } - }; -} - -function generateFirebaseRc( - tree: Tree, - path: string, - firebaseProject: string, - project: string -) { - const firebaseRc: FirebaseRc = tree.exists(path) - ? safeReadJSON(path, tree) - : emptyFirebaseRc(); - - firebaseRc.targets = firebaseRc.targets || {}; - - if (firebaseProject in firebaseRc.targets!) { - throw new SchematicsException( - `Firebase project ${firebaseProject} already defined in .firebaserc` - ); - } - - firebaseRc.targets[firebaseProject] = generateFirebaseRcTarget( - firebaseProject, - project - ); - - overwriteIfExists(tree, path, stringifyFormatted(firebaseRc)); -} - -const overwriteIfExists = (tree: Tree, path: string, content: string) => { - if (tree.exists(path)) tree.overwrite(path, content); - else tree.create(path, content); -}; +import { listProjects, projectPrompt, projectTypePrompt } from './utils'; + +import {DeployOptions, NgAddNormalizedOptions} from './ng-add-common'; +import { + setupUniversalDeployment, + addFirebaseFunctionsDependencies +} from './ng-add-ssr'; +import { + setupStaticDeployment, + addFirebaseHostingDependencies +} from './ng-add-static'; function getWorkspace( host: Tree ): { path: string; workspace: experimental.workspace.WorkspaceSchema } { const possibleFiles = ['/angular.json', '/.angular.json']; - const path = possibleFiles.filter(path => host.exists(path))[0]; + const path = possibleFiles.filter(p => host.exists(p))[0]; const configBuffer = host.read(path); if (configBuffer === null) { @@ -146,57 +50,17 @@ function getWorkspace( }; } -interface NgAddOptions { - firebaseProject: string; - project?: string; -} - -interface DeployOptions { - project: string; -} - -// You don't have to export the function as default. You can also have more than one rule factory -// per file. -export const setupNgDeploy = ({ project }: DeployOptions) => (host: Tree) => - from(listProjects()).pipe( - switchMap((projects: Project[]) => projectPrompt(projects)), - map(({ firebaseProject }: any) => setupFirebaseProject(host, { firebaseProject, project })) - ); - -export const ngAdd = (options: DeployOptions) => (host: Tree, context: SchematicContext) => { - const packageJson = host.exists('package.json') && safeReadJSON('package.json', host); - - if (packageJson === undefined) { - throw new SchematicsException('Could not locate package.json'); - } - - Object.keys(requiredDependencies).forEach(name => { - const dev: Boolean|undefined = requiredDependencies[name].dev; - const dependencies = dev ? packageJson.devDependencies : packageJson.dependencies - dependencies[name] = dependencies[name] || requiredDependencies[name].version; - }); - - overwriteIfExists(host, 'package.json', stringifyFormatted(packageJson)); - - const installTaskId = context.addTask(new NodePackageInstallTask()); - - context.addTask(new RunSchematicTask('ng-add-setup-firebase-deploy', options), [installTaskId]); -} +const getProject = (options: DeployOptions, host: Tree) => { + const { workspace } = getWorkspace(host); + const projectName = options.project || workspace.defaultProject; -export function setupFirebaseProject(tree: Tree, options: NgAddOptions) { - const { path: workspacePath, workspace } = getWorkspace(tree); - - if (!options.project) { - if (workspace.defaultProject) { - options.project = workspace.defaultProject; - } else { - throw new SchematicsException( - 'No Angular project selected and no default project in the workspace' - ); - } + if (!projectName) { + throw new SchematicsException( + 'No Angular project selected and no default project in the workspace' + ); } - const project = workspace.projects[options.project]; + const project = workspace.projects[projectName]; if (!project) { throw new SchematicsException( 'The specified Angular project is not defined in this workspace' @@ -209,33 +73,67 @@ export function setupFirebaseProject(tree: Tree, options: NgAddOptions) { ); } - if ( - !project.architect || - !project.architect.build || - !project.architect.build.options || - !project.architect.build.options.outputPath - ) { - throw new SchematicsException( - `Cannot read the output path (architect.build.options.outputPath) of the Angular project "${ - options.project - }" in angular.json` - ); - } - - const outputPath = project.architect.build.options.outputPath; + return {project, projectName}; +}; - project.architect['deploy'] = { - builder: '@angular/fire:deploy', - options: {} +export const setupProject = + (host: Tree, options: DeployOptions & { isUniversalProject: boolean, firebaseProject: string }) => { + const { path: workspacePath, workspace } = getWorkspace(host); + + const {project, projectName} = getProject(options, host); + + const config: NgAddNormalizedOptions = { + project: projectName, + firebaseProject: options.firebaseProject + }; + + if (options.isUniversalProject) { + return setupUniversalDeployment({ + workspace, + workspacePath, + options: config, + tree: host, + project + }); + } + return setupStaticDeployment({ + workspace, + workspacePath, + options: config, + tree: host, + project + }); }; - tree.overwrite(workspacePath, JSON.stringify(workspace, null, 2)); - generateFirebaseJson(tree, 'firebase.json', options.project!, outputPath); - generateFirebaseRc( - tree, - '.firebaserc', - options.firebaseProject, - options.project! +export const ngAddSetupProject = ( + options: DeployOptions & { isUniversalProject: boolean } +) => async (host: Tree) => { + const projects = await listProjects(); + const { firebaseProject } = await projectPrompt(projects); + return setupProject(host, {...options, firebaseProject}); +}; + +export const ngAdd = (options: DeployOptions) => ( + host: Tree, + context: SchematicContext +) => { + + const {project} = getProject(options, host); + + return projectTypePrompt(project).then( + ({ universalProject }: { universalProject: boolean }) => { + if (universalProject) { + addFirebaseFunctionsDependencies(host); + } else { + addFirebaseHostingDependencies(host); + } + const projectOptions: DeployOptions & { isUniversalProject: boolean } = { + ...options, + isUniversalProject: universalProject + }; + context.addTask(new RunSchematicTask('ng-add-setup-project', projectOptions), [ + context.addTask(new NodePackageInstallTask()) + ]); + } ); - return tree; -} +}; diff --git a/src/schematics/public_api.ts b/src/schematics/public_api.ts index 76736dec9..ad655b255 100644 --- a/src/schematics/public_api.ts +++ b/src/schematics/public_api.ts @@ -1,3 +1,3 @@ -export * from "./ng-add"; -export * from "./deploy/actions"; -export * from "./deploy/builder"; \ No newline at end of file +export * from './ng-add'; +export * from './deploy/actions'; +export * from './deploy/builder'; diff --git a/src/schematics/utils.ts b/src/schematics/utils.ts index 837485e8e..8601af926 100644 --- a/src/schematics/utils.ts +++ b/src/schematics/utils.ts @@ -1,6 +1,8 @@ -import { readFileSync } from "fs"; -import { FirebaseRc, Project } from "./interfaces"; -import { join } from "path"; +import { experimental } from '@angular-devkit/core'; +import { readFileSync } from 'fs'; +import { FirebaseRc, Project } from './interfaces'; +import { join } from 'path'; +import { isUniversalApp } from './ng-add-ssr'; export async function listProjects() { const firebase = require('firebase-tools'); @@ -43,23 +45,34 @@ const searchProjects = (projects: Project[]) => { export const projectPrompt = (projects: Project[]) => { const inquirer = require('inquirer'); inquirer.registerPrompt( - "autocomplete", - require("inquirer-autocomplete-prompt") + 'autocomplete', + require('inquirer-autocomplete-prompt') ); return inquirer.prompt({ - type: "autocomplete", - name: "firebaseProject", + type: 'autocomplete', + name: 'firebaseProject', source: searchProjects(projects), - message: "Please select a project:" + message: 'Please select a project:' }); }; +export const projectTypePrompt = (project: experimental.workspace.WorkspaceProject) => { + if (isUniversalApp(project)) { + return require('inquirer').prompt({ + type: 'confirm', + name: 'universalProject', + message: 'We detected an Angular Universal project. Do you want to deploy as a Firebase Function?' + }); + } + return Promise.resolve({ universalProject: false }); +}; + export function getFirebaseProjectName( workspaceRoot: string, target: string ): string | undefined { const { targets }: FirebaseRc = JSON.parse( - readFileSync(join(workspaceRoot, ".firebaserc"), "UTF-8") + readFileSync(join(workspaceRoot, '.firebaserc'), 'UTF-8') ); const projects = Object.keys(targets!); return projects.find( diff --git a/src/schematics/versions.json b/src/schematics/versions.json index 7964dacc8..2b38cae20 100644 --- a/src/schematics/versions.json +++ b/src/schematics/versions.json @@ -6,5 +6,10 @@ "fuzzy": { "dev": true, "version": "0.0.0"}, "inquirer": { "dev": true, "version": "0.0.0"}, "inquirer-autocomplete-prompt": { "dev": true, "version": "0.0.0"} + }, + "firebaseFunctions": { + "firebase-admin": { "dev": true, "version": "0.0.0" }, + "firebase-functions": { "dev": true, "version": "0.0.0" }, + "firebase-functions-test": { "dev": true, "version": "0.0.0" } } -} \ No newline at end of file +} diff --git a/tools/build.ts b/tools/build.ts index 6cb7a2520..fa2a88f96 100644 --- a/tools/build.ts +++ b/tools/build.ts @@ -46,6 +46,9 @@ async function replaceSchematicVersions() { Object.keys(dependencies.default).forEach(name => { dependencies.default[name].version = root.dependencies[name] || root.devDependencies[name]; }); + Object.keys(dependencies.firebaseFunctions).forEach(name => { + dependencies.firebaseFunctions[name].version = root.dependencies[name] || root.devDependencies[name]; + }); return writeFile(path, JSON.stringify(dependencies, null, 2)); } @@ -143,4 +146,4 @@ Package Size Gzipped ------------------------------------ ${stats.map((s, i) => [MODULES[i].padEnd(16), s.size.padEnd(8), s.gzip].join("")).join("\n")}` ) -); \ No newline at end of file +); diff --git a/tsconfig.jasmine.json b/tsconfig.jasmine.json index 2aa5ccc39..531e06287 100644 --- a/tsconfig.jasmine.json +++ b/tsconfig.jasmine.json @@ -2,9 +2,10 @@ "extends": "./tsconfig.base.json", "compilerOptions": { "outDir": "dist/out-tsc/jasmine", - "module": "none", + "module": "commonjs", "target": "es2015", "allowJs": true, + "resolveJsonModule": true, "types": [ "jasmine", "node" @@ -17,4 +18,3 @@ "node_modules/zone.js/dist/zone.js.d.ts" ] } - \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 2f35b410d..98ad51569 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,27 +2,27 @@ # yarn lockfile v1 -"@angular-devkit/architect@0.900.0-rc.11", "@angular-devkit/architect@^0.900.0-0 || ^0.900.0": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.900.0-rc.11.tgz#e9f3e5e372d467a220027cf53231b88e8e857fbc" - integrity sha512-rRbq4ipppnY4FvVo89Cv+yC7rlt1/VFE/jaB77Ra2tI6zVlFWCTjnMzuc9TYz/3jK1ssThzgEA2sebPDmjH47w== +"@angular-devkit/architect@0.900.0-rc.12", "@angular-devkit/architect@^0.900.0-0 || ^0.900.0": + version "0.900.0-rc.12" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.900.0-rc.12.tgz#0ee80d4ab89865d25a2df7c877fa396bd357b321" + integrity sha512-JscaHONGpvHca8ZsErQ0LQj1r0BB2eNMOEMINTDCLQoSQwOZvZaQsL3xZXwW4Uq2C9MGkWBnw2FsCxPY300NFA== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.12" rxjs "6.5.3" "@angular-devkit/build-angular@^0.900.0-0 || ^0.900.0": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.900.0-rc.11.tgz#0bdec73d3ba631b3550527c54a35cf4145b0ed6d" - integrity sha512-lp0f5lRsiTYSPjIgiWtX2N0NgV7dUZ/ifXYqqAEURKZ5O0jFF2gt2x9R29TmyFzTQ3ZP8O5AojO7POh1OfJ6iA== - dependencies: - "@angular-devkit/architect" "0.900.0-rc.11" - "@angular-devkit/build-optimizer" "0.900.0-rc.11" - "@angular-devkit/build-webpack" "0.900.0-rc.11" - "@angular-devkit/core" "9.0.0-rc.11" + version "0.900.0-rc.12" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.900.0-rc.12.tgz#dd5d8150290566a992086eedbcdb5cd201695150" + integrity sha512-g+f3RJ4ATKbcQj3T9ZZAWkp+y4A/PWaUOXS7CuhoOgue0WJbmM1wAoWwtQylEgIggcf0EhByOn3QJSzF0L8m0A== + dependencies: + "@angular-devkit/architect" "0.900.0-rc.12" + "@angular-devkit/build-optimizer" "0.900.0-rc.12" + "@angular-devkit/build-webpack" "0.900.0-rc.12" + "@angular-devkit/core" "9.0.0-rc.12" "@babel/core" "7.7.7" "@babel/generator" "7.7.7" "@babel/preset-env" "7.7.7" - "@ngtools/webpack" "9.0.0-rc.11" + "@ngtools/webpack" "9.0.0-rc.12" ajv "6.10.2" autoprefixer "9.7.1" babel-loader "8.0.6" @@ -78,17 +78,17 @@ worker-plugin "3.2.0" "@angular-devkit/build-ng-packagr@^0.900.0-0 || ^0.900.0": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.900.0-rc.11.tgz#27c05c5e6464e01f9e3d175911cdea3b4222c6c3" - integrity sha512-dtBbC/qERnw7WpQmX1ZJxfwpx71+hlfal1ZSTSmiOfhz5kdPVGBLGD17WoJsmhU7A3SGcUycg3/PYTmWGXrDaA== + version "0.900.0-rc.12" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.900.0-rc.12.tgz#760ba60c21e01737a84bac44f406dde207fe1b6a" + integrity sha512-yjj8Qbw16kp59TGWMT1FJNlm+ln9VJeUKfBPfNI4fxxx45sS4s6fmv61gfh/9laHRvtfgiT3j+1DxVDeXWG1/g== dependencies: - "@angular-devkit/architect" "0.900.0-rc.11" + "@angular-devkit/architect" "0.900.0-rc.12" rxjs "6.5.3" -"@angular-devkit/build-optimizer@0.900.0-rc.11": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.900.0-rc.11.tgz#96c2446fa9cd2e90700ab8a68312b28b3907f6d9" - integrity sha512-GJC+7H7ER6bxDC2UdAGwW357EYHpv8ISKKmS19wdJV5gZPMPANcpbg9FIpl27SDhUyZX9C2DOrcATvYYFoYgDQ== +"@angular-devkit/build-optimizer@0.900.0-rc.12": + version "0.900.0-rc.12" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.900.0-rc.12.tgz#0bbc1e796346d1c1319600d7603b17290336baf2" + integrity sha512-8qIMlxsN5MhjsEJcGEaQ4IRgaxdbQIz0mr0yez8B7sJbhTeoy11iZayunEdPHqtGc9mpyVHGJIoDA0+SJUtbNQ== dependencies: loader-utils "1.2.3" source-map "0.7.3" @@ -96,19 +96,19 @@ typescript "3.6.4" webpack-sources "1.4.3" -"@angular-devkit/build-webpack@0.900.0-rc.11": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.900.0-rc.11.tgz#d9a91c2b67a629f6adfe87980d26e495f2e30e0a" - integrity sha512-utBAnkO6WLi323Rto1s7TJpaDRqDNR8jkD0C0PG5Zm3y1U9ARbAjTkugkrB/7bc4gEIqWZD+1dLYaaJCidye2Q== +"@angular-devkit/build-webpack@0.900.0-rc.12": + version "0.900.0-rc.12" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.900.0-rc.12.tgz#7197def451279d620cad618ec036ca4475d867ae" + integrity sha512-FGW8R6rO6Wf1Z93drKDuf87UcQgcqCnkh+3zdzQgACnjqs3wGqR7NCtkrKLlkSPTF98nK6q0wYlOeY3LN3fZAA== dependencies: - "@angular-devkit/architect" "0.900.0-rc.11" - "@angular-devkit/core" "9.0.0-rc.11" + "@angular-devkit/architect" "0.900.0-rc.12" + "@angular-devkit/core" "9.0.0-rc.12" rxjs "6.5.3" -"@angular-devkit/core@8.3.23", "@angular-devkit/core@^8.3.21": - version "8.3.23" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-8.3.23.tgz#612bf8a76b1ccde40cd6a5e85a02dc083bb666c6" - integrity sha512-y++LN6R/fu+obPUKEMDSKZ5FzeWN5rV0Z8vrdC+uF02VJLv/5QI/dUx3ROKFzJO3m2LU6EAuo5b/TLAPq4ving== +"@angular-devkit/core@8.3.24", "@angular-devkit/core@^8.3.21": + version "8.3.24" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-8.3.24.tgz#94e8e6470443e2752c319cb95cdbeae9e37e19fe" + integrity sha512-xpT5yg+ddGDnifryBv2sRSYtq5F3iZIS+lN/K2AhhEa50B7Z+QaCVlEzoV/IfrGd6sLArdnKYwjLHFZ0LElUuw== dependencies: ajv "6.10.2" fast-json-stable-stringify "2.0.0" @@ -116,10 +116,10 @@ rxjs "6.4.0" source-map "0.7.3" -"@angular-devkit/core@9.0.0-rc.11", "@angular-devkit/core@^9.0.0-0 || ^9.0.0 || ^10.0.0-0": - version "9.0.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.0.0-rc.11.tgz#9e69545eb21284a573ad78e4c33003f2ea25afd5" - integrity sha512-ki7Sln+mQdCctJNBalzy70tiFn2hOCY2Yyte8B0xKWVHnofZySvG+ANzoLgodnKFOBH18AQy35FhgzZM++N9tQ== +"@angular-devkit/core@9.0.0-rc.12", "@angular-devkit/core@^9.0.0-0 || ^9.0.0 || ^10.0.0-0": + version "9.0.0-rc.12" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.0.0-rc.12.tgz#044917d6e3bc51546810db41526a9b564f0e1e21" + integrity sha512-uZEYlZSiYLk0R1ISN35np8yfRCDCk1lZMWn/FNXSRWpGBvoEoFkcbnJUfQIJJda56cyoG9tJKqgeGXdopkkyLQ== dependencies: ajv "6.10.2" fast-json-stable-stringify "2.0.0" @@ -127,20 +127,20 @@ rxjs "6.5.3" source-map "0.7.3" -"@angular-devkit/schematics@8.3.23", "@angular-devkit/schematics@^8.3.21": - version "8.3.23" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-8.3.23.tgz#c5232d74ce6595955961aeb0b6b48dda1f047b88" - integrity sha512-O8i/vn6YfqbT0q7o4jsVOTnWE07T1tcvk2zJ4O/1ete2z+Z2aw1YtIddwXEGJNCDpeE0B7f2sUHoLOS4Jc4O9w== +"@angular-devkit/schematics@8.3.24", "@angular-devkit/schematics@^8.3.21": + version "8.3.24" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-8.3.24.tgz#583b1841d032eccf31cc86dbd15c5ac15a50cf95" + integrity sha512-HrwDCgw7i3GrNns0Ce5zStWkxBqlcLuDkMcLY6981jpvVzgXMIQ+YqDrJ2kD46xHh979ev7hhw1d6jwPXh85Xw== dependencies: - "@angular-devkit/core" "8.3.23" + "@angular-devkit/core" "8.3.24" rxjs "6.4.0" -"@angular-devkit/schematics@9.0.0-rc.11", "@angular-devkit/schematics@^9.0.0-0 || ^9.0.0 || ^10.0.0-0": - version "9.0.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.0.0-rc.11.tgz#e0d4d271d8d783ebf05eced576262f20e6c3562c" - integrity sha512-aJqOLzsoAkVj3AVTf1ehH2hA9wHHz1+7TTtfqI+Yx+S3jFyvGmnKrNBCKtMuIV5JdEHiXmhhuGbNBHwRFWpOow== +"@angular-devkit/schematics@9.0.0-rc.12", "@angular-devkit/schematics@^9.0.0-0 || ^9.0.0 || ^10.0.0-0": + version "9.0.0-rc.12" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.0.0-rc.12.tgz#68afa1007f132eea92d3a124fabab1fd313d56ee" + integrity sha512-SLyuJgLWgBQLZyGUzOaKMB82fnUmiQbjTMeHtBllhDZaf7bJYVuRkdyOrIyDMCaxN/87o7t7idXj8vP+hFRd1A== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.12" ora "4.0.2" rxjs "6.5.3" @@ -150,15 +150,15 @@ integrity sha512-fsTxlCptaVlwGD8B2H3ke2bRLKzSDCMwEWxf9O/rjQhUSaIVRMGMTTy8rW8OvxF2ysZatU+ademROxBaUsO/sw== "@angular/cli@^9.0.0-0 || ^9.0.0 || ^10.0.0-0": - version "9.0.0-rc.11" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-9.0.0-rc.11.tgz#5d830045bb95f4a38a1d0df02212207731061a57" - integrity sha512-Du0y6rpOfGkH7h6ukZf5SKgpTv0uAZ5McNFrPAH2KFs7PgM4fyj9VmJ1lSns9WsVcEHdPlIPh8WGResUzvePdA== - dependencies: - "@angular-devkit/architect" "0.900.0-rc.11" - "@angular-devkit/core" "9.0.0-rc.11" - "@angular-devkit/schematics" "9.0.0-rc.11" - "@schematics/angular" "9.0.0-rc.11" - "@schematics/update" "0.900.0-rc.11" + version "9.0.0-rc.12" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-9.0.0-rc.12.tgz#b2be65d3d3e1db5cc0732987c1f352def88bdaa3" + integrity sha512-L8ywRdVPLIfBJ7TkTVQ48KQoqm3ATjM1fU4X8c2NxC1Qry2Pp3FdZOnlRld73qwR9qcaXjGef323DFqQE6N3Ag== + dependencies: + "@angular-devkit/architect" "0.900.0-rc.12" + "@angular-devkit/core" "9.0.0-rc.12" + "@angular-devkit/schematics" "9.0.0-rc.12" + "@schematics/angular" "9.0.0-rc.12" + "@schematics/update" "0.900.0-rc.12" "@yarnpkg/lockfile" "1.1.0" ansi-colors "4.1.1" debug "^4.1.1" @@ -975,7 +975,7 @@ dependencies: "@firebase/app-types" "0.5.0" -"@firebase/database@0.5.20": +"@firebase/database@0.5.20", "@firebase/database@^0.5.17": version "0.5.20" resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.5.20.tgz#f157e04409a94dda90ebb5493cc639310242cdea" integrity sha512-31dNLqMW4nGrGzIDS5hh+1LFzkr/m1Kb+EcftQGC3NaGC3zHwGyG7ijn+Xo7gIWtXukvJvm1cFC7R+eOCPEejw== @@ -1131,6 +1131,32 @@ resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.35.tgz#232e857698efb30cdda98b6f6a7a31a905d16147" integrity sha512-7njiGBbFW0HCnuKNEJLcQt9EjfOzG8EJiXlFJwA3XfgiFxPVHmXrcF4d5yold2wfiwCwrXpeNTGZ854oRr6Hcw== +"@google-cloud/common@^2.1.1": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-2.3.0.tgz#492ddd3a163c9c335ce596d70f8821fa83603a64" + integrity sha512-nmIyi3q/FL2j6ZJ61xK/863DoJEZayI2/W/iCgwrCYUYsem277XO45MBTAimjgiKBCA0c9InmQyfT48h/IK4jg== + dependencies: + "@google-cloud/projectify" "^1.0.0" + "@google-cloud/promisify" "^1.0.0" + arrify "^2.0.0" + duplexify "^3.6.0" + ent "^2.2.0" + extend "^3.0.2" + google-auth-library "^5.5.0" + retry-request "^4.0.0" + teeny-request "^6.0.0" + +"@google-cloud/firestore@^3.0.0": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@google-cloud/firestore/-/firestore-3.4.1.tgz#10bb1deaf518f622bd96cacd476d436816e16c09" + integrity sha512-k3PPcLvP9wr4yyA0djzfPdj2ZewburifhpcFACa0wiXvjXj3Ob68MORcPC3a3wyonX73TY72RsEGPk4Ult0Dyw== + dependencies: + deep-equal "^2.0.0" + functional-red-black-tree "^1.0.1" + google-gax "^1.13.0" + readable-stream "^3.4.0" + through2 "^3.0.0" + "@google-cloud/paginator@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-2.0.3.tgz#c7987ad05d1c3ebcef554381be80e9e8da4e4882" @@ -1175,6 +1201,34 @@ p-defer "^3.0.0" protobufjs "^6.8.1" +"@google-cloud/storage@^4.1.2": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@google-cloud/storage/-/storage-4.3.0.tgz#90118c42817fb2c8b3b8663a0f2857b5b45dda78" + integrity sha512-ph0jsUsZ9FPtN40V5eIkKPLUmxnTpxqBDkWxStW/kbQZgoNVGW9vJcbsYSyE4ath7jQIpM4OHu6aqmPFX1OnGw== + dependencies: + "@google-cloud/common" "^2.1.1" + "@google-cloud/paginator" "^2.0.0" + "@google-cloud/promisify" "^1.0.0" + arrify "^2.0.0" + compressible "^2.0.12" + concat-stream "^2.0.0" + date-and-time "^0.12.0" + duplexify "^3.5.0" + extend "^3.0.2" + gaxios "^2.0.1" + gcs-resumable-upload "^2.2.4" + hash-stream-validation "^0.2.2" + mime "^2.2.0" + mime-types "^2.0.8" + onetime "^5.1.0" + p-limit "^2.2.0" + pumpify "^2.0.0" + readable-stream "^3.4.0" + snakeize "^0.1.0" + stream-events "^1.0.1" + through2 "^3.0.0" + xdg-basedir "^4.0.0" + "@grpc/grpc-js@^0.6.12": version "0.6.15" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-0.6.15.tgz#534d1051ddced4e5e5849212789dd64014214dd4" @@ -1195,12 +1249,12 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@ngtools/webpack@9.0.0-rc.11": - version "9.0.0-rc.11" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-9.0.0-rc.11.tgz#10b5997bec7cf48d1b144c8b4d46ffd0039c522a" - integrity sha512-qeW81ISiO8GVEndOaCYv0k6fzRIxzZs6jrXGl1pcLH1H6qv2mxhA5DA0vC/9TN6wenrS43RGjDIQpp+RvkiLwA== +"@ngtools/webpack@9.0.0-rc.12": + version "9.0.0-rc.12" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-9.0.0-rc.12.tgz#c67f24b514f093dc7a135bf4a7c4cc3504f52db0" + integrity sha512-bMAVTIS0oe2mTfIC2FWM1lcdMkvjLe1no47Yu/Eimz6N/HDVPDd0B1TkimDf0gjn9/UuJIhsgkbYinIFy8Ss0Q== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.12" enhanced-resolve "4.1.1" rxjs "6.5.3" webpack-sources "1.4.3" @@ -1258,29 +1312,29 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@schematics/angular@9.0.0-rc.11": - version "9.0.0-rc.11" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.0.0-rc.11.tgz#d544c0d4e7b3dd59ed56be5183e038ebe06a165e" - integrity sha512-9InC+F71KiPXE0jl7Ow4iPFJ2AZZDbfTM6yWZoYLk3hzTCohAZZciBl00Tfyu2uerGshx8akbJMLySjXtf+q0g== +"@schematics/angular@9.0.0-rc.12": + version "9.0.0-rc.12" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.0.0-rc.12.tgz#51de45a2a81f994f3b1f92ef0ccd047ae2b7c63a" + integrity sha512-OhF2ebzRWnY3E0k2yAYEpAa4z5Ww0sC4Di0uYtkleSfUdaqe68ntXzV1tBZgScsSKwanDxerHwrzGK91TN+IIQ== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" - "@angular-devkit/schematics" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.12" + "@angular-devkit/schematics" "9.0.0-rc.12" "@schematics/angular@^8.3.21": - version "8.3.23" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-8.3.23.tgz#8f46c4673eca3fadc46b2d43c4367e5542be624e" - integrity sha512-yisP1iCLGC4VnZNC3kOnYyTS5cmfKEnLM9bMzhZGMWwov9RRfdxKKeSnG9FJNwHxI0WjQ0UWwfiz1dj0YacG3g== + version "8.3.24" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-8.3.24.tgz#4d8bfbeed2849bed611e682f418bfcadb0c62277" + integrity sha512-0nf/LgMHAvhjWS97Pl3JGMqS9/4PI+C0+vJoAo6D7ax8Fb+wuY5uD6Pb7ZqaZALlEnqTgE+FBQ1K8VBVbuwKbA== dependencies: - "@angular-devkit/core" "8.3.23" - "@angular-devkit/schematics" "8.3.23" + "@angular-devkit/core" "8.3.24" + "@angular-devkit/schematics" "8.3.24" -"@schematics/update@0.900.0-rc.11": - version "0.900.0-rc.11" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.900.0-rc.11.tgz#d22df30f13a6f38970b759db61ad84d3f9b03a78" - integrity sha512-nV0oCPzzd0vi2Exo1910rWXwz/RnMc4zF9FxSOCZzsIv+AkwIehhL815OKyjUSCzU9+IM0/o1LKkPPrSWK7QEA== +"@schematics/update@0.900.0-rc.12": + version "0.900.0-rc.12" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.900.0-rc.12.tgz#0b09990ead04e83056567b89c4b429ad730f6d4e" + integrity sha512-Wkp8MINr/BoEsyBTPdUrQJpt3s235lfrDtZw75w3+h+c7iHkhczXTox1LJkt8t/YH5YkuDjAWJiaT2DNVaOJow== dependencies: - "@angular-devkit/core" "9.0.0-rc.11" - "@angular-devkit/schematics" "9.0.0-rc.11" + "@angular-devkit/core" "9.0.0-rc.12" + "@angular-devkit/schematics" "9.0.0-rc.12" "@yarnpkg/lockfile" "1.1.0" ini "1.3.5" npm-package-arg "^7.0.0" @@ -1290,12 +1344,12 @@ semver-intersect "1.4.0" "@schematics/update@^0.803.21": - version "0.803.23" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.803.23.tgz#214df8f0f6cdd5e1ad7b535ba9b0f46a5f24a113" - integrity sha512-pLd5PseFTYF3VZ+IgMeNEFATQY5A80ylot7Dcg9FDeihqr5R9Rd1maCWIR43oKXvtK5C5+ackwR0QaPBAZ9bdw== + version "0.803.24" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.803.24.tgz#f1076b6b1e4aa997f2bbe6db392089bb467787a6" + integrity sha512-NvCKn3QfpRjx1EzL56q9IC9fRtDXZP4bMGs/2tj+wtdBNHgm6ZJMJ9qc4mGeztKGbDFLmnX3Xz0XawAl+KeYzQ== dependencies: - "@angular-devkit/core" "8.3.23" - "@angular-devkit/schematics" "8.3.23" + "@angular-devkit/core" "8.3.24" + "@angular-devkit/schematics" "8.3.24" "@yarnpkg/lockfile" "1.1.0" ini "1.3.5" pacote "9.5.5" @@ -1315,6 +1369,14 @@ dependencies: defer-to-connect "^1.0.1" +"@types/body-parser@*": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.1.tgz#18fcf61768fb5c30ccc508c21d6fd2e8b3bf7897" + integrity sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/bytebuffer@^5.0.40": version "5.0.40" resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.40.tgz#d6faac40dcfb09cd856cdc4c01d3690ba536d3ee" @@ -1328,6 +1390,13 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@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/duplexify@^3.6.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@types/duplexify/-/duplexify-3.6.0.tgz#dfc82b64bd3a2168f5bd26444af165bf0237dcd8" @@ -1350,6 +1419,23 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== +"@types/express-serve-static-core@*": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz#f6f41fa35d42e79dbf6610eccbb2637e6008a0cf" + integrity sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg== + dependencies: + "@types/node" "*" + "@types/range-parser" "*" + +"@types/express@^4.17.0": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.2.tgz#a0fb7a23d8855bac31bc01d5a58cadd9b2173e6c" + integrity sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/serve-static" "*" + "@types/form-data@0.0.*": version "0.0.33" resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8" @@ -1405,20 +1491,30 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/lodash@^4.14.104": + version "4.14.149" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.149.tgz#1342d63d948c6062838fbf961012f74d4e638440" + integrity sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ== + "@types/long@*", "@types/long@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== +"@types/mime@*": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" + integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw== + "@types/minimatch@*", "@types/minimatch@3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*": - version "13.5.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.5.3.tgz#37f1f539b7535b9fb4ef77d59db1847a837b7f17" - integrity sha512-ZPnWX9PW992w6DUsz3JIXHaSb5v7qmKCVzC3km6SxcDGxk7zmLfYaCJTbktIa5NeywJkkZDhGldKqDIvC5DRrA== + version "13.7.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.0.tgz#b417deda18cf8400f278733499ad5547ed1abec4" + integrity sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ== "@types/node@6.0.*": version "6.0.118" @@ -1435,6 +1531,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.26.tgz#213e153babac0ed169d44a6d919501e68f59dea9" integrity sha512-UmUm94/QZvU5xLcUlNR8hA7Ac+fGpO1EG/a8bcWVz0P0LqtxFmun9Y2bbtuckwGboWJIT70DoWq1r3hb56n3DA== +"@types/node@^8.10.59": + version "8.10.59" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.59.tgz#9e34261f30183f9777017a13d185dfac6b899e04" + integrity sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ== + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -1445,6 +1546,11 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== +"@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/request@0.0.30": version "0.0.30" resolved "https://registry.yarnpkg.com/@types/request/-/request-0.0.30.tgz#18208841a0cf6538eff5e306bfa92e86c8c8acae" @@ -1561,6 +1667,21 @@ "@types/rx-lite-time" "*" "@types/rx-lite-virtualtime" "*" +"@types/semver@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.1.0.tgz#c8c630d4c18cd326beff77404887596f96408408" + integrity sha512-pOKLaubrAEMUItGNpgwl0HMFPrSAFic8oSVIvfu1UwcgGNmNyK9gyhBHKmBnUTwwVvpZfkzUC0GaMgnL6P86uA== + dependencies: + "@types/node" "*" + +"@types/serve-static@*": + version "1.13.3" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1" + integrity sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g== + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -1816,6 +1937,13 @@ agent-base@5: resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== +agent-base@6: + version "6.0.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.0.tgz#5d0101f19bbfaed39980b22ae866de153b93f09a" + integrity sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw== + dependencies: + debug "4" + agent-base@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" @@ -3598,7 +3726,7 @@ compress-commons@^2.1.1: normalize-path "^3.0.0" readable-stream "^2.3.6" -compressible@~2.0.16: +compressible@^2.0.12, compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== @@ -3633,6 +3761,16 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" +concat-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.0.2" + typedarray "^0.0.6" + concurrently@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-2.2.0.tgz#bad248e0bb129fb1621768903a6311d45d56895a" @@ -3972,6 +4110,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + cosmiconfig@^5.0.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" @@ -4357,6 +4503,11 @@ data-uri-to-buffer@0.0.4: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.4.tgz#46e13ab9da8e309745c8d01ce547213ebdb2fe3f" integrity sha1-RuE6udqOMJdFyNAc5UchPr2y/j8= +date-and-time@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/date-and-time/-/date-and-time-0.12.0.tgz#6d30c91c47fa72edadd628b71ec2ac46909b9267" + integrity sha512-n2RJIAp93AucgF/U/Rz5WRS2Hjg5Z+QxscaaMCi6pVZT1JpJKRH+C08vyH/lRR1kxNXnPxgo3lWfd+jCb/UcuQ== + date-format@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" @@ -4444,6 +4595,24 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-equal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.1.tgz#fc12bbd6850e93212f21344748682ccc5a8813cf" + integrity sha512-7Et6r6XfNW61CPPCIYfm1YPGSmh6+CliYeL4km7GWJcpX5LTAflGF8drLLR+MZX+2P3NZfAfSduutBbSWqER4g== + dependencies: + es-abstract "^1.16.3" + es-get-iterator "^1.0.1" + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + isarray "^2.0.5" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + side-channel "^1.0.1" + which-boxed-primitive "^1.0.1" + which-collection "^1.0.0" + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -4591,6 +4760,13 @@ di@^0.0.1: resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw= +dicer@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" + integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== + dependencies: + streamsearch "0.1.2" + didyoumean@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.1.tgz#e92edfdada6537d484d73c0172fd1eba0c4976ff" @@ -4731,7 +4907,7 @@ duplexer@^0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= -duplexify@^3.4.2, duplexify@^3.6.0: +duplexify@^3.4.2, duplexify@^3.5.0, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== @@ -4741,6 +4917,16 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +duplexify@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.1.tgz#7027dc374f157b122a8ae08c2d3ea4d2d953aa61" + integrity sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.0" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -4862,7 +5048,7 @@ enhanced-resolve@4.1.1, enhanced-resolve@^4.1.0: memory-fs "^0.5.0" tapable "^1.0.0" -ent@~2.2.0: +ent@^2.2.0, ent@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= @@ -4891,7 +5077,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: +es-abstract@^1.16.3, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4: version "1.17.4" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== @@ -4908,6 +5094,19 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" +es-get-iterator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" + integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== + dependencies: + es-abstract "^1.17.4" + has-symbols "^1.0.1" + is-arguments "^1.0.4" + is-map "^2.0.1" + is-set "^2.0.1" + is-string "^1.0.5" + isarray "^2.0.5" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -5407,6 +5606,39 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +firebase-admin@^8.9.2: + version "8.9.2" + resolved "https://registry.yarnpkg.com/firebase-admin/-/firebase-admin-8.9.2.tgz#248ba184dc13b4929b043870a2067787a0dd013e" + integrity sha512-ix4qcx+hHnr3mnc41Z8EzQa9Mr+2nhogLEv6ktkOCCpdKJ+9HxW9vikRCElSbC8ICHLD0KIH0GVOIZK80vbvqw== + dependencies: + "@firebase/database" "^0.5.17" + "@types/node" "^8.10.59" + dicer "^0.3.0" + jsonwebtoken "8.1.0" + node-forge "0.7.4" + optionalDependencies: + "@google-cloud/firestore" "^3.0.0" + "@google-cloud/storage" "^4.1.2" + +firebase-functions-test@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/firebase-functions-test/-/firebase-functions-test-0.1.7.tgz#c1f9b82157a1435e38c6707f0943447403ab1703" + integrity sha512-/zVQhaUZ+M7z25aUaZSIah0MIDZIfnRfQxtHYTE8hgUgODmKdaMX20vh5Gv23hnCPauIHuYb7XFTUOZiWU1udA== + dependencies: + "@types/lodash" "^4.14.104" + lodash "^4.17.5" + +firebase-functions@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/firebase-functions/-/firebase-functions-3.3.0.tgz#8c6d909eebfa4bce1b25a2e34edfc52cc786a020" + integrity sha512-dP6PCG+OwR6RtFpOqwPsLnfiCr3CwXAm/SVGMbO53vDAk0nhUQ1WGAyHDYmIyMAkaLJkIKGwDnX7XmZ5+yAg7g== + dependencies: + "@types/express" "^4.17.0" + cors "^2.8.5" + express "^4.17.1" + jsonwebtoken "^8.5.1" + lodash "^4.17.14" + firebase-tools@^7.12.1: version "7.12.1" resolved "https://registry.yarnpkg.com/firebase-tools/-/firebase-tools-7.12.1.tgz#af78bbc446ae46d722938bd8009c351d52ec01f3" @@ -5669,6 +5901,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + fuzzy@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/fuzzy/-/fuzzy-0.1.3.tgz#4c76ec2ff0ac1a36a9dccf9a00df8623078d4ed8" @@ -5688,10 +5925,10 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gaxios@^2.1.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-2.2.2.tgz#f58516ead6868d997fcf267d4fe2dcef652bd1a3" - integrity sha512-fzttYsjvZxCaN+bQK7FtAMgoIlPtHkMwlz7vHD+aNRcU7I7gHgnp6hvGJksoo+dO1TDxaog+dSBycbYhHIStaA== +gaxios@^2.0.0, gaxios@^2.0.1, gaxios@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-2.3.0.tgz#442eb57c6f00811795946a89a13dfe47bdb9ba40" + integrity sha512-VgC4JKJQAAAGK5rFZbPcS5mXsdIYVMIUJOxMjSOkYdfhB74R0L6y8PFQDdS0r1ObG6hdP11e71EjHh3xbI+6fQ== dependencies: abort-controller "^3.0.0" extend "^3.0.2" @@ -5715,6 +5952,18 @@ gcp-metadata@^3.3.0: gaxios "^2.1.0" json-bigint "^0.3.0" +gcs-resumable-upload@^2.2.4: + version "2.3.2" + resolved "https://registry.yarnpkg.com/gcs-resumable-upload/-/gcs-resumable-upload-2.3.2.tgz#f04a7459483f871f0de71db7454296938688a296" + integrity sha512-OPS0iAmPCV+r7PziOIhyxmQOzsazFCy76yYDOS/Z80O/7cuny1KMfqDQa2T0jLaL8EreTU7EMZG5pUuqBKgzHA== + dependencies: + abort-controller "^3.0.0" + configstore "^5.0.0" + gaxios "^2.0.0" + google-auth-library "^5.0.0" + pumpify "^2.0.0" + stream-events "^1.0.4" + generate-function@^2.0.0: version "2.3.1" resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" @@ -5993,7 +6242,7 @@ google-auto-auth@^0.7.2: google-auth-library "^0.10.0" request "^2.79.0" -google-gax@^1.7.5: +google-gax@^1.13.0, google-gax@^1.7.5: version "1.14.1" resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-1.14.1.tgz#8271a5293b7b61377d548289964b7e8209984381" integrity sha512-lAvILUMnXL+BVSSlbzwpGzs3ZP2r+b1l44zeDTRWceejDgyZORKdPEEhtUw49x9CVwxpPx02+v0yktqnRhUD1A== @@ -6273,6 +6522,13 @@ hash-base@^3.0.0: inherits "^2.0.1" safe-buffer "^5.0.1" +hash-stream-validation@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/hash-stream-validation/-/hash-stream-validation-0.2.2.tgz#6b34c4fce5e9fce265f1d3380900049d92a10090" + integrity sha512-cMlva5CxWZOrlS/cY0C+9qAzesn5srhFA8IT1VPiHc9bWWBLkJfEUIZr7MWoi89oOOGmpg8ymchaOjiArsGu5A== + dependencies: + through2 "^2.0.0" + hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" @@ -6435,6 +6691,14 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" +http-proxy-agent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.0.tgz#6b74d332e1934a1107b97e97de4a00e267c790fe" + integrity sha512-GX0FA6+IcDf4Oxc/FBWgYj4zKgo/DnZrksaG9jyuQLExs6xlX+uI5lcA8ymM3JaZTRrF/4s2UX19wJolyo7OBA== + dependencies: + agent-base "6" + debug "4" + http-proxy-middleware@0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" @@ -6654,9 +6918,9 @@ injection-js@^2.2.1: integrity sha512-rhS6E5jv603kbaO72ylOt0hGF1LT03oqQ4GU5KOO0qSaRbIWmdUCHjXq+VT79jL6/NmXtw9ccfK6dh/CzjoYjA== inquirer-autocomplete-prompt@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-1.0.1.tgz#e4be98a9e727ea5160937e33f8724e70464e3c4d" - integrity sha512-Y4V6ifAu9LNrNjcEtYq8YUKhrgmmufUn5fsDQqeWgHY8rEO6ZAQkNUiZtBm2kw2uUQlC9HdgrRCHDhTPPguH5A== + version "1.0.2" + resolved "https://registry.yarnpkg.com/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-1.0.2.tgz#3f2548f73dd12f0a541be055ea9c8c7aedeb42bf" + integrity sha512-vNmAhhrOQwPnUm4B9kz1UB7P98rVF1z8txnjp53r40N0PBCuqoRWqjg3Tl0yz0UkDg7rEUtZ2OZpNc7jnOU9Zw== dependencies: ansi-escapes "^3.0.0" chalk "^2.0.0" @@ -6809,6 +7073,11 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" + integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -6823,6 +7092,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" + integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -6977,6 +7251,11 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" + integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -7008,6 +7287,11 @@ is-npm@^4.0.0: resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== +is-number-object@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -7112,6 +7396,11 @@ is-retry-allowed@^1.0.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== +is-set@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" + integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== + is-stream-ended@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" @@ -7127,6 +7416,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== +is-string@^1.0.4, is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + is-subset@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" @@ -7168,6 +7462,16 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakset@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" + integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -7212,6 +7516,11 @@ isarray@2.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isbinaryfile@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80" @@ -7540,7 +7849,23 @@ jsonschema@^1.0.2: resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.5.tgz#bab69d97fa28946aec0a56a9cc266d23fe80ae61" integrity sha512-kVTF+08x25PQ0CjuVc0gRM9EUPb0Fe9Ln/utFOgcdxEIOHuU7ooBk/UPTd7t1M91pP35m0MU1T8M5P7vP1bRRw== -jsonwebtoken@^8.2.1: +jsonwebtoken@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.1.0.tgz#c6397cd2e5fd583d65c007a83dc7bb78e6982b83" + integrity sha1-xjl80uX9WD1lwAeoPce7eOaYK4M= + dependencies: + jws "^3.1.4" + 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.0.0" + xtend "^4.0.1" + +jsonwebtoken@^8.2.1, jsonwebtoken@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== @@ -8407,7 +8732,7 @@ mime-db@~1.12.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.12.0.tgz#3d0c63180f458eb10d325aaa37d7c58ae312e9d7" integrity sha1-PQxjGA9FjrENMlqqN9fFiuMS6dc= -mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: +mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.26" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== @@ -8783,11 +9108,16 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.3.0, node-fetch@^2.6.0: +node-fetch@^2.2.0, node-fetch@^2.3.0, node-fetch@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-forge@0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.4.tgz#8e6e9f563a1e32213aa7508cded22aa791dbf986" + integrity sha512-8Df0906+tq/omxuCZD6PqhPaQDYuyJ1d+VITgxoIA8zvQd1ru+nMJcDChHH324MWitIgbVkAkQoGEEVJNpn/PA== + node-forge@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" @@ -9048,7 +9378,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -10216,6 +10546,15 @@ pumpify@^1.3.3: inherits "^2.0.3" pump "^2.0.0" +pumpify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-2.0.1.tgz#abfc7b5a621307c728b551decbbefb51f0e4aa1e" + integrity sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw== + dependencies: + duplexify "^4.1.1" + inherits "^2.0.3" + pump "^3.0.0" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -10435,7 +10774,7 @@ read-pkg@^5.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@^3.0.1, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0: +"readable-stream@2 || 3", readable-stream@^3.0.1, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.5.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.5.0.tgz#465d70e6d1087f6162d079cd0b5db7fbebfd1606" integrity sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA== @@ -11186,6 +11525,11 @@ semver@^4.3.3: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= +semver@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" + integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== + semver@~5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" @@ -11309,6 +11653,14 @@ shelljs@^0.8.0, shelljs@^0.8.3: interpret "^1.0.0" rechoir "^0.6.2" +side-channel@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947" + integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA== + dependencies: + es-abstract "^1.17.0-next.1" + object-inspect "^1.7.0" + sigmund@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" @@ -11341,6 +11693,11 @@ smart-buffer@^4.1.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== +snakeize@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/snakeize/-/snakeize-0.1.0.tgz#10c088d8b58eb076b3229bb5a04e232ce126422d" + integrity sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0= + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -11708,6 +12065,13 @@ stream-each@^1.1.0: end-of-stream "^1.1.0" stream-shift "^1.0.0" +stream-events@^1.0.1, stream-events@^1.0.4, stream-events@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" + integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== + dependencies: + stubs "^3.0.0" + stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" @@ -11735,6 +12099,11 @@ streamroller@^1.0.6: fs-extra "^7.0.1" lodash "^4.17.14" +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -11891,6 +12260,11 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +stubs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" + integrity sha1-6NK6H6nJBXAwPAMLaQD31fiavls= + style-loader@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.0.0.tgz#1d5296f9165e8e2c85d24eee0b7caf9ec8ca1f82" @@ -12095,6 +12469,17 @@ tcp-port-used@^1.0.1: debug "4.1.0" is2 "2.0.1" +teeny-request@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-6.0.1.tgz#9b1f512cef152945827ba7e34f62523a4ce2c5b0" + integrity sha512-TAK0c9a00ELOqLrZ49cFxvPVogMUFaWY8dUsQc/0CuQPGF+BOxOQzXfE413BAk2kLomwNplvdtMpeaeGWmoc2g== + dependencies: + http-proxy-agent "^4.0.0" + https-proxy-agent "^4.0.0" + node-fetch "^2.2.0" + stream-events "^1.0.5" + uuid "^3.3.2" + tempfile@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-1.1.1.tgz#5bcc4eaecc4ab2c707d8bc11d99ccc9a2cb287f2" @@ -12184,7 +12569,7 @@ through2@^2.0.0, through2@^2.0.2: readable-stream "~2.3.6" xtend "~4.0.1" -through2@^3.0.1: +through2@^3.0.0, through2@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.1.tgz#39276e713c3302edf9e388dd9c812dd3b825bd5a" integrity sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww== @@ -12882,7 +13267,7 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" -vary@~1.1.2: +vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= @@ -13082,6 +13467,27 @@ when@~3.6.x: resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" integrity sha1-RztRfsFZ4rhQBUl6E5g/CVQS404= +which-boxed-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" + integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== + dependencies: + is-bigint "^1.0.0" + is-boolean-object "^1.0.0" + is-number-object "^1.0.3" + is-string "^1.0.4" + is-symbol "^1.0.2" + +which-collection@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -13299,7 +13705,7 @@ xmlhttprequest@1.8.0: resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= -xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==