From 2114446acb1704e93cabd2933d5876f8d9adb56a Mon Sep 17 00:00:00 2001 From: Derek Burgman Date: Sun, 2 Oct 2022 23:10:35 -0500 Subject: [PATCH] feat: added firebase scheduled tasks --- apps/demo-api/src/app/app.ts | 21 +- .../app/function/example/example.schedule.ts | 5 + apps/demo-api/src/app/function/function.ts | 29 +- .../app/function/guestbook/guestbook.crud.ts | 4 +- .../guestbook/guestbookentry.update.ts | 4 +- .../app/function/model/schedule.functions.ts | 10 + .../app/function/profile/profile.update.ts | 8 +- apps/demo-api/src/main.ts | 8 +- package-lock.json | 1242 +++++++++++++++-- package.json | 7 +- .../src/lib/env/env.service.ts | 1 + .../src/lib/nest/env/env.service.ts | 7 + .../src/lib/nest/env/env.util.ts | 11 + .../firebase-server/src/lib/nest/env/index.ts | 1 + .../src/lib/nest/function/index.ts | 2 + .../src/lib/nest/function/schedule.ts | 75 + .../src/lib/nest/function/schedule.util.ts | 86 ++ .../src/lib/nest/function/v1/index.ts | 1 + .../src/lib/nest/function/v1/nest.spec.ts | 49 + .../src/lib/nest/function/v1/schedule.ts | 73 + .../src/lib/nest/function/v2/index.ts | 1 + .../src/lib/nest/function/v2/schedule.ts | 71 + .../src/lib/nest/function/v2/taskqueue.ts | 2 + packages/util/src/lib/value/cron.ts | 10 + packages/util/src/lib/value/index.ts | 1 + setup/templates/apps/api/src/app/app.ts | 14 +- .../apps/api/src/app/function/function.ts | 6 +- 27 files changed, 1651 insertions(+), 98 deletions(-) create mode 100644 apps/demo-api/src/app/function/example/example.schedule.ts create mode 100644 apps/demo-api/src/app/function/model/schedule.functions.ts create mode 100644 packages/firebase-server/src/lib/nest/env/env.util.ts create mode 100644 packages/firebase-server/src/lib/nest/function/schedule.ts create mode 100644 packages/firebase-server/src/lib/nest/function/schedule.util.ts create mode 100644 packages/firebase-server/src/lib/nest/function/v1/schedule.ts create mode 100644 packages/firebase-server/src/lib/nest/function/v2/schedule.ts create mode 100644 packages/util/src/lib/value/cron.ts diff --git a/apps/demo-api/src/app/app.ts b/apps/demo-api/src/app/app.ts index 25b335eb8..56d41a345 100644 --- a/apps/demo-api/src/app/app.ts +++ b/apps/demo-api/src/app/app.ts @@ -1,9 +1,10 @@ import { demoCreateModel, demoDeleteModel, demoUpdateModel } from './function/model/crud.functions'; import { profileSetUsernameKey } from '@dereekb/demo-firebase'; -import { NestAppPromiseGetter, nestServerInstance } from '@dereekb/firebase-server'; +import { initEnvironmentForScheduleConfiguredFunctions, nestAppHasDevelopmentSchedulerEnabled, NestAppPromiseGetter, nestServerInstance } from '@dereekb/firebase-server'; import { UPDATE_MODEL_APP_FUNCTION_KEY, DELETE_MODEL_APP_FUNCTION_KEY, CREATE_MODEL_APP_FUNCTION_KEY } from '@dereekb/firebase'; import { DemoApiAppModule } from './app.module'; import { profileSetUsername, initUserOnCreate } from './function'; +import { demoExampleUsageOfSchedule } from './function/model/schedule.functions'; export const { initNestServer } = nestServerInstance({ moduleClass: DemoApiAppModule, @@ -32,3 +33,21 @@ export function allAppFunctions(nest: NestAppPromiseGetter) { // Guestbook }; } + +/** + * Builder for all scheduled functions in the app. + * + * @param nest + */ +export function allScheduledAppFunctions(nest: NestAppPromiseGetter) { + return initEnvironmentForScheduleConfiguredFunctions( + { + // Scheduled Functions + exampleSchedule: demoExampleUsageOfSchedule(nest) + }, + { + // Only enable when the development scheduler is enabled + checkEnabled: nestAppHasDevelopmentSchedulerEnabled(nest) + } + ); +} diff --git a/apps/demo-api/src/app/function/example/example.schedule.ts b/apps/demo-api/src/app/function/example/example.schedule.ts new file mode 100644 index 000000000..49eba6b6e --- /dev/null +++ b/apps/demo-api/src/app/function/example/example.schedule.ts @@ -0,0 +1,5 @@ +import { DemoScheduleFunction } from '../function'; + +export const exampleUsageOfSchedule: DemoScheduleFunction = (request) => { + console.log('exampleUsageOfSchedule() was called!'); +}; diff --git a/apps/demo-api/src/app/function/function.ts b/apps/demo-api/src/app/function/function.ts index 7065fd48c..7af167a33 100644 --- a/apps/demo-api/src/app/function/function.ts +++ b/apps/demo-api/src/app/function/function.ts @@ -1,6 +1,23 @@ import { INestApplicationContext } from '@nestjs/common'; import { DemoFirebaseContextAppContext, demoFirebaseModelServices, DemoFirebaseModelTypes, DemoFirestoreCollections } from '@dereekb/demo-firebase'; -import { onCallWithNestApplicationFactory, onCallWithNestContextFactory, taskQueueFunctionHandlerWithNestContextFactory, cloudEventHandlerWithNestContextFactory, blockingFunctionHandlerWithNestContextFactory, onEventWithNestContextFactory, AbstractFirebaseNestContext, OnCallUpdateModelFunction, OnCallUpdateModelMap, OnCallDeleteModelMap, OnCallDeleteModelFunction, OnCallCreateModelFunction, OnCallCreateModelMap } from '@dereekb/firebase-server'; +import { + onCallWithNestApplicationFactory, + onCallWithNestContextFactory, + taskQueueFunctionHandlerWithNestContextFactory, + cloudEventHandlerWithNestContextFactory, + blockingFunctionHandlerWithNestContextFactory, + onEventWithNestContextFactory, + AbstractFirebaseNestContext, + OnCallUpdateModelFunction, + OnCallUpdateModelMap, + OnCallDeleteModelMap, + OnCallDeleteModelFunction, + OnCallCreateModelFunction, + OnCallCreateModelMap, + onScheduleWithNestApplicationFactory, + onScheduleWithNestContextFactory, + OnScheduleWithNestContext +} from '@dereekb/firebase-server'; import { OnCallCreateModelResult } from '@dereekb/firebase'; import { ProfileServerActions, GuestbookServerActions, DemoApiAuthService, DemoFirebaseServerActionsContext } from '../common'; @@ -37,17 +54,21 @@ export class DemoApiNestContext extends AbstractFirebaseNestContext new DemoApiNestContext(nest); export const onCallWithDemoNest = onCallWithNestApplicationFactory(); export const onCallWithDemoNestContext = onCallWithNestContextFactory(onCallWithDemoNest, mapDemoApiNestContext); +export const onScheduleWithDemoNest = onScheduleWithNestApplicationFactory(); +export const onScheduleWithDemoNestContext = onScheduleWithNestContextFactory(onScheduleWithDemoNest, mapDemoApiNestContext); export const onEventWithDemoNestContext = onEventWithNestContextFactory(mapDemoApiNestContext); export const cloudEventWithDemoNestContext = cloudEventHandlerWithNestContextFactory(mapDemoApiNestContext); export const blockingEventWithDemoNestContext = blockingFunctionHandlerWithNestContextFactory(mapDemoApiNestContext); export const taskqueueEventWithDemoNestContext = taskQueueFunctionHandlerWithNestContextFactory(mapDemoApiNestContext); // MARK: CRUD Functions -export type DemoCreateModelfunction = OnCallCreateModelFunction; +export type DemoCreateModelFunction = OnCallCreateModelFunction; export type DemoOnCallCreateModelMap = OnCallCreateModelMap; -export type DemoUpdateModelfunction = OnCallUpdateModelFunction; +export type DemoUpdateModelFunction = OnCallUpdateModelFunction; export type DemoOnCallUpdateModelMap = OnCallUpdateModelMap; -export type DemoDeleteModelfunction = OnCallDeleteModelFunction; +export type DemoDeleteModelFunction = OnCallDeleteModelFunction; export type DemoOnCallDeleteModelMap = OnCallDeleteModelMap; + +export type DemoScheduleFunction = OnScheduleWithNestContext; diff --git a/apps/demo-api/src/app/function/guestbook/guestbook.crud.ts b/apps/demo-api/src/app/function/guestbook/guestbook.crud.ts index 883933fee..b98aebac1 100644 --- a/apps/demo-api/src/app/function/guestbook/guestbook.crud.ts +++ b/apps/demo-api/src/app/function/guestbook/guestbook.crud.ts @@ -1,8 +1,8 @@ import { CreateGuestbookParams } from '@dereekb/demo-firebase'; import { onCallCreateModelResultWithDocs } from '@dereekb/firebase'; -import { DemoCreateModelfunction } from '../function'; +import { DemoCreateModelFunction } from '../function'; -export const createGuestbook: DemoCreateModelfunction = async (request) => { +export const createGuestbook: DemoCreateModelFunction = async (request) => { const { nest, auth, data } = request; const createGuestbook = await nest.guestbookActions.createGuestbook(data); const guestbook = await createGuestbook(); diff --git a/apps/demo-api/src/app/function/guestbook/guestbookentry.update.ts b/apps/demo-api/src/app/function/guestbook/guestbookentry.update.ts index 5ddd61f56..4d6ae1b9a 100644 --- a/apps/demo-api/src/app/function/guestbook/guestbookentry.update.ts +++ b/apps/demo-api/src/app/function/guestbook/guestbookentry.update.ts @@ -1,8 +1,8 @@ import { UpdateGuestbookEntryParams } from '@dereekb/demo-firebase'; -import { DemoUpdateModelfunction } from '../function'; +import { DemoUpdateModelFunction } from '../function'; import { guestbookEntryForUser } from './guestbook.util'; -export const updateGuestbookEntry: DemoUpdateModelfunction = async (request) => { +export const updateGuestbookEntry: DemoUpdateModelFunction = async (request) => { const { nest, auth, data } = request; const guestbookEntryUpdateEntry = await nest.guestbookActions.updateGuestbookEntry(data); diff --git a/apps/demo-api/src/app/function/model/schedule.functions.ts b/apps/demo-api/src/app/function/model/schedule.functions.ts new file mode 100644 index 000000000..c6b3992fa --- /dev/null +++ b/apps/demo-api/src/app/function/model/schedule.functions.ts @@ -0,0 +1,10 @@ +import { exampleUsageOfSchedule } from '../example/example.schedule'; +import { onScheduleWithDemoNestContext } from '../function'; + +// MARK: Example +export const demoExampleUsageOfSchedule = onScheduleWithDemoNestContext( + { + cron: 1 + }, + exampleUsageOfSchedule +); diff --git a/apps/demo-api/src/app/function/profile/profile.update.ts b/apps/demo-api/src/app/function/profile/profile.update.ts index 9b88c3742..940795bc5 100644 --- a/apps/demo-api/src/app/function/profile/profile.update.ts +++ b/apps/demo-api/src/app/function/profile/profile.update.ts @@ -1,17 +1,17 @@ import { FinishOnboardingProfileParams, ProfileDocument, SetProfileUsernameParams, UpdateProfileParams } from '@dereekb/demo-firebase'; -import { DemoUpdateModelfunction } from '../function'; +import { DemoUpdateModelFunction } from '../function'; import { profileForUserRequest } from './profile.util'; import { userHasNoProfileError } from '../../common'; import { AUTH_ONBOARDED_ROLE, AUTH_TOS_SIGNED_ROLE } from '@dereekb/util'; -export const updateProfile: DemoUpdateModelfunction = async (request) => { +export const updateProfile: DemoUpdateModelFunction = async (request) => { const { nest, auth, data } = request; const updateProfile = await nest.profileActions.updateProfile(data); const profileDocument: ProfileDocument = await profileForUserRequest(request); await updateProfile(profileDocument); }; -export const updateProfileUsername: DemoUpdateModelfunction = async (request) => { +export const updateProfileUsername: DemoUpdateModelFunction = async (request) => { const { nest, auth, data } = request; const setProfileUsername = await nest.profileActions.setProfileUsername(data); const profileDocument: ProfileDocument = await profileForUserRequest(request); @@ -24,7 +24,7 @@ export const updateProfileUsername: DemoUpdateModelfunction = async (request) => { +export const updateProfleOnboarding: DemoUpdateModelFunction = async (request) => { const { nest, auth, data } = request; const uid = auth.uid; diff --git a/apps/demo-api/src/main.ts b/apps/demo-api/src/main.ts index 31ec37301..39b6d4d1f 100644 --- a/apps/demo-api/src/main.ts +++ b/apps/demo-api/src/main.ts @@ -2,7 +2,7 @@ import { environment } from './environments/environment'; import 'reflect-metadata'; import { onRequest } from 'firebase-functions/v2/https'; import * as admin from 'firebase-admin'; -import { allAppFunctions, initNestServer } from './app/app'; +import { allAppFunctions, allScheduledAppFunctions, initNestServer } from './app/app'; const app = admin.initializeApp(); @@ -10,4 +10,10 @@ const { server, nest } = initNestServer(app, { environment }); export const api = onRequest(server); +// App Functions export const { initUserOnCreate, profileSetUsername, createModel, updateModel, deleteModel } = allAppFunctions(nest); + +// Scheduled Functions +const scheduledFunctions = allScheduledAppFunctions(nest); + +export const { exampleSchedule } = scheduledFunctions; diff --git a/package-lock.json b/package-lock.json index 2963de241..ee3efc212 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dereekb/dbx-components", - "version": "9.8.0", + "version": "9.9.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@dereekb/dbx-components", - "version": "9.8.0", + "version": "9.9.5", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -23,6 +23,7 @@ "@angular/platform-browser-dynamic": "^14.0.0", "@angular/router": "^14.0.0", "@google-cloud/firestore": "^5.0.2", + "@google-cloud/pubsub": "^3.2.0", "@mapbox/geo-viewport": "git+https://git@github.com/dereekb/geo-viewport#165513972f87dca23a20c177f4d173edc51b5e2f", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.0.1", @@ -37,6 +38,7 @@ "@ngrx/store": "^14.0.0", "@ngx-formly/core": "6.0.0-rc.2", "@ngx-formly/material": "6.0.0-rc.2", + "@types/node-schedule": "^2.1.0", "@uirouter/angular": "git+https://git@github.com/dereekb/uirouter-angular#7c4bb71689377b167af1d5bd0a38c91429afc041", "@uirouter/core": "^6.0.8", "@uirouter/rx": "^1.0.0", @@ -51,8 +53,8 @@ "extra-set": "^2.2.11", "firebase": "^9.9.2", "firebase-admin": "^11.0.0", - "firebase-functions": "^3.22.0", - "form-data": "^4.0.0", + "firebase-functions": "^3.24.0", + "form-data": "4.0.0", "jsonwebtoken": "^8.0.0", "linkify-string": "4.0.0-beta.5", "linkifyjs": "^4.0.0-beta.5", @@ -67,6 +69,7 @@ "ngx-infinite-scroll": "^14.0.0", "ngx-mapbox-gl": "^9.1.0", "ngx-mat-intl-tel-input": "^5.0.0", + "node-schedule": "^2.1.0", "reflect-metadata": "^0.1.13", "rrule": "git+https://git@github.com/dereekb/rrule#2b13b1ea881059ba2ecfec380e12ef244ef54570", "rxfire": "^6.0.3", @@ -1413,7 +1416,6 @@ "version": "7.18.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -4332,11 +4334,18 @@ "node": ">=10" } }, + "node_modules/@google-cloud/precise-date": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-3.0.1.tgz", + "integrity": "sha512-crK2rgNFfvLoSgcKJY7ZBOLW91IimVNmPfi1CL+kMTf78pTJYd29XqEVedAeBu4DwCJc0EDIp1MpctLgoPq+Uw==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/@google-cloud/projectify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", - "optional": true, "engines": { "node": ">=12.0.0" } @@ -4350,6 +4359,350 @@ "node": ">=12" } }, + "node_modules/@google-cloud/pubsub": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-3.2.0.tgz", + "integrity": "sha512-QYRbrR7Jk/UnxeU4mYW29/i6WZ8vhzwYH83z55Ki9UFbK0uKL8B0D0o7JMOpk6KRJnmZira48m1dxagv3Av74A==", + "dependencies": { + "@google-cloud/paginator": "^4.0.0", + "@google-cloud/precise-date": "^3.0.0", + "@google-cloud/projectify": "^3.0.0", + "@google-cloud/promisify": "^2.0.0", + "@opentelemetry/api": "^1.0.0", + "@opentelemetry/semantic-conventions": "~1.3.0", + "@types/duplexify": "^3.6.0", + "@types/long": "^4.0.0", + "arrify": "^2.0.0", + "extend": "^3.0.2", + "google-auth-library": "^8.0.2", + "google-gax": "^3.3.0", + "heap-js": "^2.2.0", + "is-stream-ended": "^0.1.4", + "lodash.snakecase": "^4.1.1", + "p-defer": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/@google-cloud/paginator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-4.0.1.tgz", + "integrity": "sha512-6G1ui6bWhNyHjmbYwavdN7mpVPRBtyDg/bfqBTAlwr413On2TnFNfDxc9UhTJctkgoCDgQXEKiRPLPR9USlkbQ==", + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/@google-cloud/promisify": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.4.tgz", + "integrity": "sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/@grpc/grpc-js": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.1.tgz", + "integrity": "sha512-GVtMU4oh/TeKkWGzXUEsyZtyvSUIT1z49RtGH1UnEGeL+sLuxKl8QH3KZTlSB329R1sWJmesm5hQ5CxXdYH9dg==", + "dependencies": { + "@grpc/proto-loader": "^0.7.0", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/@grpc/proto-loader": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.3.tgz", + "integrity": "sha512-5dAvoZwna2Py3Ef96Ux9jIkp3iZ62TUsV00p3wVBPNX5K178UbNi8Q7gQVqwXT1Yq9RejIGG9G2IPEo93T6RcA==", + "dependencies": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^7.0.0", + "yargs": "^16.2.0" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@google-cloud/pubsub/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/google-gax": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-3.5.2.tgz", + "integrity": "sha512-AyP53w0gHcWlzxm+jSgqCR3Xu4Ld7EpSjhtNBnNhzwwWaIUyphH9kBGNIEH+i4UGkTUXOY29K/Re8EiAvkBRGw==", + "dependencies": { + "@grpc/grpc-js": "~1.7.0", + "@grpc/proto-loader": "^0.7.0", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^8.0.2", + "is-stream-ended": "^0.1.4", + "node-fetch": "^2.6.1", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^1.0.0", + "protobufjs": "7.1.2", + "protobufjs-cli": "1.0.2", + "retry-request": "^5.0.0" + }, + "bin": { + "compileProtos": "build/tools/compileProtos.js", + "minifyProtoJson": "build/tools/minify.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/proto3-json-serializer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.0.tgz", + "integrity": "sha512-SjXwUWe/vANGs/mJJTbw5++7U67nwsymg7qsoPtw6GiXqw3kUy8ByojrlEdVE2efxAdKreX8WkDafxvYW95ZQg==", + "dependencies": { + "protobufjs": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/protobufjs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.2.tgz", + "integrity": "sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/protobufjs-cli": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.0.2.tgz", + "integrity": "sha512-cz9Pq9p/Zs7okc6avH20W7QuyjTclwJPgqXG11jNaulfS3nbVisID8rC+prfgq0gbZE0w9LBFd1OKFF03kgFzg==", + "dependencies": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^3.6.3", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "protobufjs": "^7.0.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/protobufjs-cli/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/protobufjs/node_modules/long": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", + "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" + }, + "node_modules/@google-cloud/pubsub/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@google-cloud/pubsub/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, "node_modules/@google-cloud/storage": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.4.0.tgz", @@ -10276,6 +10629,22 @@ "node": ">=8" } }, + "node_modules/@opentelemetry/api": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.2.0.tgz", + "integrity": "sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", + "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==", + "engines": { + "node": ">=8.12.0" + } + }, "node_modules/@panva/asn1.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", @@ -10776,6 +11145,14 @@ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" }, + "node_modules/@types/duplexify": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.1.tgz", + "integrity": "sha512-n0zoEj/fMdMOvqbHxmqnza/kXyoGgJmEpsXjpP+gEqE1Ye4yNqc7xWipKnUoMpWhMuzJQSfK2gMrwlElly7OGQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "8.4.5", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", @@ -10936,6 +11313,11 @@ "@types/node": "*" } }, + "node_modules/@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==" + }, "node_modules/@types/lodash": { "version": "4.14.182", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", @@ -10980,6 +11362,20 @@ "@types/geojson": "*" } }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==" + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -11012,6 +11408,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.48.tgz", "integrity": "sha512-Z9r9UWlNeNkYnxybm+1fc0jxUNjZqRekTAr1pG0qdXe9apT9yCiqk1c4VvKQJsFpnchU4+fLl25MabSLA2wxIw==" }, + "node_modules/@types/node-schedule": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/node-schedule/-/node-schedule-2.1.0.tgz", + "integrity": "sha512-NiTwl8YN3v/1YCKrDFSmCTkVxFDylueEqsOFdgF+vPsm+AlyJKGAo5yzX1FiOxPsZiN6/r8gJitYx2EaSuBmmg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", @@ -11745,7 +12149,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -12965,7 +13368,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -13261,6 +13663,17 @@ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -14584,6 +14997,18 @@ "node": ">=8" } }, + "node_modules/cron-parser": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz", + "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==", + "dependencies": { + "is-nan": "^1.3.2", + "luxon": "^1.26.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -15373,8 +15798,7 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "node_modules/deepmerge": { "version": "4.2.2", @@ -15417,7 +15841,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, "dependencies": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -16952,7 +17375,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -17166,7 +17588,6 @@ "version": "9.3.3", "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", - "dev": true, "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", @@ -17183,7 +17604,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -17250,7 +17670,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -17541,8 +17960,7 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fast-safe-stringify": { "version": "2.1.1", @@ -17804,9 +18222,9 @@ } }, "node_modules/firebase-functions": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-3.22.0.tgz", - "integrity": "sha512-d1BxBpT95MhvVqXkpLWDvWbyuX7e2l69cFAiqG3U1XQDaMV88bM9S+Zg7H8i9pitEGFr+76ErjKgrY0n+g3ZDA==", + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-3.24.0.tgz", + "integrity": "sha512-YKZm/AxjnWTP9VbxAyjs7ImWfMydleQAiHB2T6li3imRCcwC4+h6BXU/Jf2uELz9AkCb+UabWbdVrklk3b+70Q==", "dependencies": { "@types/cors": "^2.8.5", "@types/express": "4.17.3", @@ -32693,7 +33111,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.0.1.tgz", "integrity": "sha512-keK47BGKHyyOVQxgcUaSaFvr3ehZYAlvhvpHXy0YB2itzZef+GqZR8TBsfVRWghdwlKrYsn+8L8i3eblF7Oviw==", - "optional": true, "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", @@ -32708,7 +33125,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.0.0.tgz", "integrity": "sha512-gfwuX3yA3nNsHSWUL4KG90UulNiq922Ukj3wLTrcnX33BB7PwB1o0ubR8KVvXu9nJH+P5w1j2SQSNNqto+H0DA==", - "optional": true, "dependencies": { "gaxios": "^5.0.0", "json-bigint": "^1.0.0" @@ -32975,7 +33391,6 @@ "version": "8.0.3", "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -33080,7 +33495,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.1.1.tgz", "integrity": "sha512-eG3pCfrLgVJe19KhAeZwW0m1LplNEo0FX1GboWf3hu18zD2jq8TUH2K8900AB2YRAuJ7A+1aSXDp1BODjwwRzg==", - "optional": true, "dependencies": { "arrify": "^2.0.0", "base64-js": "^1.3.0", @@ -33100,7 +33514,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "optional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -33234,7 +33647,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.0.tgz", "integrity": "sha512-lRTMn5ElBdDixv4a86bixejPSRk1boRtUowNepeKEVvYiFlkLuAJUVpEz6PfObDHYEKnZWq/9a2zC98xu62A9w==", - "optional": true, "dependencies": { "node-forge": "^1.3.1" }, @@ -33259,7 +33671,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.0.tgz", "integrity": "sha512-WPZcFw34wh2LUvbCUWI70GDhOlO7qHpSvFHFqq7d3Wvsf8dIJedE0lnUdOmsKuC0NgflKmF0LxIF38vsGeHHiQ==", - "optional": true, "dependencies": { "gaxios": "^4.0.0", "google-p12-pem": "^4.0.0", @@ -33273,7 +33684,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz", "integrity": "sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==", - "optional": true, "dependencies": { "abort-controller": "^3.0.0", "extend": "^3.0.2", @@ -33369,7 +33779,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.1" }, @@ -33444,6 +33853,14 @@ "tslib": "^2.0.3" } }, + "node_modules/heap-js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/heap-js/-/heap-js-2.2.0.tgz", + "integrity": "sha512-G3uM72G9F/zo9Hph/T7m4ZZVlVu5bx2f5CiCS78TBHz2mNIXnB5KRdEEYssXZJ7ldLDqID29bZ1D5ezCKQD2Zw==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -34420,6 +34837,21 @@ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -39672,12 +40104,61 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, + "node_modules/jsdoc": { + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "dependencies": { + "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, "node_modules/jsdom": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", @@ -40017,6 +40498,14 @@ "node": ">=0.10.0" } }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -40224,6 +40713,14 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/linkify-string": { "version": "4.0.0-beta.5", "resolved": "https://registry.npmjs.org/linkify-string/-/linkify-string-4.0.0-beta.5.tgz", @@ -40421,6 +40918,11 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -40610,6 +41112,11 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, + "node_modules/long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==" + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -40650,6 +41157,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" }, + "node_modules/luxon": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==", + "engines": { + "node": "*" + } + }, "node_modules/macos-release": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", @@ -40798,12 +41313,65 @@ "vt-pbf": "^3.1.3" } }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", + "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/marked": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", + "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -41015,7 +41583,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -41155,7 +41722,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -41691,6 +42257,19 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, + "node_modules/node-schedule": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.0.tgz", + "integrity": "sha512-nl4JTiZ7ZQDc97MmpTq9BQjYhq7gOtoh7SiPH069gBFBj0PzD8HI7zyFs6rzqL8Y5tTiEEYLxgtbx034YPrbyQ==", + "dependencies": { + "cron-parser": "^3.5.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -42187,7 +42766,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -42446,6 +43024,14 @@ "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", "dev": true }, + "node_modules/p-defer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-3.0.0.tgz", + "integrity": "sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==", + "engines": { + "node": ">=8" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -45202,6 +45788,14 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, + "node_modules/requizzle": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -45360,7 +45954,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.1.tgz", "integrity": "sha512-lxFKrlBt0OZzCWh/V0uPEN0vlr3OhdeXnpeY5OES+ckslm791Cb1D5P7lJUSnY7J5hiCjcyaUGmzCnIGDCUBig==", - "optional": true, "dependencies": { "debug": "^4.1.1", "extend": "^3.0.2" @@ -46457,6 +47050,11 @@ "node": ">= 10" } }, + "node_modules/sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -46931,7 +47529,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "engines": { "node": ">=8" }, @@ -47205,6 +47802,11 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==" + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -48095,12 +48697,15 @@ "node": ">=4.2.0" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, "node_modules/uglify-js": { "version": "3.16.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.3.tgz", "integrity": "sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==", - "dev": true, - "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" }, @@ -48123,6 +48728,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -48958,7 +49568,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -49073,6 +49682,11 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -50061,8 +50675,7 @@ "@babel/parser": { "version": "7.18.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", - "dev": true + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -52171,11 +52784,15 @@ "extend": "^3.0.2" } }, + "@google-cloud/precise-date": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-3.0.1.tgz", + "integrity": "sha512-crK2rgNFfvLoSgcKJY7ZBOLW91IimVNmPfi1CL+kMTf78pTJYd29XqEVedAeBu4DwCJc0EDIp1MpctLgoPq+Uw==" + }, "@google-cloud/projectify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", - "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", - "optional": true + "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==" }, "@google-cloud/promisify": { "version": "3.0.0", @@ -52183,6 +52800,260 @@ "integrity": "sha512-91ArYvRgXWb73YvEOBMmOcJc0bDRs5yiVHnqkwoG0f3nm7nZuipllz6e7BvFESBvjkDTBC0zMD8QxedUwNLc1A==", "optional": true }, + "@google-cloud/pubsub": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-3.2.0.tgz", + "integrity": "sha512-QYRbrR7Jk/UnxeU4mYW29/i6WZ8vhzwYH83z55Ki9UFbK0uKL8B0D0o7JMOpk6KRJnmZira48m1dxagv3Av74A==", + "requires": { + "@google-cloud/paginator": "^4.0.0", + "@google-cloud/precise-date": "^3.0.0", + "@google-cloud/projectify": "^3.0.0", + "@google-cloud/promisify": "^2.0.0", + "@opentelemetry/api": "^1.0.0", + "@opentelemetry/semantic-conventions": "~1.3.0", + "@types/duplexify": "^3.6.0", + "@types/long": "^4.0.0", + "arrify": "^2.0.0", + "extend": "^3.0.2", + "google-auth-library": "^8.0.2", + "google-gax": "^3.3.0", + "heap-js": "^2.2.0", + "is-stream-ended": "^0.1.4", + "lodash.snakecase": "^4.1.1", + "p-defer": "^3.0.0" + }, + "dependencies": { + "@google-cloud/paginator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-4.0.1.tgz", + "integrity": "sha512-6G1ui6bWhNyHjmbYwavdN7mpVPRBtyDg/bfqBTAlwr413On2TnFNfDxc9UhTJctkgoCDgQXEKiRPLPR9USlkbQ==", + "requires": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + } + }, + "@google-cloud/promisify": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.4.tgz", + "integrity": "sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA==" + }, + "@grpc/grpc-js": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.1.tgz", + "integrity": "sha512-GVtMU4oh/TeKkWGzXUEsyZtyvSUIT1z49RtGH1UnEGeL+sLuxKl8QH3KZTlSB329R1sWJmesm5hQ5CxXdYH9dg==", + "requires": { + "@grpc/proto-loader": "^0.7.0", + "@types/node": ">=12.12.47" + } + }, + "@grpc/proto-loader": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.3.tgz", + "integrity": "sha512-5dAvoZwna2Py3Ef96Ux9jIkp3iZ62TUsV00p3wVBPNX5K178UbNi8Q7gQVqwXT1Yq9RejIGG9G2IPEo93T6RcA==", + "requires": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^7.0.0", + "yargs": "^16.2.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "google-gax": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-3.5.2.tgz", + "integrity": "sha512-AyP53w0gHcWlzxm+jSgqCR3Xu4Ld7EpSjhtNBnNhzwwWaIUyphH9kBGNIEH+i4UGkTUXOY29K/Re8EiAvkBRGw==", + "requires": { + "@grpc/grpc-js": "~1.7.0", + "@grpc/proto-loader": "^0.7.0", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "fast-text-encoding": "^1.0.3", + "google-auth-library": "^8.0.2", + "is-stream-ended": "^0.1.4", + "node-fetch": "^2.6.1", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^1.0.0", + "protobufjs": "7.1.2", + "protobufjs-cli": "1.0.2", + "retry-request": "^5.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" + }, + "proto3-json-serializer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.0.tgz", + "integrity": "sha512-SjXwUWe/vANGs/mJJTbw5++7U67nwsymg7qsoPtw6GiXqw3kUy8ByojrlEdVE2efxAdKreX8WkDafxvYW95ZQg==", + "requires": { + "protobufjs": "^7.0.0" + } + }, + "protobufjs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.2.tgz", + "integrity": "sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "dependencies": { + "long": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", + "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" + } + } + }, + "protobufjs-cli": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.0.2.tgz", + "integrity": "sha512-cz9Pq9p/Zs7okc6avH20W7QuyjTclwJPgqXG11jNaulfS3nbVisID8rC+prfgq0gbZE0w9LBFd1OKFF03kgFzg==", + "requires": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^3.6.3", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + } + } + }, "@google-cloud/storage": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.4.0.tgz", @@ -56646,6 +57517,16 @@ } } }, + "@opentelemetry/api": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.2.0.tgz", + "integrity": "sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g==" + }, + "@opentelemetry/semantic-conventions": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz", + "integrity": "sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==" + }, "@panva/asn1.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", @@ -57038,6 +57919,14 @@ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" }, + "@types/duplexify": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.1.tgz", + "integrity": "sha512-n0zoEj/fMdMOvqbHxmqnza/kXyoGgJmEpsXjpP+gEqE1Ye4yNqc7xWipKnUoMpWhMuzJQSfK2gMrwlElly7OGQ==", + "requires": { + "@types/node": "*" + } + }, "@types/eslint": { "version": "8.4.5", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", @@ -57197,6 +58086,11 @@ "@types/node": "*" } }, + "@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==" + }, "@types/lodash": { "version": "4.14.182", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", @@ -57241,6 +58135,20 @@ "@types/geojson": "*" } }, + "@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "requires": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==" + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -57272,6 +58180,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.48.tgz", "integrity": "sha512-Z9r9UWlNeNkYnxybm+1fc0jxUNjZqRekTAr1pG0qdXe9apT9yCiqk1c4VvKQJsFpnchU4+fLl25MabSLA2wxIw==" }, + "@types/node-schedule": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/node-schedule/-/node-schedule-2.1.0.tgz", + "integrity": "sha512-NiTwl8YN3v/1YCKrDFSmCTkVxFDylueEqsOFdgF+vPsm+AlyJKGAo5yzX1FiOxPsZiN6/r8gJitYx2EaSuBmmg==", + "requires": { + "@types/node": "*" + } + }, "@types/normalize-package-data": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", @@ -57835,7 +58751,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "requires": {} }, "acorn-walk": { @@ -58766,7 +59681,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "requires": { "balanced-match": "^1.0.0" } @@ -58979,6 +59893,14 @@ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, + "catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "requires": { + "lodash": "^4.17.15" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -60017,6 +60939,15 @@ } } }, + "cron-parser": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-3.5.0.tgz", + "integrity": "sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==", + "requires": { + "is-nan": "^1.3.2", + "luxon": "^1.26.0" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -60587,8 +61518,7 @@ "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "deepmerge": { "version": "4.2.2", @@ -60622,7 +61552,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, "requires": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -61842,14 +62771,12 @@ "eslint-visitor-keys": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" }, "espree": { "version": "9.3.3", "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", - "dev": true, "requires": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", @@ -61859,8 +62786,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.4.0", @@ -61908,8 +62834,7 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "etag": { "version": "1.8.1", @@ -62136,8 +63061,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "fast-safe-stringify": { "version": "2.1.1", @@ -62352,9 +63276,9 @@ } }, "firebase-functions": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-3.22.0.tgz", - "integrity": "sha512-d1BxBpT95MhvVqXkpLWDvWbyuX7e2l69cFAiqG3U1XQDaMV88bM9S+Zg7H8i9pitEGFr+76ErjKgrY0n+g3ZDA==", + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-3.24.0.tgz", + "integrity": "sha512-YKZm/AxjnWTP9VbxAyjs7ImWfMydleQAiHB2T6li3imRCcwC4+h6BXU/Jf2uELz9AkCb+UabWbdVrklk3b+70Q==", "requires": { "@types/cors": "^2.8.5", "@types/express": "4.17.3", @@ -74219,7 +75143,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.0.1.tgz", "integrity": "sha512-keK47BGKHyyOVQxgcUaSaFvr3ehZYAlvhvpHXy0YB2itzZef+GqZR8TBsfVRWghdwlKrYsn+8L8i3eblF7Oviw==", - "optional": true, "requires": { "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", @@ -74231,7 +75154,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.0.0.tgz", "integrity": "sha512-gfwuX3yA3nNsHSWUL4KG90UulNiq922Ukj3wLTrcnX33BB7PwB1o0ubR8KVvXu9nJH+P5w1j2SQSNNqto+H0DA==", - "optional": true, "requires": { "gaxios": "^5.0.0", "json-bigint": "^1.0.0" @@ -74447,7 +75369,6 @@ "version": "8.0.3", "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -74526,7 +75447,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.1.1.tgz", "integrity": "sha512-eG3pCfrLgVJe19KhAeZwW0m1LplNEo0FX1GboWf3hu18zD2jq8TUH2K8900AB2YRAuJ7A+1aSXDp1BODjwwRzg==", - "optional": true, "requires": { "arrify": "^2.0.0", "base64-js": "^1.3.0", @@ -74543,7 +75463,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "optional": true, "requires": { "yallist": "^4.0.0" } @@ -74648,7 +75567,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.0.tgz", "integrity": "sha512-lRTMn5ElBdDixv4a86bixejPSRk1boRtUowNepeKEVvYiFlkLuAJUVpEz6PfObDHYEKnZWq/9a2zC98xu62A9w==", - "optional": true, "requires": { "node-forge": "^1.3.1" } @@ -74667,7 +75585,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.0.tgz", "integrity": "sha512-WPZcFw34wh2LUvbCUWI70GDhOlO7qHpSvFHFqq7d3Wvsf8dIJedE0lnUdOmsKuC0NgflKmF0LxIF38vsGeHHiQ==", - "optional": true, "requires": { "gaxios": "^4.0.0", "google-p12-pem": "^4.0.0", @@ -74678,7 +75595,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz", "integrity": "sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==", - "optional": true, "requires": { "abort-controller": "^3.0.0", "extend": "^3.0.2", @@ -74752,7 +75668,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, "requires": { "get-intrinsic": "^1.1.1" } @@ -74809,6 +75724,11 @@ "tslib": "^2.0.3" } }, + "heap-js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/heap-js/-/heap-js-2.2.0.tgz", + "integrity": "sha512-G3uM72G9F/zo9Hph/T7m4ZZVlVu5bx2f5CiCS78TBHz2mNIXnB5KRdEEYssXZJ7ldLDqID29bZ1D5ezCKQD2Zw==" + }, "hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -75531,6 +76451,15 @@ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -79613,12 +80542,54 @@ "esprima": "^4.0.0" } }, + "js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "requires": { + "xmlcreate": "^2.0.4" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, + "jsdoc": { + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "requires": { + "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.13.2" + }, + "dependencies": { + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } + }, "jsdom": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", @@ -79915,6 +80886,14 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "requires": { + "graceful-fs": "^4.1.9" + } + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -80061,6 +81040,14 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "requires": { + "uc.micro": "^1.0.1" + } + }, "linkify-string": { "version": "4.0.0-beta.5", "resolved": "https://registry.npmjs.org/linkify-string/-/linkify-string-4.0.0-beta.5.tgz", @@ -80223,6 +81210,11 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -80367,6 +81359,11 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, + "long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==" + }, "lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -80406,6 +81403,11 @@ } } }, + "luxon": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" + }, "macos-release": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", @@ -80531,12 +81533,52 @@ "vt-pbf": "^3.1.3" } }, + "markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "requires": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" + } + } + }, + "markdown-it-anchor": { + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", + "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", + "requires": {} + }, + "marked": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz", + "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==" + }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -80684,7 +81726,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dev": true, "requires": { "brace-expansion": "^2.0.1" } @@ -80793,8 +81834,7 @@ "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "mockdate": { "version": "3.0.5", @@ -81209,6 +82249,16 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, + "node-schedule": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.0.tgz", + "integrity": "sha512-nl4JTiZ7ZQDc97MmpTq9BQjYhq7gOtoh7SiPH069gBFBj0PzD8HI7zyFs6rzqL8Y5tTiEEYLxgtbx034YPrbyQ==", + "requires": { + "cron-parser": "^3.5.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" + } + }, "nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -81582,8 +82632,7 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { "version": "4.1.3", @@ -81769,6 +82818,11 @@ "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", "dev": true }, + "p-defer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-3.0.0.tgz", + "integrity": "sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==" + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -83710,6 +84764,14 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, + "requizzle": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "requires": { + "lodash": "^4.17.14" + } + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -83833,7 +84895,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.1.tgz", "integrity": "sha512-lxFKrlBt0OZzCWh/V0uPEN0vlr3OhdeXnpeY5OES+ckslm791Cb1D5P7lJUSnY7J5hiCjcyaUGmzCnIGDCUBig==", - "optional": true, "requires": { "debug": "^4.1.1", "extend": "^3.0.2" @@ -84686,6 +85747,11 @@ "socks": "^2.6.2" } }, + "sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" + }, "source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -85063,8 +86129,7 @@ "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, "stripe": { "version": "9.16.0", @@ -85263,6 +86328,11 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==" + }, "tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -85919,12 +86989,15 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==" }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, "uglify-js": { "version": "3.16.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.3.tgz", - "integrity": "sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==", - "dev": true, - "optional": true + "integrity": "sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==" }, "unbox-primitive": { "version": "1.0.2", @@ -85938,6 +87011,11 @@ "which-boxed-primitive": "^1.0.2" } }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -86560,8 +87638,7 @@ "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, "wordwrap": { "version": "1.0.0", @@ -86643,6 +87720,11 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 73e4e0566..4994b9cb4 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@angular/platform-browser-dynamic": "^14.0.0", "@angular/router": "^14.0.0", "@google-cloud/firestore": "^5.0.2", + "@google-cloud/pubsub": "^3.2.0", "@mapbox/geo-viewport": "git+https://git@github.com/dereekb/geo-viewport#165513972f87dca23a20c177f4d173edc51b5e2f", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.0.1", @@ -39,6 +40,7 @@ "@ngrx/store": "^14.0.0", "@ngx-formly/core": "6.0.0-rc.2", "@ngx-formly/material": "6.0.0-rc.2", + "@types/node-schedule": "^2.1.0", "@uirouter/angular": "git+https://git@github.com/dereekb/uirouter-angular#7c4bb71689377b167af1d5bd0a38c91429afc041", "@uirouter/core": "^6.0.8", "@uirouter/rx": "^1.0.0", @@ -53,14 +55,14 @@ "extra-set": "^2.2.11", "firebase": "^9.9.2", "firebase-admin": "^11.0.0", - "firebase-functions": "^3.22.0", + "firebase-functions": "^3.24.0", + "form-data": "4.0.0", "jsonwebtoken": "^8.0.0", "linkify-string": "4.0.0-beta.5", "linkifyjs": "^4.0.0-beta.5", "lodash.clonedeep": "^4.5.0", "lodash.isequal": "^4.5.0", "mailgun.js": "^8.0.0", - "form-data": "4.0.0", "make-error": "^1.3.0", "mapbox-gl": "^2.9.2", "ms": "^3.0.0-canary.1", @@ -69,6 +71,7 @@ "ngx-infinite-scroll": "^14.0.0", "ngx-mapbox-gl": "^9.1.0", "ngx-mat-intl-tel-input": "^5.0.0", + "node-schedule": "^2.1.0", "reflect-metadata": "^0.1.13", "rrule": "git+https://git@github.com/dereekb/rrule#2b13b1ea881059ba2ecfec380e12ef244ef54570", "rxfire": "^6.0.3", diff --git a/packages/firebase-server/src/lib/env/env.service.ts b/packages/firebase-server/src/lib/env/env.service.ts index 842388ca3..695756835 100644 --- a/packages/firebase-server/src/lib/env/env.service.ts +++ b/packages/firebase-server/src/lib/env/env.service.ts @@ -9,6 +9,7 @@ export abstract class FirebaseServerEnvService { abstract readonly isTestingEnv: boolean; abstract readonly isProduction: boolean; abstract readonly developerToolsEnabled: boolean; + abstract readonly developmentSchedulerEnabled: boolean; // MARK: Compat /** * @deprecated use isDeveloperToolsEnabled instead. diff --git a/packages/firebase-server/src/lib/nest/env/env.service.ts b/packages/firebase-server/src/lib/nest/env/env.service.ts index 1554cb0a8..164b61f2b 100644 --- a/packages/firebase-server/src/lib/nest/env/env.service.ts +++ b/packages/firebase-server/src/lib/nest/env/env.service.ts @@ -4,6 +4,13 @@ import { FirebaseServerEnvService } from '../../env'; @Injectable() export class DefaultFirebaseServerEnvService extends ServerEnvironmentService implements FirebaseServerEnvService { + /** + * Enabled when not in production and not in a testing environment. + */ + get developmentSchedulerEnabled() { + return !this.isProduction && !this.isTestingEnv; + } + /** * @deprecated use isDeveloperToolsEnabled instead.z */ diff --git a/packages/firebase-server/src/lib/nest/env/env.util.ts b/packages/firebase-server/src/lib/nest/env/env.util.ts new file mode 100644 index 000000000..53c975a1b --- /dev/null +++ b/packages/firebase-server/src/lib/nest/env/env.util.ts @@ -0,0 +1,11 @@ +import { AsyncDecisionFunction } from '@dereekb/util'; +import { FirebaseServerEnvService } from '../../env/env.service'; +import { NestAppPromiseGetter } from '../app'; + +export function nestAppIsProductionEnvironment(nest: NestAppPromiseGetter): AsyncDecisionFunction { + return () => nest().then((x) => x.get(FirebaseServerEnvService).isProduction); +} + +export function nestAppHasDevelopmentSchedulerEnabled(nest: NestAppPromiseGetter): AsyncDecisionFunction { + return () => nest().then((x) => x.get(FirebaseServerEnvService).developmentSchedulerEnabled); +} diff --git a/packages/firebase-server/src/lib/nest/env/index.ts b/packages/firebase-server/src/lib/nest/env/index.ts index e3a1e3f39..6c7295b2d 100644 --- a/packages/firebase-server/src/lib/nest/env/index.ts +++ b/packages/firebase-server/src/lib/nest/env/index.ts @@ -1,3 +1,4 @@ export * from './env.config'; export * from './env.nest'; export * from './env.service'; +export * from './env.util'; diff --git a/packages/firebase-server/src/lib/nest/function/index.ts b/packages/firebase-server/src/lib/nest/function/index.ts index a70e0b971..02960db77 100644 --- a/packages/firebase-server/src/lib/nest/function/index.ts +++ b/packages/firebase-server/src/lib/nest/function/index.ts @@ -3,3 +3,5 @@ export * from './v2'; export * from './context'; export * from './call'; export * from './nest'; +export * from './schedule'; +export * from './schedule.util'; diff --git a/packages/firebase-server/src/lib/nest/function/schedule.ts b/packages/firebase-server/src/lib/nest/function/schedule.ts new file mode 100644 index 000000000..71aac6213 --- /dev/null +++ b/packages/firebase-server/src/lib/nest/function/schedule.ts @@ -0,0 +1,75 @@ +import { Configurable, Minutes, PromiseOrValue } from '@dereekb/util'; +import { MakeNestContext, NestApplicationFunctionFactory } from '../nest.provider'; +import { NestApplicationContextRequest, NestContextRequest } from './nest'; + +export interface OnScheduleConfig { + /** + * Scheduled time in a cron-tab format. + * + * Used by v1 and v2. + */ + cron?: Minutes | string; + /** + * Scheduled time configuration in an english format. + * + * Used by v2. + */ + schedule?: string; + /** + * Optional timezone to specify. + */ + timezone?: string; + /** + * The number of retry attempts for a failed run. + */ + retryCount?: number; + /** + * The time limit for retrying. + */ + maxRetrySeconds?: number; + /** + * The minimum time to wait before retying. + */ + minBackoffSeconds?: number; + /** + * The maximum time to wait before retrying. + */ + maxBackoffSeconds?: number; + /** + * The time between will double max doublings times. + */ + maxDoublings?: number; +} + +export interface OnScheduleRequest { + scheduleContext?: S; +} + +// MARK: Application +export type OnScheduleWithNestApplicationRequest = NestApplicationContextRequest>; + +/** + * Scheduled function that is passed an INestApplicationContext in addition to the OnScheduleRequest object. + */ +export type OnScheduleWithNestApplication = (request: OnScheduleWithNestApplicationRequest) => PromiseOrValue; + +// MARK: Context +export type OnScheduleWithNestContextRequest = NestContextRequest>; + +/** + * Scheduled function that is passed an arbitrary nest context object in addition to the OnScheduleRequest object. + */ +export type OnScheduleWithNestContext = (request: OnScheduleWithNestContextRequest) => PromiseOrValue; + +export function setNestContextOnScheduleRequest(makeNestContext: MakeNestContext, request: OnScheduleWithNestApplicationRequest): OnScheduleWithNestContextRequest { + (request as unknown as Configurable>).nest = makeNestContext(request.nestApplication); + return request as unknown as OnScheduleWithNestContextRequest; +} + +// MARK: Factory +export type NestApplicationScheduleConfiguredFunction = F & { + _runNow(): PromiseOrValue; + readonly _schedule: OnScheduleConfig; +}; + +export type NestApplicationScheduleConfiguredFunctionFactory = NestApplicationFunctionFactory>; diff --git a/packages/firebase-server/src/lib/nest/function/schedule.util.ts b/packages/firebase-server/src/lib/nest/function/schedule.util.ts new file mode 100644 index 000000000..27aef65a4 --- /dev/null +++ b/packages/firebase-server/src/lib/nest/function/schedule.util.ts @@ -0,0 +1,86 @@ +import { AsyncDecisionFunction, CronExpression, cronExpressionRepeatingEveryNMinutes, mapObjectMap, MappedObjectMap, Minutes } from '@dereekb/util'; +import { scheduleJob, Job, RecurrenceRule } from 'node-schedule'; +import { NestApplicationScheduleConfiguredFunction } from './schedule'; +import { PubSub } from '@google-cloud/pubsub'; + +// MARK: Utilities +export type NestApplicationScheduleConfiguredFunctionMap = { + [key: string]: NestApplicationScheduleConfiguredFunction; +}; + +export type NestApplicationScheduleConfiguredFunctionJobMap = MappedObjectMap; + +export type ScheduledRecurrenceValue = Minutes | CronExpression | RecurrenceRule; + +/** + * Configuration for initEnvironmentForScheduleConfiguredFunctions() + */ +export interface InitEnvironmentForScheduleConfiguredFunctionsConfig { + /** + * Function to check whether or not to enable the dev schedule. + * + * If not provided, then the initialization is skipped. + */ + checkEnabled?: AsyncDecisionFunction; + /** + * Runs all cron jobs at the given interval in minutes. + */ + overrideAll?: ScheduledRecurrenceValue; + /** + * Override specific function's times. + */ + override?: Partial>; + /** + * Runs all jobs that have a readable schedule on the given + */ + scheduleCron?: ScheduledRecurrenceValue; +} + +/** + * Initializes the current environment to handle the input scheduled functions. + * + * - For production, nothing will be configured. + * - For development, + * + * @param input + * @returns + */ +export function initEnvironmentForScheduleConfiguredFunctions(input: T, config: InitEnvironmentForScheduleConfiguredFunctionsConfig): T { + const { checkEnabled } = config; + + if (checkEnabled != null) { + setTimeout(async () => { + const isEnabled = await checkEnabled(); + if (isEnabled) { + console.log('Initializing scheduled functions for development environment...'); + initDevelopmentEnvironmentForScheduleConfiguredFunctions(input, config); + } + }, 100); + } + + return input; +} + +/** + * Initializes jobs for each scheduled task in the input map. + * + * @param input + * @param config + * @returns + */ +export function initDevelopmentEnvironmentForScheduleConfiguredFunctions(input: T, config: InitEnvironmentForScheduleConfiguredFunctionsConfig): NestApplicationScheduleConfiguredFunctionJobMap { + const { overrideAll, override = {} as Partial>, scheduleCron } = config; + + const result = mapObjectMap(input, (entry, key) => { + const schedule = entry._schedule; + let cron: ScheduledRecurrenceValue = overrideAll ?? override[key] ?? schedule.cron ?? scheduleCron ?? 1; + + if (typeof cron === 'number') { + cron = cronExpressionRepeatingEveryNMinutes(cron); + } + + return scheduleJob(cron, entry._runNow); + }); + + return result; +} diff --git a/packages/firebase-server/src/lib/nest/function/v1/index.ts b/packages/firebase-server/src/lib/nest/function/v1/index.ts index 05b40b5e6..ece947f58 100644 --- a/packages/firebase-server/src/lib/nest/function/v1/index.ts +++ b/packages/firebase-server/src/lib/nest/function/v1/index.ts @@ -1,2 +1,3 @@ export * from './call'; export * from './event'; +export * from './schedule'; diff --git a/packages/firebase-server/src/lib/nest/function/v1/nest.spec.ts b/packages/firebase-server/src/lib/nest/function/v1/nest.spec.ts index 7af20afc5..3288bddc6 100644 --- a/packages/firebase-server/src/lib/nest/function/v1/nest.spec.ts +++ b/packages/firebase-server/src/lib/nest/function/v1/nest.spec.ts @@ -6,6 +6,8 @@ import { onEventWithNestApplicationFactory, NestApplicationEventHandler, OnEvent import * as functions from 'firebase-functions'; import { NestApplicationFunctionFactory } from '../../nest.provider'; import { OnCallWithNestApplication } from '../call'; +import { OnScheduleConfig, OnScheduleWithNestApplication } from '../schedule'; +import { onScheduleWithNestApplicationFactory } from './schedule'; @Injectable() export class TestInjectable {} @@ -121,5 +123,52 @@ describe('nest function utilities', () => { expect(retrievedNestApplication).toBe(true); }); }); + + describe('onScheduleWithNestApplicationFactory()', () => { + it('should create a factory.', () => { + const factory = onScheduleWithNestApplicationFactory(); + expect(typeof factory).toBe('function'); + }); + + it('should retrieve the module.', async () => { + const expectedValue = 0; + + // This creates a factory that we can pass handlers to, which will return another factory that accepts a nestAppGetter for our INestApplication. + // This is to allow us to create all our functions for our app without being bound to a specific nest context, which could make testing more difficult. + const factory = onScheduleWithNestApplicationFactory(); + + let retrievedNestApplication = false; // use as a flag for our tests. + const testData = { test: true }; // use as the test data to be passed to our handler. + + // Our actual handler function that is invoked by our applications. + const handler: OnScheduleWithNestApplication = (request) => { + expect(request).toBeDefined(); + expect(request.nestApplication).toBeDefined(); + retrievedNestApplication = true; + }; + + const scheduleConfig: OnScheduleConfig = { + schedule: '' + }; + + // Create our runnable factory. + // This type will take in a NestApplicationPromiseGetter to build the final runnable. + const runnableFactory: NestApplicationFunctionFactory = factory(scheduleConfig, handler); + + // For our tests, we pass it the testing context's nest getter. + // This runnable is now the cloud function that the "firebase-functions" library can consume. + const runnable = runnableFactory(f.nestAppPromiseGetter); + + // For our tests, we use the "firebase-functions-test" wrap function to wrap it once more into a function we can use. + // We can now execute this test function against the emulators and in our test nest context. + const testFunction = f.fnWrapper.wrapV1CloudFunction(runnable); + + // Now we test the wrapped function. This should call our handler. + const result = await testFunction(testData); + + expect(result).toBe(expectedValue); + expect(retrievedNestApplication).toBe(true); + }); + }); }); }); diff --git a/packages/firebase-server/src/lib/nest/function/v1/schedule.ts b/packages/firebase-server/src/lib/nest/function/v1/schedule.ts new file mode 100644 index 000000000..8f988cc1e --- /dev/null +++ b/packages/firebase-server/src/lib/nest/function/v1/schedule.ts @@ -0,0 +1,73 @@ +import * as functions from 'firebase-functions'; +import { INestApplicationContext } from '@nestjs/common'; +import { MakeNestContext, NestApplicationFunctionFactory, NestApplicationPromiseGetter } from '../../nest.provider'; +import { NestApplicationScheduleConfiguredFunction, NestApplicationScheduleConfiguredFunctionFactory, OnScheduleConfig, OnScheduleWithNestApplication, OnScheduleWithNestApplicationRequest, OnScheduleWithNestContext, setNestContextOnScheduleRequest } from '../schedule'; +import { cronExpressionRepeatingEveryNMinutes, CronExpression, mergeObjects } from '@dereekb/util'; +import { Buildable } from 'ts-essentials'; + +export function makeOnScheduleWithNestApplicationRequest(nestApplication: INestApplicationContext, scheduleContext?: functions.EventContext): OnScheduleWithNestApplicationRequest { + return { + nestApplication, + scheduleContext + }; +} + +// TODO: use functions.pubsub.schedule to configure tasks. Have one that uses the nest context, and one that optionally does not. +// Factory should return a function that can also be called directly for immediate execution. + +// MARK: Nest +export type NestApplicationScheduleCloudFunctionFactory = NestApplicationScheduleConfiguredFunctionFactory>; + +/** + * Factory function for generating a NestApplicationFunctionFactory for a HttpsFunctions/Runnable firebase function. + */ +export type OnScheduleWithNestApplicationFactory = (schedule: OnScheduleConfig, fn: OnScheduleWithNestApplication) => NestApplicationScheduleCloudFunctionFactory; + +/** + * Creates a factory for generating OnCallWithNestApplication functions. + * + * @param nestAppPromiseGetter + * @returns + */ +export function onScheduleWithNestApplicationFactory(baseSchedule?: OnScheduleConfig): OnScheduleWithNestApplicationFactory { + return (inputSchedule: OnScheduleConfig, fn: OnScheduleWithNestApplication) => { + const schedule = mergeObjects([baseSchedule, inputSchedule]) as OnScheduleConfig; + let cron: CronExpression; + + if (!schedule.cron) { + throw new Error('Missing required "cron" variable for configuration.'); + } else if (typeof schedule.cron === 'number') { + cron = cronExpressionRepeatingEveryNMinutes(schedule.cron); + } + + return (nestAppPromiseGetter: NestApplicationPromiseGetter) => { + let builder = functions.pubsub.schedule(schedule.cron as string).retryConfig(schedule); + + if (schedule.timezone) { + builder = builder.timeZone(schedule.timezone); + } + + const runNow = (scheduleContext?: functions.EventContext) => nestAppPromiseGetter().then((x) => fn(makeOnScheduleWithNestApplicationRequest(x, scheduleContext))); + const fnn: Buildable>>> = builder.onRun(runNow); + fnn._schedule = schedule; + fnn._runNow = runNow; + return fnn as NestApplicationScheduleConfiguredFunction>; + }; + }; +} + +/** + * Factory function for generating HttpsFunctions/Runnable firebase function that returns the value from the input OnCallWithNestContext function. + */ +export type OnScheduleWithNestContextFactory = (schedule: OnScheduleConfig, fn: OnScheduleWithNestContext) => NestApplicationScheduleCloudFunctionFactory; + +/** + * Creates a factory for generating OnCallWithNestContext functions with a nest context object that is generated by the input function. + * + * @param appFactory + * @param makeNestContext + * @returns + */ +export function onScheduleWithNestContextFactory(appFactory: OnScheduleWithNestApplicationFactory, makeNestContext: MakeNestContext): OnScheduleWithNestContextFactory { + return (schedule: OnScheduleConfig, fn: OnScheduleWithNestContext) => appFactory(schedule, (request: OnScheduleWithNestApplicationRequest) => fn(setNestContextOnScheduleRequest(makeNestContext, request))); +} diff --git a/packages/firebase-server/src/lib/nest/function/v2/index.ts b/packages/firebase-server/src/lib/nest/function/v2/index.ts index c2fcf8107..1780f3aa7 100644 --- a/packages/firebase-server/src/lib/nest/function/v2/index.ts +++ b/packages/firebase-server/src/lib/nest/function/v2/index.ts @@ -1,4 +1,5 @@ export * from './blocking'; export * from './call'; export * from './event'; +export * from './schedule'; export * from './taskqueue'; diff --git a/packages/firebase-server/src/lib/nest/function/v2/schedule.ts b/packages/firebase-server/src/lib/nest/function/v2/schedule.ts new file mode 100644 index 000000000..9abfa7e0e --- /dev/null +++ b/packages/firebase-server/src/lib/nest/function/v2/schedule.ts @@ -0,0 +1,71 @@ +import { GlobalOptions } from 'firebase-functions/v2/options'; +import { scheduler } from 'firebase-functions/v2'; +import { INestApplicationContext } from '@nestjs/common'; +import { MakeNestContext, NestApplicationFunctionFactory, NestApplicationPromiseGetter } from '../../nest.provider'; +import { NestApplicationScheduleConfiguredFunction, NestApplicationScheduleConfiguredFunctionFactory, OnScheduleConfig, OnScheduleWithNestApplication, OnScheduleWithNestApplicationRequest, OnScheduleWithNestContext, setNestContextOnScheduleRequest } from '../schedule'; +import { cronExpressionRepeatingEveryNMinutes, mergeObjects } from '@dereekb/util'; +import { Buildable } from 'ts-essentials'; +import { ScheduleOptions } from 'firebase-functions/v2/scheduler'; + +export type OnScheduleConfigWithGlobalOptions = OnScheduleConfig & GlobalOptions; + +export function makeOnScheduleHandlerWithNestApplicationRequest(nestApplication: INestApplicationContext, scheduleContext?: scheduler.ScheduledEvent): OnScheduleWithNestApplicationRequest { + return { + nestApplication, + scheduleContext + }; +} + +// MARK: Nest +export type NestApplicationScheduleFunctionFactory = NestApplicationScheduleConfiguredFunctionFactory; + +/** + * Factory function for generating a NestApplicationFunctionFactory for a HttpsFunctions/Runnable firebase function. + */ +export type OnScheduleHandlerWithNestApplicationFactory = (schedule: OnScheduleConfig, fn: OnScheduleWithNestApplication) => NestApplicationScheduleFunctionFactory; + +/** + * Creates a factory for generating OnCallWithNestApplication functions. + * + * @param nestAppPromiseGetter + * @returns + */ +export function onScheduleHandlerWithNestApplicationFactory(baseSchedule?: OnScheduleConfig): OnScheduleHandlerWithNestApplicationFactory { + return (inputSchedule: OnScheduleConfig, fn: OnScheduleWithNestApplication) => { + const schedule = mergeObjects([baseSchedule, inputSchedule]) as OnScheduleConfig; + + if (!schedule.schedule) { + if (schedule.cron) { + if (typeof schedule.cron === 'number') { + schedule.schedule = cronExpressionRepeatingEveryNMinutes(schedule.cron); + } + } else { + throw new Error('Missing required "cron" or "schedule" variable for configuration.'); + } + } + + return (nestAppPromiseGetter: NestApplicationPromiseGetter) => { + const runNow = (scheduleContext?: scheduler.ScheduledEvent) => nestAppPromiseGetter().then((x) => fn(makeOnScheduleHandlerWithNestApplicationRequest(x, scheduleContext))); + const fnn: Buildable>> = scheduler.onSchedule(schedule as ScheduleOptions, runNow); + fnn._schedule = schedule; + fnn._runNow = runNow; + return fnn as NestApplicationScheduleConfiguredFunction; + }; + }; +} + +/** + * Factory function for generating HttpsFunctions/Runnable firebase function that returns the value from the input OnCallWithNestContext function. + */ +export type OnScheduleHandlerWithNestContextFactory = (schedule: OnScheduleConfig, fn: OnScheduleWithNestContext) => NestApplicationScheduleFunctionFactory; + +/** + * Creates a factory for generating OnCallWithNestContext functions with a nest context object that is generated by the input function. + * + * @param appFactory + * @param makeNestContext + * @returns + */ +export function onScheduleHandlerWithNestContextFactory(appFactory: OnScheduleHandlerWithNestApplicationFactory, makeNestContext: MakeNestContext): OnScheduleHandlerWithNestContextFactory { + return (schedule: OnScheduleConfig, fn: OnScheduleWithNestContext) => appFactory(schedule, (request: OnScheduleWithNestApplicationRequest) => fn(setNestContextOnScheduleRequest(makeNestContext, request))); +} diff --git a/packages/firebase-server/src/lib/nest/function/v2/taskqueue.ts b/packages/firebase-server/src/lib/nest/function/v2/taskqueue.ts index d48661f94..50f141da4 100644 --- a/packages/firebase-server/src/lib/nest/function/v2/taskqueue.ts +++ b/packages/firebase-server/src/lib/nest/function/v2/taskqueue.ts @@ -43,3 +43,5 @@ export function taskQueueFunctionHandlerWithNestContextFactory(makeNestContex }; }; } + +// TODO: Add factory that also adds onTaskDispatched usage, as the above is incomplete for full usage and only sets up a function for the request. diff --git a/packages/util/src/lib/value/cron.ts b/packages/util/src/lib/value/cron.ts new file mode 100644 index 000000000..57262e846 --- /dev/null +++ b/packages/util/src/lib/value/cron.ts @@ -0,0 +1,10 @@ +import { Minutes } from '../date/date'; + +/** + * A cron schedule expression string + */ +export type CronExpression = string; + +export function cronExpressionRepeatingEveryNMinutes(minutes: Minutes): CronExpression { + return `*/${minutes} * * * *`; // every nth minute +} diff --git a/packages/util/src/lib/value/index.ts b/packages/util/src/lib/value/index.ts index b5035c0bc..a135c4908 100644 --- a/packages/util/src/lib/value/index.ts +++ b/packages/util/src/lib/value/index.ts @@ -1,5 +1,6 @@ export * from './bound'; export * from './build'; +export * from './cron'; export * from './decision'; export * from './equal'; export * from './indexed'; diff --git a/setup/templates/apps/api/src/app/app.ts b/setup/templates/apps/api/src/app/app.ts index 032ae4b1a..bfd858ea7 100644 --- a/setup/templates/apps/api/src/app/app.ts +++ b/setup/templates/apps/api/src/app/app.ts @@ -1,6 +1,6 @@ import { APP_CODE_PREFIX_LOWERCreateModel, APP_CODE_PREFIX_LOWERUpdateModel, APP_CODE_PREFIX_LOWERDeleteModel } from './function/model/crud.functions'; import { exampleSetUsernameKey } from 'FIREBASE_COMPONENTS_NAME'; -import { NestAppPromiseGetter, nestServerInstance } from '@dereekb/firebase-server'; +import { initEnvironmentForScheduleConfiguredFunctions, nestAppHasDevelopmentSchedulerEnabled, NestAppPromiseGetter, nestServerInstance } from '@dereekb/firebase-server'; import { CREATE_MODEL_APP_FUNCTION_KEY, UPDATE_MODEL_APP_FUNCTION_KEY, DELETE_MODEL_APP_FUNCTION_KEY } from '@dereekb/firebase'; import { APP_CODE_PREFIXApiAppModule } from './app.module'; import { exampleSetUsername } from './function'; @@ -23,3 +23,15 @@ export function allAppFunctions(nest: NestAppPromiseGetter) { [exampleSetUsernameKey]: exampleSetUsername(nest) }; } + + export function allScheduledAppFunctions(nest: NestAppPromiseGetter) { + return initEnvironmentForScheduleConfiguredFunctions( + { + // Scheduled Functions + }, + { + // Only enable when the development scheduler is enabled + checkEnabled: nestAppHasDevelopmentSchedulerEnabled(nest) + } + ); +} diff --git a/setup/templates/apps/api/src/app/function/function.ts b/setup/templates/apps/api/src/app/function/function.ts index 8f5ec4a20..81436e3ba 100644 --- a/setup/templates/apps/api/src/app/function/function.ts +++ b/setup/templates/apps/api/src/app/function/function.ts @@ -1,6 +1,6 @@ import { INestApplicationContext } from '@nestjs/common'; import { APP_CODE_PREFIXFirebaseContextAppContext, APP_CODE_PREFIX_LOWERFirebaseModelServices, APP_CODE_PREFIXFirebaseModelTypes, APP_CODE_PREFIXFirestoreCollections } from "FIREBASE_COMPONENTS_NAME"; -import { onCallWithNestApplicationFactory, onCallWithNestContextFactory, taskQueueFunctionHandlerWithNestContextFactory, cloudEventHandlerWithNestContextFactory, blockingFunctionHandlerWithNestContextFactory, onEventWithNestContextFactory, AbstractFirebaseNestContext, OnCallCreateModelFunction, OnCallCreateModelMap, OnCallUpdateModelFunction, OnCallUpdateModelMap, OnCallDeleteModelMap, OnCallDeleteModelFunction } from '@dereekb/firebase-server'; +import { onCallWithNestApplicationFactory, onCallWithNestContextFactory, taskQueueFunctionHandlerWithNestContextFactory, cloudEventHandlerWithNestContextFactory, blockingFunctionHandlerWithNestContextFactory, onEventWithNestContextFactory, AbstractFirebaseNestContext, OnCallCreateModelFunction, OnCallCreateModelMap, OnCallUpdateModelFunction, OnCallUpdateModelMap, OnCallDeleteModelMap, OnCallDeleteModelFunction, onScheduleWithNestApplicationFactory, onScheduleWithNestContextFactory, OnScheduleWithNestContext } from '@dereekb/firebase-server'; import { OnCallCreateModelResult } from '@dereekb/firebase'; import { APP_CODE_PREFIXFirebaseServerActionsContext, ExampleServerActions, APP_CODE_PREFIXApiAuthService } from '../common'; @@ -35,6 +35,8 @@ export class APP_CODE_PREFIXApiNestContext extends AbstractFirebaseNestContext new APP_CODE_PREFIXApiNestContext(nest); export const onCallWithAPP_CODE_PREFIXNest = onCallWithNestApplicationFactory(); export const onCallWithAPP_CODE_PREFIXNestContext = onCallWithNestContextFactory(onCallWithAPP_CODE_PREFIXNest, mapAPP_CODE_PREFIXApiNestContext); +export const onScheduleWithAPP_CODE_PREFIXNest = onScheduleWithNestApplicationFactory(); +export const onScheduleWithAPP_CODE_PREFIXNestContext = onScheduleWithNestContextFactory(onScheduleWithAPP_CODE_PREFIXNest, mapAPP_CODE_PREFIXApiNestContext); export const onEventWithAPP_CODE_PREFIXNestContext = onEventWithNestContextFactory(mapAPP_CODE_PREFIXApiNestContext); export const cloudEventWithAPP_CODE_PREFIXNestContext = cloudEventHandlerWithNestContextFactory(mapAPP_CODE_PREFIXApiNestContext); export const blockingEventWithAPP_CODE_PREFIXNestContext = blockingFunctionHandlerWithNestContextFactory(mapAPP_CODE_PREFIXApiNestContext); @@ -49,3 +51,5 @@ export type APP_CODE_PREFIXOnCallUpdateModelMap = OnCallUpdateModelMap = OnCallDeleteModelFunction; export type APP_CODE_PREFIXOnCallDeleteModelMap = OnCallDeleteModelMap; + +export type APP_CODE_PREFIXScheduleFunction = OnScheduleWithNestContext;