From abb37258d0f2180e97135453542025c199009142 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Tue, 12 Oct 2021 18:56:42 +0100 Subject: [PATCH 01/17] Content drivers --- packages/server/src/models/BaseModel.ts | 8 +- packages/server/src/models/ItemModel.ts | 86 ++++++++++++++----- packages/server/src/models/factory.ts | 51 +++++++---- .../src/models/itemModel/ContentDriverBase.ts | 9 ++ .../models/itemModel/ContentDriverFs.test.ts | 54 ++++++++++++ .../src/models/itemModel/ContentDriverFs.ts | 45 ++++++++++ .../models/itemModel/ContentDriverMemory.ts | 22 +++++ packages/server/src/utils/joplinUtils.ts | 4 +- packages/server/src/utils/setupAppContext.ts | 9 +- .../server/src/utils/testing/testUtils.ts | 13 ++- 10 files changed, 249 insertions(+), 52 deletions(-) create mode 100644 packages/server/src/models/itemModel/ContentDriverBase.ts create mode 100644 packages/server/src/models/itemModel/ContentDriverFs.test.ts create mode 100644 packages/server/src/models/itemModel/ContentDriverFs.ts create mode 100644 packages/server/src/models/itemModel/ContentDriverMemory.ts diff --git a/packages/server/src/models/BaseModel.ts b/packages/server/src/models/BaseModel.ts index 59812225424..a5a0ce4b926 100644 --- a/packages/server/src/models/BaseModel.ts +++ b/packages/server/src/models/BaseModel.ts @@ -3,7 +3,7 @@ import { DbConnection } from '../db'; import TransactionHandler from '../utils/TransactionHandler'; import uuidgen from '../utils/uuidgen'; import { ErrorUnprocessableEntity, ErrorBadRequest } from '../utils/errors'; -import { Models } from './factory'; +import { Models, NewModelFactoryHandler } from './factory'; import * as EventEmitter from 'events'; import { Config } from '../utils/types'; import personalizedUserContentBaseUrl from '@joplin/lib/services/joplinServer/personalizedUserContentBaseUrl'; @@ -54,12 +54,12 @@ export default abstract class BaseModel { private defaultFields_: string[] = []; private db_: DbConnection; private transactionHandler_: TransactionHandler; - private modelFactory_: Function; + private modelFactory_: NewModelFactoryHandler; private static eventEmitter_: EventEmitter = null; private config_: Config; private savePoints_: SavePoint[] = []; - public constructor(db: DbConnection, modelFactory: Function, config: Config) { + public constructor(db: DbConnection, modelFactory: NewModelFactoryHandler, config: Config) { this.db_ = db; this.modelFactory_ = modelFactory; this.config_ = config; @@ -71,7 +71,7 @@ export default abstract class BaseModel { // connection is passed to it. That connection can be the regular db // connection, or the active transaction. protected models(db: DbConnection = null): Models { - return this.modelFactory_(db || this.db, this.config_); + return this.modelFactory_(db || this.db); } protected get baseUrl(): string { diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index 0dfa4db38d2..b119bd86583 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -7,6 +7,10 @@ import { ApiError, ErrorForbidden, ErrorUnprocessableEntity } from '../utils/err import { Knex } from 'knex'; import { ChangePreviousItem } from './ChangeModel'; import { unique } from '../utils/array'; +import ContentDriverBase from './itemModel/ContentDriverBase'; +import { DbConnection } from '../db'; +import { Config } from '../utils/types'; +import { NewModelFactoryHandler } from './factory'; const mimeUtils = require('@joplin/lib/mime-utils.js').mime; @@ -38,9 +42,22 @@ export interface ItemSaveOption extends SaveOptions { shareId?: Uuid; } +export interface ItemLoadOptions extends LoadOptions { + withContent?: boolean; +} + export default class ItemModel extends BaseModel { private updatingTotalSizes_: boolean = false; + private contentDriver_: ContentDriverBase = null; + + public constructor(db: DbConnection, modelFactory: NewModelFactoryHandler, contentDriver: ContentDriverBase, config: Config) { + super(db, modelFactory, config); + + if (!contentDriver) throw new Error('contentDriver is required'); + + this.contentDriver_ = contentDriver; + } protected get tableName(): string { return 'items'; @@ -106,62 +123,83 @@ export default class ItemModel extends BaseModel { return path.replace(extractNameRegex, '$1'); } - public byShareIdQuery(shareId: Uuid, options: LoadOptions = {}): Knex.QueryBuilder { + public byShareIdQuery(shareId: Uuid, options: ItemLoadOptions = {}): Knex.QueryBuilder { return this .db('items') .select(this.selectFields(options, null, 'items')) .where('jop_share_id', '=', shareId); } - public async byShareId(shareId: Uuid, options: LoadOptions = {}): Promise { + public async byShareId(shareId: Uuid, options: ItemLoadOptions = {}): Promise { const query = this.byShareIdQuery(shareId, options); return await query; } - public async loadByJopIds(userId: Uuid | Uuid[], jopIds: string[], options: LoadOptions = {}): Promise { + public async loadByJopIds(userId: Uuid | Uuid[], jopIds: string[], options: ItemLoadOptions = {}): Promise { if (!jopIds.length) return []; const userIds = Array.isArray(userId) ? userId : [userId]; if (!userIds.length) return []; - return this + const rows: Item[] = await this .db('user_items') .leftJoin('items', 'items.id', 'user_items.item_id') .distinct(this.selectFields(options, null, 'items')) .whereIn('user_items.user_id', userIds) .whereIn('jop_id', jopIds); + + if (options.withContent) { + for (const row of rows) { + row.content = await this.contentDriver_.read(row.id); + } + } + + return rows; } - public async loadByJopId(userId: Uuid, jopId: string, options: LoadOptions = {}): Promise { + public async loadByJopId(userId: Uuid, jopId: string, options: ItemLoadOptions = {}): Promise { const items = await this.loadByJopIds(userId, [jopId], options); return items.length ? items[0] : null; } - public async loadByNames(userId: Uuid | Uuid[], names: string[], options: LoadOptions = {}): Promise { + public async loadByNames(userId: Uuid | Uuid[], names: string[], options: ItemLoadOptions = {}): Promise { if (!names.length) return []; const userIds = Array.isArray(userId) ? userId : [userId]; - return this + const rows: Item[] = await this .db('user_items') .leftJoin('items', 'items.id', 'user_items.item_id') .distinct(this.selectFields(options, null, 'items')) .whereIn('user_items.user_id', userIds) .whereIn('name', names); + + if (options.withContent) { + for (const row of rows) { + row.content = await this.contentDriver_.read(row.id); + } + } + + return rows; } - public async loadByName(userId: Uuid, name: string, options: LoadOptions = {}): Promise { + public async loadByName(userId: Uuid, name: string, options: ItemLoadOptions = {}): Promise { const items = await this.loadByNames(userId, [name], options); return items.length ? items[0] : null; } - public async loadWithContent(id: Uuid, options: LoadOptions = {}): Promise { - return this - .db('user_items') - .leftJoin('items', 'items.id', 'user_items.item_id') - .select(this.selectFields(options, ['*'], 'items')) - .where('items.id', '=', id) - .first(); + public async loadWithContent(id: Uuid, options: ItemLoadOptions = {}): Promise { + const content = await this.contentDriver_.read(id); + + return { + ...await this + .db('user_items') + .leftJoin('items', 'items.id', 'user_items.item_id') + .select(this.selectFields(options, ['*'], 'items')) + .where('items.id', '=', id) + .first(), + content, + }; } public async loadAsSerializedJoplinItem(id: Uuid): Promise { @@ -349,11 +387,18 @@ export default class ItemModel extends BaseModel { continue; } - const itemToSave = o.item; + const itemToSave = { ...o.item }; try { + const content = itemToSave.content; + delete itemToSave.content; + + itemToSave.content_size = content ? content.byteLength : 0; + const savedItem = await this.saveForUser(user.id, itemToSave); + await this.contentDriver_.write(savedItem.id, content); + if (o.isNote) { await this.models().itemResource().deleteByItemId(savedItem.id); await this.models().itemResource().addResourceIds(savedItem.id, o.resourceIds); @@ -390,7 +435,7 @@ export default class ItemModel extends BaseModel { } - private childrenQuery(userId: Uuid, pathQuery: string = '', count: boolean = false, options: LoadOptions = {}): Knex.QueryBuilder { + private childrenQuery(userId: Uuid, pathQuery: string = '', count: boolean = false, options: ItemLoadOptions = {}): Knex.QueryBuilder { const query = this .db('user_items') .leftJoin('items', 'user_items.item_id', 'items.id') @@ -420,7 +465,7 @@ export default class ItemModel extends BaseModel { return `${this.baseUrl}/items/${itemId}/content`; } - public async children(userId: Uuid, pathQuery: string = '', pagination: Pagination = null, options: LoadOptions = {}): Promise { + public async children(userId: Uuid, pathQuery: string = '', pagination: Pagination = null, options: ItemLoadOptions = {}): Promise { pagination = pagination || defaultPagination(); const query = this.childrenQuery(userId, pathQuery, false, options); return paginateDbQuery(query, pagination, 'items'); @@ -532,6 +577,7 @@ export default class ItemModel extends BaseModel { await this.models().share().delete(shares.map(s => s.id)); await this.models().userItem().deleteByItemIds(ids); await this.models().itemResource().deleteByItemIds(ids); + await this.contentDriver_.delete(ids); await super.delete(ids, options); }, 'ItemModel::delete'); @@ -571,10 +617,6 @@ export default class ItemModel extends BaseModel { item = { ... item }; const isNew = await this.isNew(item, options); - if (item.content) { - item.content_size = item.content.byteLength; - } - let previousItem: ChangePreviousItem = null; if (isNew) { diff --git a/packages/server/src/models/factory.ts b/packages/server/src/models/factory.ts index d763c4e1e7d..de22587b227 100644 --- a/packages/server/src/models/factory.ts +++ b/packages/server/src/models/factory.ts @@ -72,83 +72,96 @@ import SubscriptionModel from './SubscriptionModel'; import UserFlagModel from './UserFlagModel'; import EventModel from './EventModel'; import { Config } from '../utils/types'; +import ContentDriverBase from './itemModel/ContentDriverBase'; + +export type NewModelFactoryHandler = (db: DbConnection)=> Models; export class Models { private db_: DbConnection; private config_: Config; + private contentDriver_: ContentDriverBase; - public constructor(db: DbConnection, config: Config) { + public constructor(db: DbConnection, contentDriver: ContentDriverBase, config: Config) { this.db_ = db; this.config_ = config; + this.contentDriver_ = contentDriver; + + if (!contentDriver) throw new Error('contentDriver is required'); + + this.newModelFactory = this.newModelFactory.bind(this); + } + + private newModelFactory(db: DbConnection) { + return new Models(db, this.contentDriver_, this.config_); } public item() { - return new ItemModel(this.db_, newModelFactory, this.config_); + return new ItemModel(this.db_, this.newModelFactory, this.contentDriver_, this.config_); } public user() { - return new UserModel(this.db_, newModelFactory, this.config_); + return new UserModel(this.db_, this.newModelFactory, this.config_); } public email() { - return new EmailModel(this.db_, newModelFactory, this.config_); + return new EmailModel(this.db_, this.newModelFactory, this.config_); } public userItem() { - return new UserItemModel(this.db_, newModelFactory, this.config_); + return new UserItemModel(this.db_, this.newModelFactory, this.config_); } public token() { - return new TokenModel(this.db_, newModelFactory, this.config_); + return new TokenModel(this.db_, this.newModelFactory, this.config_); } public itemResource() { - return new ItemResourceModel(this.db_, newModelFactory, this.config_); + return new ItemResourceModel(this.db_, this.newModelFactory, this.config_); } public apiClient() { - return new ApiClientModel(this.db_, newModelFactory, this.config_); + return new ApiClientModel(this.db_, this.newModelFactory, this.config_); } public session() { - return new SessionModel(this.db_, newModelFactory, this.config_); + return new SessionModel(this.db_, this.newModelFactory, this.config_); } public change() { - return new ChangeModel(this.db_, newModelFactory, this.config_); + return new ChangeModel(this.db_, this.newModelFactory, this.config_); } public notification() { - return new NotificationModel(this.db_, newModelFactory, this.config_); + return new NotificationModel(this.db_, this.newModelFactory, this.config_); } public share() { - return new ShareModel(this.db_, newModelFactory, this.config_); + return new ShareModel(this.db_, this.newModelFactory, this.config_); } public shareUser() { - return new ShareUserModel(this.db_, newModelFactory, this.config_); + return new ShareUserModel(this.db_, this.newModelFactory, this.config_); } public keyValue() { - return new KeyValueModel(this.db_, newModelFactory, this.config_); + return new KeyValueModel(this.db_, this.newModelFactory, this.config_); } public subscription() { - return new SubscriptionModel(this.db_, newModelFactory, this.config_); + return new SubscriptionModel(this.db_, this.newModelFactory, this.config_); } public userFlag() { - return new UserFlagModel(this.db_, newModelFactory, this.config_); + return new UserFlagModel(this.db_, this.newModelFactory, this.config_); } public event() { - return new EventModel(this.db_, newModelFactory, this.config_); + return new EventModel(this.db_, this.newModelFactory, this.config_); } } -export default function newModelFactory(db: DbConnection, config: Config): Models { - return new Models(db, config); +export default function newModelFactory(db: DbConnection, contentDriver: ContentDriverBase, config: Config): Models { + return new Models(db, contentDriver, config); } diff --git a/packages/server/src/models/itemModel/ContentDriverBase.ts b/packages/server/src/models/itemModel/ContentDriverBase.ts new file mode 100644 index 00000000000..fdd8d442f6c --- /dev/null +++ b/packages/server/src/models/itemModel/ContentDriverBase.ts @@ -0,0 +1,9 @@ +export default class ContentDriverBase { + + public async write(_itemId: string, _content: Buffer): Promise {} + + public async read(_itemId: string): Promise { return null; } + + public async delete(_itemId: string | string[]): Promise {} + +} diff --git a/packages/server/src/models/itemModel/ContentDriverFs.test.ts b/packages/server/src/models/itemModel/ContentDriverFs.test.ts new file mode 100644 index 00000000000..0b9e14824f9 --- /dev/null +++ b/packages/server/src/models/itemModel/ContentDriverFs.test.ts @@ -0,0 +1,54 @@ +import { pathExists, remove } from 'fs-extra'; +import { expectNotThrow, expectThrow, tempDirPath } from '../../utils/testing/testUtils'; +import ContentDriverFs from './ContentDriverFs'; + +let basePath_: string = ''; + +const newDriver = () => { + return new ContentDriverFs({ basePath: basePath_ }); +}; + +describe('ContentDriverFs', function() { + + beforeEach(async () => { + basePath_ = tempDirPath(); + }); + + afterEach(async () => { + await remove(basePath_); + basePath_ = ''; + }); + + test('should write to a file and read it back', async function() { + const driver = newDriver(); + await driver.write('testing', Buffer.from('testing')); + const content = await driver.read('testing'); + expect(content.toString()).toBe('testing'); + }); + + test('should automatically create the base path', async function() { + expect(await pathExists(basePath_)).toBe(false); + const driver = newDriver(); + await driver.write('testing', Buffer.from('testing')); + expect(await pathExists(basePath_)).toBe(true); + }); + + test('should delete a file', async function() { + const driver = newDriver(); + await driver.write('testing', Buffer.from('testing')); + expect((await driver.read('testing')).toString()).toBe('testing'); + await driver.delete('testing'); + await expectThrow(async () => driver.read('testing'), 'ENOENT'); + }); + + test('should throw if the file does not exist when reading it', async function() { + const driver = newDriver(); + await expectThrow(async () => driver.read('testread'), 'ENOENT'); + }); + + test('should not throw if deleting a file that does not exist', async function() { + const driver = newDriver(); + await expectNotThrow(async () => driver.delete('notthere')); + }); + +}); diff --git a/packages/server/src/models/itemModel/ContentDriverFs.ts b/packages/server/src/models/itemModel/ContentDriverFs.ts new file mode 100644 index 00000000000..670a102d65d --- /dev/null +++ b/packages/server/src/models/itemModel/ContentDriverFs.ts @@ -0,0 +1,45 @@ +import { mkdirp, readFile, remove, writeFile } from 'fs-extra'; +import ContentDriverBase from './ContentDriverBase'; + +interface Options { + basePath: string; +} + +export default class ContentDriverFs extends ContentDriverBase { + + private options_: Options; + private basePathCreated_: boolean = false; + + public constructor(options: Options) { + super(); + + this.options_ = options; + } + + private async checkBasePath() { + if (this.basePathCreated_) return; + await mkdirp(this.options_.basePath); + this.basePathCreated_ = true; + } + + private itemPath(itemId: string): string { + return `${this.options_.basePath}/${itemId}`; + } + + public async write(itemId: string, content: Buffer): Promise { + await this.checkBasePath(); + await writeFile(this.itemPath(itemId), content); + } + + public async read(itemId: string): Promise { + return readFile(this.itemPath(itemId)); + } + + public async delete(itemId: string | string[]): Promise { + const itemIds = Array.isArray(itemId) ? itemId : [itemId]; + for (const id of itemIds) { + await remove(this.itemPath(id)); + } + } + +} diff --git a/packages/server/src/models/itemModel/ContentDriverMemory.ts b/packages/server/src/models/itemModel/ContentDriverMemory.ts new file mode 100644 index 00000000000..d77ec19ff94 --- /dev/null +++ b/packages/server/src/models/itemModel/ContentDriverMemory.ts @@ -0,0 +1,22 @@ +import ContentDriverBase from './ContentDriverBase'; + +export default class ContentDriverMemory extends ContentDriverBase { + + private data_: Record = {}; + + public async write(itemId: string, content: Buffer): Promise { + this.data_[itemId] = content; + } + + public async read(itemId: string): Promise { + return this.data_[itemId]; + } + + public async delete(itemId: string | string[]): Promise { + const itemIds = Array.isArray(itemId) ? itemId : [itemId]; + for (const id of itemIds) { + delete this.data_[id]; + } + } + +} diff --git a/packages/server/src/utils/joplinUtils.ts b/packages/server/src/utils/joplinUtils.ts index d6f9519bbf9..b0fc7534249 100644 --- a/packages/server/src/utils/joplinUtils.ts +++ b/packages/server/src/utils/joplinUtils.ts @@ -141,7 +141,7 @@ async function noteLinkedItemInfos(userId: Uuid, itemModel: ItemModel, note: Not const output: LinkedItemInfos = {}; for (const jopId of jopIds) { - const item = await itemModel.loadByJopId(userId, jopId, { fields: ['*'] }); + const item = await itemModel.loadByJopId(userId, jopId, { fields: ['*'], withContent: true }); if (!item) continue; output[jopId] = { @@ -265,7 +265,7 @@ export async function renderItem(userId: Uuid, item: Item, share: Share, query: }; if (query.resource_id) { - const resourceItem = await models_.item().loadByName(userId, resourceBlobPath(query.resource_id), { fields: ['*'] }); + const resourceItem = await models_.item().loadByName(userId, resourceBlobPath(query.resource_id), { fields: ['*'], withContent: true }); fileToRender.item = resourceItem; fileToRender.content = resourceItem.content; fileToRender.jopItemId = query.resource_id; diff --git a/packages/server/src/utils/setupAppContext.ts b/packages/server/src/utils/setupAppContext.ts index d3dce8e6550..f5a35fcfab9 100644 --- a/packages/server/src/utils/setupAppContext.ts +++ b/packages/server/src/utils/setupAppContext.ts @@ -9,6 +9,11 @@ import { Services } from '../services/types'; import EmailService from '../services/EmailService'; import MustacheService from '../services/MustacheService'; import setupTaskService from './setupTaskService'; +import ContentDriverBase from '../models/itemModel/ContentDriverBase'; + +interface Options { + contentDriver: ContentDriverBase; +} async function setupServices(env: Env, models: Models, config: Config): Promise { const output: Services = { @@ -23,8 +28,8 @@ async function setupServices(env: Env, models: Models, config: Config): Promise< return output; } -export default async function(appContext: AppContext, env: Env, dbConnection: DbConnection, appLogger: ()=> LoggerWrapper): Promise { - const models = newModelFactory(dbConnection, config()); +export default async function(appContext: AppContext, env: Env, dbConnection: DbConnection, appLogger: ()=> LoggerWrapper, options: Options): Promise { + const models = newModelFactory(dbConnection, options.contentDriver, config()); // The joplinBase object is immutable because it is shared by all requests. // Then a "joplin" context property is created from it per request, which diff --git a/packages/server/src/utils/testing/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts index 203daeb2b36..b31558fd854 100644 --- a/packages/server/src/utils/testing/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -23,6 +23,7 @@ import MustacheService from '../../services/MustacheService'; import uuidgen from '../uuidgen'; import { createCsrfToken } from '../csrf'; import { cookieSet } from '../cookies'; +import ContentDriverMemory from '../../models/itemModel/ContentDriverMemory'; // Takes into account the fact that this file will be inside the /dist directory // when it runs. @@ -36,10 +37,14 @@ export function randomHash(): string { return crypto.createHash('md5').update(`${Date.now()}-${Math.random()}`).digest('hex'); } +export function tempDirPath(): string { + return `${packageRootDir}/temp/${randomHash()}`; +} + let tempDir_: string = null; export async function tempDir(): Promise { if (tempDir_) return tempDir_; - tempDir_ = `${packageRootDir}/temp/${randomHash()}`; + tempDir_ = tempDirPath(); await fs.mkdirp(tempDir_); return tempDir_; } @@ -189,7 +194,7 @@ export async function koaAppContext(options: AppContextTestOptions = null): Prom const appLogger = Logger.create('AppTest'); - const baseAppContext = await setupAppContext({} as any, Env.Dev, db_, () => appLogger); + const baseAppContext = await setupAppContext({} as any, Env.Dev, db_, () => appLogger, { contentDriver: new ContentDriverMemory() }); // Set type to "any" because the Koa context has many properties and we // don't need to mock all of them. @@ -241,8 +246,10 @@ export function db() { // return 'http://localhost:22300'; // } +const contentDriverMemory = new ContentDriverMemory(); + export function models() { - return modelFactory(db(), config()); + return modelFactory(db(), contentDriverMemory, config()); } export function parseHtml(html: string): Document { From b298861dc37b7e97606d82f9be73b7b601dcb4c7 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Tue, 19 Oct 2021 19:44:43 +0100 Subject: [PATCH 02/17] db driver --- packages/server/src/models/BaseModel.ts | 2 +- packages/server/src/models/ItemModel.ts | 10 +-- .../src/models/itemModel/ContentDriverBase.ts | 16 +++- .../itemModel/ContentDriverDatabase.test.ts | 88 +++++++++++++++++++ .../models/itemModel/ContentDriverDatabase.ts | 54 ++++++++++++ .../server/src/utils/testing/testUtils.ts | 14 ++- 6 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 packages/server/src/models/itemModel/ContentDriverDatabase.test.ts create mode 100644 packages/server/src/models/itemModel/ContentDriverDatabase.ts diff --git a/packages/server/src/models/BaseModel.ts b/packages/server/src/models/BaseModel.ts index a5a0ce4b926..9d6da77d742 100644 --- a/packages/server/src/models/BaseModel.ts +++ b/packages/server/src/models/BaseModel.ts @@ -90,7 +90,7 @@ export default abstract class BaseModel { return this.config_.appName; } - protected get db(): DbConnection { + public get db(): DbConnection { if (this.transactionHandler_.activeTransaction) return this.transactionHandler_.activeTransaction; return this.db_; } diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index b119bd86583..b5d46de3f9d 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -150,7 +150,7 @@ export default class ItemModel extends BaseModel { if (options.withContent) { for (const row of rows) { - row.content = await this.contentDriver_.read(row.id); + row.content = await this.contentDriver_.read(row.id, { models: this.models() }); } } @@ -176,7 +176,7 @@ export default class ItemModel extends BaseModel { if (options.withContent) { for (const row of rows) { - row.content = await this.contentDriver_.read(row.id); + row.content = await this.contentDriver_.read(row.id, { models: this.models() }); } } @@ -189,7 +189,7 @@ export default class ItemModel extends BaseModel { } public async loadWithContent(id: Uuid, options: ItemLoadOptions = {}): Promise { - const content = await this.contentDriver_.read(id); + const content = await this.contentDriver_.read(id, { models: this.models() }); return { ...await this @@ -397,7 +397,7 @@ export default class ItemModel extends BaseModel { const savedItem = await this.saveForUser(user.id, itemToSave); - await this.contentDriver_.write(savedItem.id, content); + await this.contentDriver_.write(savedItem.id, content, { models: this.models() }); if (o.isNote) { await this.models().itemResource().deleteByItemId(savedItem.id); @@ -577,7 +577,7 @@ export default class ItemModel extends BaseModel { await this.models().share().delete(shares.map(s => s.id)); await this.models().userItem().deleteByItemIds(ids); await this.models().itemResource().deleteByItemIds(ids); - await this.contentDriver_.delete(ids); + await this.contentDriver_.delete(ids, { models: this.models() }); await super.delete(ids, options); }, 'ItemModel::delete'); diff --git a/packages/server/src/models/itemModel/ContentDriverBase.ts b/packages/server/src/models/itemModel/ContentDriverBase.ts index fdd8d442f6c..c29a0e02b79 100644 --- a/packages/server/src/models/itemModel/ContentDriverBase.ts +++ b/packages/server/src/models/itemModel/ContentDriverBase.ts @@ -1,9 +1,19 @@ +import { Models } from '../factory'; + +// ItemModel passes the models object when calling any of the driver handler. +// This is so that if there's an active transaction, the driver can use that (as +// required for example by ContentDriverDatabase). + +export interface Context { + models: Models; +} + export default class ContentDriverBase { - public async write(_itemId: string, _content: Buffer): Promise {} + public async write(_itemId: string, _content: Buffer, _context: Context): Promise {} - public async read(_itemId: string): Promise { return null; } + public async read(_itemId: string, _context: Context): Promise { return null; } - public async delete(_itemId: string | string[]): Promise {} + public async delete(_itemId: string | string[], _context: Context): Promise {} } diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts b/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts new file mode 100644 index 00000000000..b1459e106fc --- /dev/null +++ b/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts @@ -0,0 +1,88 @@ +import { clientType } from '../../db'; +import { Item } from '../../services/database/types'; +import { afterAllTests, beforeAllDb, beforeEachDb, createUserAndSession, db, expectNotThrow, expectThrow, makeNoteSerializedBody, models } from '../../utils/testing/testUtils'; +import ContentDriverDatabase from './ContentDriverDatabase'; + +const newDriver = () => { + return new ContentDriverDatabase({ + dbClientType: clientType(db()), + }); +}; + +const testModels = (driver: ContentDriverDatabase = null) => { + return models({ contentDriver: driver ? driver : newDriver() }); +}; + +describe('ContentDriverDatabase', function() { + + beforeAll(async () => { + await beforeAllDb('ContentDriverDatabase'); + }); + + afterAll(async () => { + await afterAllTests(); + }); + + beforeEach(async () => { + await beforeEachDb(); + }); + + test('should write to content and read it back', async function() { + const driver = new ContentDriverDatabase({ + dbClientType: clientType(db()), + }); + + const { user } = await createUserAndSession(1); + const noteBody = makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing driver', + }); + + const output = await testModels(driver).item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(noteBody), + }]); + + const result = output['00000000000000000000000000000001.md']; + expect(result.error).toBeFalsy(); + + // Check that we hace some data in the "content" property to ensure the + // driver is indeed writing there. + const itemFromDb = await testModels().item().load(result.item.id, { fields: ['content', 'content_size'] }); + expect(itemFromDb.content.byteLength).toBe(itemFromDb.content_size); + + const item = testModels(driver).item().itemToJoplinItem(await testModels().item().loadByJopId(user.id, '00000000000000000000000000000001', { withContent: true })); + expect(item.id).toBe('00000000000000000000000000000001'); + expect(item.title).toBe('testing driver'); + }); + + test('should delete the content', async function() { + const { user } = await createUserAndSession(1); + const noteBody = makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing driver', + }); + + const output = await testModels().item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(noteBody), + }]); + + const item: Item = output['00000000000000000000000000000001.md'].item; + + expect((await testModels().item().all()).length).toBe(1); + await testModels().item().delete(item.id); + expect((await testModels().item().all()).length).toBe(0); + }); + + test('should fail if the item row does not exist', async function() { + const driver = newDriver(); + await expectThrow(async () => driver.read('oops', { models: models() })); + }); + + test('should do nothing if deleting non-existing row', async function() { + const driver = newDriver(); + await expectNotThrow(async () => driver.delete('oops', { models: models() })); + }); + +}); diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.ts b/packages/server/src/models/itemModel/ContentDriverDatabase.ts new file mode 100644 index 00000000000..358ede728e6 --- /dev/null +++ b/packages/server/src/models/itemModel/ContentDriverDatabase.ts @@ -0,0 +1,54 @@ +// This driver allows storing the content directly with the item row in the +// database (as a binary blob). For now the driver expects that the content is +// stored in the same table as the items, as it originally was. + +import { DatabaseConfigClient } from '../../utils/types'; +import ContentDriverBase, { Context } from './ContentDriverBase'; + +interface Options { + dbClientType: DatabaseConfigClient; +} + +export default class ContentDriverDatabase extends ContentDriverBase { + + private handleReturnedRows_: boolean = null; + + public constructor(options: Options) { + super(); + + this.handleReturnedRows_ = options.dbClientType === DatabaseConfigClient.PostgreSQL; + } + + public async write(itemId: string, content: Buffer, context: Context): Promise { + // console.info('AAAAAAAAAAAAAAAAA', context); + const returningOption = this.handleReturnedRows_ ? ['id'] : undefined; + + const updatedRows = await context.models.item().db('items').update({ content }, returningOption).where('id', '=', itemId); + if (!this.handleReturnedRows_) return; + + // Not possible because the ID is unique + if (updatedRows.length > 1) throw new Error('Update more than one row'); + + // Not possible either because the row is created before this handler is called, but still could happen + if (!updatedRows.length) throw new Error(`No such item: ${itemId}`); + + // That would be weird + if (updatedRows[0].id !== itemId) throw new Error(`Did not update the right row. Expected: ${itemId}. Got: ${updatedRows[0].id}`); + } + + public async read(itemId: string, context: Context): Promise { + const row = await context.models.item().db('items').select('content').where('id', '=', itemId).first(); + + // Calling code should only call this handler if the row exists, so if + // we find it doesn't, it's an error. + if (!row) throw new Error(`No such row: ${itemId}`); + + return row.content; + } + + public async delete(_itemId: string | string[], _context: Context): Promise { + // noop because the calling code deletes the whole row, including the + // content. + } + +} diff --git a/packages/server/src/utils/testing/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts index b31558fd854..d63d250d8a7 100644 --- a/packages/server/src/utils/testing/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -24,6 +24,7 @@ import uuidgen from '../uuidgen'; import { createCsrfToken } from '../csrf'; import { cookieSet } from '../cookies'; import ContentDriverMemory from '../../models/itemModel/ContentDriverMemory'; +import ContentDriverBase from '../../models/itemModel/ContentDriverBase'; // Takes into account the fact that this file will be inside the /dist directory // when it runs. @@ -246,10 +247,19 @@ export function db() { // return 'http://localhost:22300'; // } +interface ModelsOptions { + contentDriver?: ContentDriverBase; +} + const contentDriverMemory = new ContentDriverMemory(); -export function models() { - return modelFactory(db(), contentDriverMemory, config()); +export function models(options: ModelsOptions = null) { + options = { + contentDriver: contentDriverMemory, + ...options, + }; + + return modelFactory(db(), options.contentDriver, config()); } export function parseHtml(html: string): Document { From f7be45c236548872ca5a9f7be91d2bc83acb0574 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Wed, 20 Oct 2021 12:18:56 +0100 Subject: [PATCH 03/17] tests --- packages/server/src/models/ItemModel.ts | 18 ++- .../src/models/itemModel/ContentDriverBase.ts | 8 +- .../itemModel/ContentDriverDatabase.test.ts | 60 ++------- .../models/itemModel/ContentDriverDatabase.ts | 6 +- .../models/itemModel/ContentDriverFs.test.ts | 32 ++++- .../itemModel/ContentDriverMemory.test.ts | 40 ++++++ .../server/src/models/itemModel/testUtils.ts | 122 ++++++++++++++++++ 7 files changed, 234 insertions(+), 52 deletions(-) create mode 100644 packages/server/src/models/itemModel/ContentDriverMemory.test.ts create mode 100644 packages/server/src/models/itemModel/testUtils.ts diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index b5d46de3f9d..fe8b18620bf 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -395,9 +395,25 @@ export default class ItemModel extends BaseModel { itemToSave.content_size = content ? content.byteLength : 0; + // Here we save the item row and content, and we want to + // make sure that either both are saved or none of them. + // This is done by setting up a save point before saving the + // row, and rollbacking if the content cannot be saved. + // + // Normally, since we are in a transaction, throwing an + // error should work, but since we catch all errors within + // this block it doesn't work. + + const savePoint = await this.setSavePoint(); const savedItem = await this.saveForUser(user.id, itemToSave); - await this.contentDriver_.write(savedItem.id, content, { models: this.models() }); + try { + await this.contentDriver_.write(savedItem.id, content, { models: this.models() }); + await this.releaseSavePoint(savePoint); + } catch (error) { + await this.rollbackSavePoint(savePoint); + throw error; + } if (o.isNote) { await this.models().itemResource().deleteByItemId(savedItem.id); diff --git a/packages/server/src/models/itemModel/ContentDriverBase.ts b/packages/server/src/models/itemModel/ContentDriverBase.ts index c29a0e02b79..2967015dab9 100644 --- a/packages/server/src/models/itemModel/ContentDriverBase.ts +++ b/packages/server/src/models/itemModel/ContentDriverBase.ts @@ -10,10 +10,12 @@ export interface Context { export default class ContentDriverBase { - public async write(_itemId: string, _content: Buffer, _context: Context): Promise {} + public async write(_itemId: string, _content: Buffer, _context: Context): Promise { throw new Error('Not implemented'); } - public async read(_itemId: string, _context: Context): Promise { return null; } + public async read(_itemId: string, _context: Context): Promise { throw new Error('Not implemented'); } - public async delete(_itemId: string | string[], _context: Context): Promise {} + public async delete(_itemId: string | string[], _context: Context): Promise { throw new Error('Not implemented'); } + + // public async size(_itemId:string, _context: Context):Promise { throw new Error('Not implemented') }; } diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts b/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts index b1459e106fc..5e2e499ab4e 100644 --- a/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts +++ b/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts @@ -1,7 +1,7 @@ import { clientType } from '../../db'; -import { Item } from '../../services/database/types'; -import { afterAllTests, beforeAllDb, beforeEachDb, createUserAndSession, db, expectNotThrow, expectThrow, makeNoteSerializedBody, models } from '../../utils/testing/testUtils'; +import { afterAllTests, beforeAllDb, beforeEachDb, db, expectNotThrow, expectThrow, models } from '../../utils/testing/testUtils'; import ContentDriverDatabase from './ContentDriverDatabase'; +import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; const newDriver = () => { return new ContentDriverDatabase({ @@ -9,10 +9,6 @@ const newDriver = () => { }); }; -const testModels = (driver: ContentDriverDatabase = null) => { - return models({ contentDriver: driver ? driver : newDriver() }); -}; - describe('ContentDriverDatabase', function() { beforeAll(async () => { @@ -28,51 +24,23 @@ describe('ContentDriverDatabase', function() { }); test('should write to content and read it back', async function() { - const driver = new ContentDriverDatabase({ - dbClientType: clientType(db()), - }); - - const { user } = await createUserAndSession(1); - const noteBody = makeNoteSerializedBody({ - id: '00000000000000000000000000000001', - title: 'testing driver', - }); - - const output = await testModels(driver).item().saveFromRawContent(user, [{ - name: '00000000000000000000000000000001.md', - body: Buffer.from(noteBody), - }]); - - const result = output['00000000000000000000000000000001.md']; - expect(result.error).toBeFalsy(); - - // Check that we hace some data in the "content" property to ensure the - // driver is indeed writing there. - const itemFromDb = await testModels().item().load(result.item.id, { fields: ['content', 'content_size'] }); - expect(itemFromDb.content.byteLength).toBe(itemFromDb.content_size); - - const item = testModels(driver).item().itemToJoplinItem(await testModels().item().loadByJopId(user.id, '00000000000000000000000000000001', { withContent: true })); - expect(item.id).toBe('00000000000000000000000000000001'); - expect(item.title).toBe('testing driver'); + const driver = newDriver(); + await shouldWriteToContentAndReadItBack(driver); }); test('should delete the content', async function() { - const { user } = await createUserAndSession(1); - const noteBody = makeNoteSerializedBody({ - id: '00000000000000000000000000000001', - title: 'testing driver', - }); - - const output = await testModels().item().saveFromRawContent(user, [{ - name: '00000000000000000000000000000001.md', - body: Buffer.from(noteBody), - }]); + const driver = newDriver(); + await shouldDeleteContent(driver); + }); - const item: Item = output['00000000000000000000000000000001.md'].item; + test('should not create the item if the content cannot be saved', async function() { + const driver = newDriver(); + await shouldNotCreateItemIfContentNotSaved(driver); + }); - expect((await testModels().item().all()).length).toBe(1); - await testModels().item().delete(item.id); - expect((await testModels().item().all()).length).toBe(0); + test('should not update the item if the content cannot be saved', async function() { + const driver = newDriver(); + await shouldNotUpdateItemIfContentNotSaved(driver); }); test('should fail if the item row does not exist', async function() { diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.ts b/packages/server/src/models/itemModel/ContentDriverDatabase.ts index 358ede728e6..6213f2361db 100644 --- a/packages/server/src/models/itemModel/ContentDriverDatabase.ts +++ b/packages/server/src/models/itemModel/ContentDriverDatabase.ts @@ -20,7 +20,6 @@ export default class ContentDriverDatabase extends ContentDriverBase { } public async write(itemId: string, content: Buffer, context: Context): Promise { - // console.info('AAAAAAAAAAAAAAAAA', context); const returningOption = this.handleReturnedRows_ ? ['id'] : undefined; const updatedRows = await context.models.item().db('items').update({ content }, returningOption).where('id', '=', itemId); @@ -51,4 +50,9 @@ export default class ContentDriverDatabase extends ContentDriverBase { // content. } + // public async size(itemId:string, context: Context):Promise { + // const content = await this.read(itemId, context); + // return content.byteLength; + // }; + } diff --git a/packages/server/src/models/itemModel/ContentDriverFs.test.ts b/packages/server/src/models/itemModel/ContentDriverFs.test.ts index 0b9e14824f9..f5b59a77d0f 100644 --- a/packages/server/src/models/itemModel/ContentDriverFs.test.ts +++ b/packages/server/src/models/itemModel/ContentDriverFs.test.ts @@ -1,6 +1,7 @@ import { pathExists, remove } from 'fs-extra'; -import { expectNotThrow, expectThrow, tempDirPath } from '../../utils/testing/testUtils'; +import { afterAllTests, beforeAllDb, beforeEachDb, expectNotThrow, expectThrow, tempDirPath } from '../../utils/testing/testUtils'; import ContentDriverFs from './ContentDriverFs'; +import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; let basePath_: string = ''; @@ -10,8 +11,17 @@ const newDriver = () => { describe('ContentDriverFs', function() { + beforeAll(async () => { + await beforeAllDb('ContentDriverFs'); + }); + + afterAll(async () => { + await afterAllTests(); + }); + beforeEach(async () => { basePath_ = tempDirPath(); + await beforeEachDb(); }); afterEach(async () => { @@ -19,6 +29,26 @@ describe('ContentDriverFs', function() { basePath_ = ''; }); + test('should write to content and read it back', async function() { + const driver = newDriver(); + await shouldWriteToContentAndReadItBack(driver); + }); + + test('should delete the content', async function() { + const driver = newDriver(); + await shouldDeleteContent(driver); + }); + + test('should not create the item if the content cannot be saved', async function() { + const driver = newDriver(); + await shouldNotCreateItemIfContentNotSaved(driver); + }); + + test('should not update the item if the content cannot be saved', async function() { + const driver = newDriver(); + await shouldNotUpdateItemIfContentNotSaved(driver); + }); + test('should write to a file and read it back', async function() { const driver = newDriver(); await driver.write('testing', Buffer.from('testing')); diff --git a/packages/server/src/models/itemModel/ContentDriverMemory.test.ts b/packages/server/src/models/itemModel/ContentDriverMemory.test.ts new file mode 100644 index 00000000000..ad9e7a14471 --- /dev/null +++ b/packages/server/src/models/itemModel/ContentDriverMemory.test.ts @@ -0,0 +1,40 @@ +import { afterAllTests, beforeAllDb, beforeEachDb } from '../../utils/testing/testUtils'; +import ContentDriverMemory from './ContentDriverMemory'; +import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; + +describe('ContentDriverMemory', function() { + + beforeAll(async () => { + await beforeAllDb('ContentDriverMemory'); + }); + + afterAll(async () => { + await afterAllTests(); + }); + + beforeEach(async () => { + await beforeEachDb(); + }); + + test('should write to content and read it back', async function() { + const driver = new ContentDriverMemory(); + await shouldWriteToContentAndReadItBack(driver); + }); + + test('should delete the content', async function() { + const driver = new ContentDriverMemory(); + await shouldDeleteContent(driver); + }); + + test('should not create the item if the content cannot be saved', async function() { + const driver = new ContentDriverMemory(); + await shouldNotCreateItemIfContentNotSaved(driver); + }); + + test('should not update the item if the content cannot be saved', async function() { + const driver = new ContentDriverMemory(); + await shouldNotUpdateItemIfContentNotSaved(driver); + }); + +}); + diff --git a/packages/server/src/models/itemModel/testUtils.ts b/packages/server/src/models/itemModel/testUtils.ts new file mode 100644 index 00000000000..f928a6e68d4 --- /dev/null +++ b/packages/server/src/models/itemModel/testUtils.ts @@ -0,0 +1,122 @@ +import { Item } from '../../services/database/types'; +import { createUserAndSession, makeNoteSerializedBody, models } from '../../utils/testing/testUtils'; +import ContentDriverBase from './ContentDriverBase'; + +const testModels = (driver: ContentDriverBase) => { + return models({ contentDriver: driver }); +}; + +export async function shouldWriteToContentAndReadItBack(driver: ContentDriverBase) { + const { user } = await createUserAndSession(1); + const noteBody = makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing driver', + }); + + const output = await testModels(driver).item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(noteBody), + }]); + + const result = output['00000000000000000000000000000001.md']; + expect(result.error).toBeFalsy(); + + const item = await testModels(driver).item().loadWithContent(result.item.id); + expect(item.content.byteLength).toBe(item.content_size); + + const rawContent = await driver.read(item.id, { models: models() }); + expect(rawContent.byteLength).toBe(item.content_size); + + const jopItem = testModels(driver).item().itemToJoplinItem(item); + expect(jopItem.id).toBe('00000000000000000000000000000001'); + expect(jopItem.title).toBe('testing driver'); +} + +export async function shouldDeleteContent(driver: ContentDriverBase) { + const { user } = await createUserAndSession(1); + const noteBody = makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing driver', + }); + + const output = await testModels(driver).item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(noteBody), + }]); + + const item: Item = output['00000000000000000000000000000001.md'].item; + + expect((await testModels(driver).item().all()).length).toBe(1); + await testModels(driver).item().delete(item.id); + expect((await testModels(driver).item().all()).length).toBe(0); +} + +export async function shouldNotCreateItemIfContentNotSaved(driver: ContentDriverBase) { + const previousWrite = driver.write; + driver.write = () => { throw new Error('not working!'); }; + + try { + const { user } = await createUserAndSession(1); + const noteBody = makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing driver', + }); + + const output = await testModels(driver).item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(noteBody), + }]); + + expect(output['00000000000000000000000000000001.md'].error.message).toBe('not working!'); + expect((await testModels(driver).item().all()).length).toBe(0); + } finally { + driver.write = previousWrite; + } +} + +export async function shouldNotUpdateItemIfContentNotSaved(driver: ContentDriverBase) { + const { user } = await createUserAndSession(1); + const noteBody = makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing driver', + }); + + await testModels(driver).item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(noteBody), + }]); + + const noteBodyMod1 = makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'updated 1', + }); + + await testModels(driver).item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(noteBodyMod1), + }]); + + const itemMod1 = testModels(driver).item().itemToJoplinItem(await testModels(driver).item().loadByJopId(user.id, '00000000000000000000000000000001', { withContent: true })); + expect(itemMod1.title).toBe('updated 1'); + + const noteBodyMod2 = makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'updated 2', + }); + + const previousWrite = driver.write; + driver.write = () => { throw new Error('not working!'); }; + + try { + const output = await testModels(driver).item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(noteBodyMod2), + }]); + + expect(output['00000000000000000000000000000001.md'].error.message).toBe('not working!'); + const itemMod2 = testModels(driver).item().itemToJoplinItem(await testModels(driver).item().loadByJopId(user.id, '00000000000000000000000000000001', { withContent: true })); + expect(itemMod2.title).toBe('updated 1'); // Check it has not been updated + } finally { + driver.write = previousWrite; + } +} From 17b580b71bc4b706f79d44e4e2b07d14c3ffa388 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Thu, 21 Oct 2021 11:41:01 +0100 Subject: [PATCH 04/17] support fallback driver --- packages/server/src/models/ItemModel.ts | 23 +++++-- packages/server/src/models/factory.ts | 12 ++-- .../src/models/itemModel/ContentDriverBase.ts | 2 +- .../itemModel/ContentDriverDatabase.test.ts | 60 ++++++++++++++++++- .../models/itemModel/ContentDriverDatabase.ts | 8 +-- .../src/models/itemModel/ContentDriverFs.ts | 27 ++++++--- .../models/itemModel/ContentDriverMemory.ts | 4 ++ packages/server/src/utils/setupAppContext.ts | 3 +- .../server/src/utils/testing/testUtils.ts | 4 +- 9 files changed, 116 insertions(+), 27 deletions(-) diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index fe8b18620bf..aaf4825c800 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -7,7 +7,7 @@ import { ApiError, ErrorForbidden, ErrorUnprocessableEntity } from '../utils/err import { Knex } from 'knex'; import { ChangePreviousItem } from './ChangeModel'; import { unique } from '../utils/array'; -import ContentDriverBase from './itemModel/ContentDriverBase'; +import ContentDriverBase, { Context } from './itemModel/ContentDriverBase'; import { DbConnection } from '../db'; import { Config } from '../utils/types'; import { NewModelFactoryHandler } from './factory'; @@ -50,13 +50,15 @@ export default class ItemModel extends BaseModel { private updatingTotalSizes_: boolean = false; private contentDriver_: ContentDriverBase = null; + private fallbackContentDriver_: ContentDriverBase = null; - public constructor(db: DbConnection, modelFactory: NewModelFactoryHandler, contentDriver: ContentDriverBase, config: Config) { + public constructor(db: DbConnection, modelFactory: NewModelFactoryHandler, contentDriver: ContentDriverBase, fallbackContentDriver: ContentDriverBase, config: Config) { super(db, modelFactory, config); if (!contentDriver) throw new Error('contentDriver is required'); this.contentDriver_ = contentDriver; + this.fallbackContentDriver_ = fallbackContentDriver; } protected get tableName(): string { @@ -135,6 +137,15 @@ export default class ItemModel extends BaseModel { return await query; } + private async contentDriverRead(itemId: Uuid, context: Context) { + if (await this.contentDriver_.exists(itemId, context)) { + return this.contentDriver_.read(itemId, context); + } else { + if (!this.fallbackContentDriver_) throw new Error(`Content does not exist but fallback content driver is not defined: ${itemId}`); + return this.fallbackContentDriver_.read(itemId, context); + } + } + public async loadByJopIds(userId: Uuid | Uuid[], jopIds: string[], options: ItemLoadOptions = {}): Promise { if (!jopIds.length) return []; @@ -150,7 +161,7 @@ export default class ItemModel extends BaseModel { if (options.withContent) { for (const row of rows) { - row.content = await this.contentDriver_.read(row.id, { models: this.models() }); + row.content = await this.contentDriverRead(row.id, { models: this.models() }); } } @@ -176,7 +187,7 @@ export default class ItemModel extends BaseModel { if (options.withContent) { for (const row of rows) { - row.content = await this.contentDriver_.read(row.id, { models: this.models() }); + row.content = await this.contentDriverRead(row.id, { models: this.models() }); } } @@ -189,7 +200,7 @@ export default class ItemModel extends BaseModel { } public async loadWithContent(id: Uuid, options: ItemLoadOptions = {}): Promise { - const content = await this.contentDriver_.read(id, { models: this.models() }); + const content = await this.contentDriverRead(id, { models: this.models() }); return { ...await this @@ -409,6 +420,7 @@ export default class ItemModel extends BaseModel { try { await this.contentDriver_.write(savedItem.id, content, { models: this.models() }); + if (this.fallbackContentDriver_) await this.fallbackContentDriver_.write(savedItem.id, Buffer.from(''), { models: this.models() }); await this.releaseSavePoint(savePoint); } catch (error) { await this.rollbackSavePoint(savePoint); @@ -594,6 +606,7 @@ export default class ItemModel extends BaseModel { await this.models().userItem().deleteByItemIds(ids); await this.models().itemResource().deleteByItemIds(ids); await this.contentDriver_.delete(ids, { models: this.models() }); + if (this.fallbackContentDriver_) await this.fallbackContentDriver_.delete(ids, { models: this.models() }); await super.delete(ids, options); }, 'ItemModel::delete'); diff --git a/packages/server/src/models/factory.ts b/packages/server/src/models/factory.ts index de22587b227..4d7004b1686 100644 --- a/packages/server/src/models/factory.ts +++ b/packages/server/src/models/factory.ts @@ -81,11 +81,13 @@ export class Models { private db_: DbConnection; private config_: Config; private contentDriver_: ContentDriverBase; + private fallbackContentDriver_: ContentDriverBase; - public constructor(db: DbConnection, contentDriver: ContentDriverBase, config: Config) { + public constructor(db: DbConnection, contentDriver: ContentDriverBase, fallbackContentDriver: ContentDriverBase, config: Config) { this.db_ = db; this.config_ = config; this.contentDriver_ = contentDriver; + this.fallbackContentDriver_ = fallbackContentDriver; if (!contentDriver) throw new Error('contentDriver is required'); @@ -93,11 +95,11 @@ export class Models { } private newModelFactory(db: DbConnection) { - return new Models(db, this.contentDriver_, this.config_); + return new Models(db, this.contentDriver_, this.fallbackContentDriver_, this.config_); } public item() { - return new ItemModel(this.db_, this.newModelFactory, this.contentDriver_, this.config_); + return new ItemModel(this.db_, this.newModelFactory, this.contentDriver_, this.fallbackContentDriver_, this.config_); } public user() { @@ -162,6 +164,6 @@ export class Models { } -export default function newModelFactory(db: DbConnection, contentDriver: ContentDriverBase, config: Config): Models { - return new Models(db, contentDriver, config); +export default function newModelFactory(db: DbConnection, contentDriver: ContentDriverBase, fallbackContentDriver: ContentDriverBase, config: Config): Models { + return new Models(db, contentDriver, fallbackContentDriver, config); } diff --git a/packages/server/src/models/itemModel/ContentDriverBase.ts b/packages/server/src/models/itemModel/ContentDriverBase.ts index 2967015dab9..4535814bc2c 100644 --- a/packages/server/src/models/itemModel/ContentDriverBase.ts +++ b/packages/server/src/models/itemModel/ContentDriverBase.ts @@ -16,6 +16,6 @@ export default class ContentDriverBase { public async delete(_itemId: string | string[], _context: Context): Promise { throw new Error('Not implemented'); } - // public async size(_itemId:string, _context: Context):Promise { throw new Error('Not implemented') }; + public async exists(_itemId: string, _context: Context): Promise { throw new Error('Not implemented'); } } diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts b/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts index 5e2e499ab4e..d9961b3ce93 100644 --- a/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts +++ b/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts @@ -1,6 +1,8 @@ import { clientType } from '../../db'; -import { afterAllTests, beforeAllDb, beforeEachDb, db, expectNotThrow, expectThrow, models } from '../../utils/testing/testUtils'; +import { Item } from '../../services/database/types'; +import { afterAllTests, beforeAllDb, beforeEachDb, createUserAndSession, db, expectNotThrow, expectThrow, makeNoteSerializedBody, models } from '../../utils/testing/testUtils'; import ContentDriverDatabase from './ContentDriverDatabase'; +import ContentDriverMemory from './ContentDriverMemory'; import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; const newDriver = () => { @@ -53,4 +55,60 @@ describe('ContentDriverDatabase', function() { await expectNotThrow(async () => driver.delete('oops', { models: models() })); }); + test('should support fallback content drivers', async function() { + const dbDriver = newDriver(); + const memoryDriver = new ContentDriverMemory(); + + const testModels = models({ + contentDriver: dbDriver, + }); + + const { user } = await createUserAndSession(1); + + const output = await testModels.item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing', + })), + }]); + + const itemId = output['00000000000000000000000000000001.md'].item.id; + + let previousByteLength = 0; + + { + const row: Item = await db()('items').select(['id', 'jop_id', 'content']).first(); + expect(row.content.byteLength).toBeGreaterThan(10); + previousByteLength = row.content.byteLength; + } + + const testModelWithFallback = models({ + contentDriver: memoryDriver, + fallbackContentDriver: dbDriver, + }); + + // If the item content is not on the main content driver, it should get + // it from the fallback one. + const itemFromDb = await testModelWithFallback.item().loadWithContent(itemId); + expect(itemFromDb.content.byteLength).toBe(previousByteLength); + + // When writing content, it should use the main content driver, and set + // the content for the fallback one to "". + await testModelWithFallback.item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing1234', + })), + }]); + + { + const row: Item = await db()('items').select(['id', 'content']).first(); + expect(row.content.byteLength).toBe(0); + const memContent = await memoryDriver.read(itemId); + expect(memContent.byteLength).toBe(previousByteLength + 4); + } + }); + }); diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.ts b/packages/server/src/models/itemModel/ContentDriverDatabase.ts index 6213f2361db..4e921704cab 100644 --- a/packages/server/src/models/itemModel/ContentDriverDatabase.ts +++ b/packages/server/src/models/itemModel/ContentDriverDatabase.ts @@ -50,9 +50,9 @@ export default class ContentDriverDatabase extends ContentDriverBase { // content. } - // public async size(itemId:string, context: Context):Promise { - // const content = await this.read(itemId, context); - // return content.byteLength; - // }; + public async exists(itemId: string, context: Context): Promise { + const row = await context.models.item().db('items').select('content').where('id', '=', itemId).first(); + return !!row && !!row.content; + } } diff --git a/packages/server/src/models/itemModel/ContentDriverFs.ts b/packages/server/src/models/itemModel/ContentDriverFs.ts index 670a102d65d..24345e52bd8 100644 --- a/packages/server/src/models/itemModel/ContentDriverFs.ts +++ b/packages/server/src/models/itemModel/ContentDriverFs.ts @@ -1,4 +1,4 @@ -import { mkdirp, readFile, remove, writeFile } from 'fs-extra'; +import { mkdirp, pathExists, readFile, remove, writeFile } from 'fs-extra'; import ContentDriverBase from './ContentDriverBase'; interface Options { @@ -8,7 +8,7 @@ interface Options { export default class ContentDriverFs extends ContentDriverBase { private options_: Options; - private basePathCreated_: boolean = false; + private pathCreated_: Record = {}; public constructor(options: Options) { super(); @@ -16,19 +16,24 @@ export default class ContentDriverFs extends ContentDriverBase { this.options_ = options; } - private async checkBasePath() { - if (this.basePathCreated_) return; - await mkdirp(this.options_.basePath); - this.basePathCreated_ = true; + private async createParentDirectories(path: string) { + const p = path.split('/'); + p.pop(); + const basename = p.join('/'); + + if (this.pathCreated_[basename]) return; + await mkdirp(basename); + this.pathCreated_[basename] = true; } private itemPath(itemId: string): string { - return `${this.options_.basePath}/${itemId}`; + return `${this.options_.basePath}/${itemId[0]}/${itemId[1]}/${itemId.substr(2)}`; } public async write(itemId: string, content: Buffer): Promise { - await this.checkBasePath(); - await writeFile(this.itemPath(itemId), content); + const itemPath = this.itemPath(itemId); + await this.createParentDirectories(itemPath); + await writeFile(itemPath, content); } public async read(itemId: string): Promise { @@ -42,4 +47,8 @@ export default class ContentDriverFs extends ContentDriverBase { } } + public async exists(itemId: string): Promise { + return pathExists(this.itemPath(itemId)); + } + } diff --git a/packages/server/src/models/itemModel/ContentDriverMemory.ts b/packages/server/src/models/itemModel/ContentDriverMemory.ts index d77ec19ff94..fc40a5be731 100644 --- a/packages/server/src/models/itemModel/ContentDriverMemory.ts +++ b/packages/server/src/models/itemModel/ContentDriverMemory.ts @@ -19,4 +19,8 @@ export default class ContentDriverMemory extends ContentDriverBase { } } + public async exists(itemId: string): Promise { + return itemId in this.data_; + } + } diff --git a/packages/server/src/utils/setupAppContext.ts b/packages/server/src/utils/setupAppContext.ts index f5a35fcfab9..9585e4af641 100644 --- a/packages/server/src/utils/setupAppContext.ts +++ b/packages/server/src/utils/setupAppContext.ts @@ -13,6 +13,7 @@ import ContentDriverBase from '../models/itemModel/ContentDriverBase'; interface Options { contentDriver: ContentDriverBase; + fallbackContentDriver?: ContentDriverBase; } async function setupServices(env: Env, models: Models, config: Config): Promise { @@ -29,7 +30,7 @@ async function setupServices(env: Env, models: Models, config: Config): Promise< } export default async function(appContext: AppContext, env: Env, dbConnection: DbConnection, appLogger: ()=> LoggerWrapper, options: Options): Promise { - const models = newModelFactory(dbConnection, options.contentDriver, config()); + const models = newModelFactory(dbConnection, options.contentDriver, options.fallbackContentDriver, config()); // The joplinBase object is immutable because it is shared by all requests. // Then a "joplin" context property is created from it per request, which diff --git a/packages/server/src/utils/testing/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts index d63d250d8a7..5860fd4df0a 100644 --- a/packages/server/src/utils/testing/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -249,6 +249,7 @@ export function db() { interface ModelsOptions { contentDriver?: ContentDriverBase; + fallbackContentDriver?: ContentDriverBase; } const contentDriverMemory = new ContentDriverMemory(); @@ -256,10 +257,11 @@ const contentDriverMemory = new ContentDriverMemory(); export function models(options: ModelsOptions = null) { options = { contentDriver: contentDriverMemory, + fallbackContentDriver: null, ...options, }; - return modelFactory(db(), options.contentDriver, config()); + return modelFactory(db(), options.contentDriver, options.fallbackContentDriver, config()); } export function parseHtml(html: string): Document { From 731142218b9b7a3c4caa9e543cbcbb8f5db61556 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Fri, 22 Oct 2021 14:33:03 +0100 Subject: [PATCH 05/17] comment --- packages/server/src/models/ItemModel.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index aaf4825c800..3215c48c3b5 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -415,6 +415,17 @@ export default class ItemModel extends BaseModel { // error should work, but since we catch all errors within // this block it doesn't work. + // TODO: When an item is uploaded multiple times + // simultaneously there could be a race condition, where the + // content would not match the db row (for example, the + // content_size would differ). + // + // Possible solutions: + // + // - Row-level lock on items.id, and release once the + // content is saved. + // - Or external lock - eg. Redis. + const savePoint = await this.setSavePoint(); const savedItem = await this.saveForUser(user.id, itemToSave); From 72834fcfc42dbc0f86d437fa33188d359a0936aa Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Fri, 22 Oct 2021 14:46:54 +0100 Subject: [PATCH 06/17] clean up --- packages/server/jest.setup.js | 6 +++-- packages/server/src/app.ts | 8 +++++-- packages/server/src/models/ItemModel.ts | 10 ++++---- packages/server/src/models/factory.ts | 23 +++++++++++-------- packages/server/src/tools/debugTools.ts | 10 ++++---- packages/server/src/utils/setupAppContext.ts | 12 +++------- .../server/src/utils/testing/testUtils.ts | 16 +++---------- 7 files changed, 39 insertions(+), 46 deletions(-) diff --git a/packages/server/jest.setup.js b/packages/server/jest.setup.js index e88aa76273d..75286b568d8 100644 --- a/packages/server/jest.setup.js +++ b/packages/server/jest.setup.js @@ -4,7 +4,9 @@ const nodeSqlite = require('sqlite3'); shimInit({ nodeSqlite }); // We don't want the tests to fail due to timeout, especially on CI, and certain -// tests can take more time since we do integration testing too. -jest.setTimeout(30 * 1000); +// tests can take more time since we do integration testing too. The share tests +// in particular can take a while. + +jest.setTimeout(60 * 1000); process.env.JOPLIN_IS_TESTING = '1'; diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 4da337b2433..89280d758b5 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -7,7 +7,7 @@ import { argv } from 'yargs'; import Logger, { LoggerWrapper, TargetType } from '@joplin/lib/Logger'; import config, { initConfig, runningInDocker, EnvVariables } from './config'; import { createDb, dropDb } from './tools/dbTools'; -import { dropTables, connectDb, disconnectDb, migrateLatest, waitForConnection, sqliteDefaultDir, migrateList, migrateUp, migrateDown } from './db'; +import { dropTables, connectDb, disconnectDb, migrateLatest, waitForConnection, sqliteDefaultDir, migrateList, migrateUp, migrateDown, clientType } from './db'; import { AppContext, Env, KoaNext } from './utils/types'; import FsDriverNode from '@joplin/lib/fs-driver-node'; import routeHandler from './middleware/routeHandler'; @@ -19,6 +19,7 @@ import startServices from './utils/startServices'; import { credentialFile } from './utils/testing/testUtils'; import apiVersionHandler from './middleware/apiVersionHandler'; import clickJackingHandler from './middleware/clickJackingHandler'; +import ContentDriverDatabase from './models/itemModel/ContentDriverDatabase'; const nodeSqlite = require('sqlite3'); const cors = require('@koa/cors'); @@ -245,7 +246,10 @@ async function main() { appLogger().info('Connection check:', connectionCheckLogInfo); const ctx = app.context as AppContext; - await setupAppContext(ctx, env, connectionCheck.connection, appLogger); + await setupAppContext(ctx, env, connectionCheck.connection, appLogger, { + contentDriver: new ContentDriverDatabase({ dbClientType: clientType(connectionCheck.connection) }), + }); + await initializeJoplinUtils(config(), ctx.joplinBase.models, ctx.joplinBase.services.mustache); appLogger().info('Migrating database...'); diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index 3215c48c3b5..ec1f152368a 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -10,7 +10,7 @@ import { unique } from '../utils/array'; import ContentDriverBase, { Context } from './itemModel/ContentDriverBase'; import { DbConnection } from '../db'; import { Config } from '../utils/types'; -import { NewModelFactoryHandler } from './factory'; +import { NewModelFactoryHandler, Options } from './factory'; const mimeUtils = require('@joplin/lib/mime-utils.js').mime; @@ -52,13 +52,11 @@ export default class ItemModel extends BaseModel { private contentDriver_: ContentDriverBase = null; private fallbackContentDriver_: ContentDriverBase = null; - public constructor(db: DbConnection, modelFactory: NewModelFactoryHandler, contentDriver: ContentDriverBase, fallbackContentDriver: ContentDriverBase, config: Config) { + public constructor(db: DbConnection, modelFactory: NewModelFactoryHandler, config: Config, options: Options) { super(db, modelFactory, config); - if (!contentDriver) throw new Error('contentDriver is required'); - - this.contentDriver_ = contentDriver; - this.fallbackContentDriver_ = fallbackContentDriver; + this.contentDriver_ = options.contentDriver; + this.fallbackContentDriver_ = options.fallbackContentDriver; } protected get tableName(): string { diff --git a/packages/server/src/models/factory.ts b/packages/server/src/models/factory.ts index 4d7004b1686..8e35d54e901 100644 --- a/packages/server/src/models/factory.ts +++ b/packages/server/src/models/factory.ts @@ -74,32 +74,35 @@ import EventModel from './EventModel'; import { Config } from '../utils/types'; import ContentDriverBase from './itemModel/ContentDriverBase'; +export interface Options { + contentDriver: ContentDriverBase; + fallbackContentDriver?: ContentDriverBase; +} + export type NewModelFactoryHandler = (db: DbConnection)=> Models; export class Models { private db_: DbConnection; private config_: Config; - private contentDriver_: ContentDriverBase; - private fallbackContentDriver_: ContentDriverBase; + private options_: Options; - public constructor(db: DbConnection, contentDriver: ContentDriverBase, fallbackContentDriver: ContentDriverBase, config: Config) { + public constructor(db: DbConnection, config: Config, options: Options) { this.db_ = db; this.config_ = config; - this.contentDriver_ = contentDriver; - this.fallbackContentDriver_ = fallbackContentDriver; + this.options_ = options; - if (!contentDriver) throw new Error('contentDriver is required'); + if (!options.contentDriver) throw new Error('contentDriver is required'); this.newModelFactory = this.newModelFactory.bind(this); } private newModelFactory(db: DbConnection) { - return new Models(db, this.contentDriver_, this.fallbackContentDriver_, this.config_); + return new Models(db, this.config_, this.options_); } public item() { - return new ItemModel(this.db_, this.newModelFactory, this.contentDriver_, this.fallbackContentDriver_, this.config_); + return new ItemModel(this.db_, this.newModelFactory, this.config_, this.options_); } public user() { @@ -164,6 +167,6 @@ export class Models { } -export default function newModelFactory(db: DbConnection, contentDriver: ContentDriverBase, fallbackContentDriver: ContentDriverBase, config: Config): Models { - return new Models(db, contentDriver, fallbackContentDriver, config); +export default function newModelFactory(db: DbConnection, config: Config, options: Options): Models { + return new Models(db, config, options); } diff --git a/packages/server/src/tools/debugTools.ts b/packages/server/src/tools/debugTools.ts index ab35dabe066..7e6067a50ab 100644 --- a/packages/server/src/tools/debugTools.ts +++ b/packages/server/src/tools/debugTools.ts @@ -1,6 +1,7 @@ import time from '@joplin/lib/time'; -import { DbConnection, dropTables, migrateLatest } from '../db'; +import { clientType, DbConnection, dropTables, migrateLatest } from '../db'; import newModelFactory from '../models/factory'; +import ContentDriverDatabase from '../models/itemModel/ContentDriverDatabase'; import { AccountType } from '../models/UserModel'; import { User, UserFlagType } from '../services/database/types'; import { Config } from '../utils/types'; @@ -34,9 +35,11 @@ export async function createTestUsers(db: DbConnection, config: Config, options: const password = 'hunter1hunter2hunter3'; - if (options.count) { - const models = newModelFactory(db, config); + const models = newModelFactory(db, config, { + contentDriver: new ContentDriverDatabase({ dbClientType: clientType(db) }), + }); + if (options.count) { const users: User[] = []; for (let i = 0; i < options.count; i++) { @@ -52,7 +55,6 @@ export async function createTestUsers(db: DbConnection, config: Config, options: } else { await dropTables(db); await migrateLatest(db); - const models = newModelFactory(db, config); for (let userNum = 1; userNum <= 2; userNum++) { await models.user().save({ diff --git a/packages/server/src/utils/setupAppContext.ts b/packages/server/src/utils/setupAppContext.ts index 9585e4af641..f53c9a4a117 100644 --- a/packages/server/src/utils/setupAppContext.ts +++ b/packages/server/src/utils/setupAppContext.ts @@ -1,7 +1,7 @@ import { LoggerWrapper } from '@joplin/lib/Logger'; import config from '../config'; import { DbConnection } from '../db'; -import newModelFactory, { Models } from '../models/factory'; +import newModelFactory, { Models, Options as ModelFactoryOptions } from '../models/factory'; import { AppContext, Config, Env } from './types'; import routes from '../routes/routes'; import ShareService from '../services/ShareService'; @@ -9,12 +9,6 @@ import { Services } from '../services/types'; import EmailService from '../services/EmailService'; import MustacheService from '../services/MustacheService'; import setupTaskService from './setupTaskService'; -import ContentDriverBase from '../models/itemModel/ContentDriverBase'; - -interface Options { - contentDriver: ContentDriverBase; - fallbackContentDriver?: ContentDriverBase; -} async function setupServices(env: Env, models: Models, config: Config): Promise { const output: Services = { @@ -29,8 +23,8 @@ async function setupServices(env: Env, models: Models, config: Config): Promise< return output; } -export default async function(appContext: AppContext, env: Env, dbConnection: DbConnection, appLogger: ()=> LoggerWrapper, options: Options): Promise { - const models = newModelFactory(dbConnection, options.contentDriver, options.fallbackContentDriver, config()); +export default async function(appContext: AppContext, env: Env, dbConnection: DbConnection, appLogger: ()=> LoggerWrapper, options: ModelFactoryOptions): Promise { + const models = newModelFactory(dbConnection, config(), options); // The joplinBase object is immutable because it is shared by all requests. // Then a "joplin" context property is created from it per request, which diff --git a/packages/server/src/utils/testing/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts index 5860fd4df0a..85b83bee614 100644 --- a/packages/server/src/utils/testing/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -1,7 +1,7 @@ import { DbConnection, connectDb, disconnectDb, truncateTables } from '../../db'; import { User, Session, Item, Uuid } from '../../services/database/types'; import { createDb, CreateDbOptions } from '../../tools/dbTools'; -import modelFactory from '../../models/factory'; +import modelFactory, { Options as ModelFactoryOptions } from '../../models/factory'; import { AppContext, Env } from '../types'; import config, { initConfig } from '../../config'; import Logger from '@joplin/lib/Logger'; @@ -24,7 +24,6 @@ import uuidgen from '../uuidgen'; import { createCsrfToken } from '../csrf'; import { cookieSet } from '../cookies'; import ContentDriverMemory from '../../models/itemModel/ContentDriverMemory'; -import ContentDriverBase from '../../models/itemModel/ContentDriverBase'; // Takes into account the fact that this file will be inside the /dist directory // when it runs. @@ -243,25 +242,16 @@ export function db() { return db_; } -// function baseUrl() { -// return 'http://localhost:22300'; -// } - -interface ModelsOptions { - contentDriver?: ContentDriverBase; - fallbackContentDriver?: ContentDriverBase; -} - const contentDriverMemory = new ContentDriverMemory(); -export function models(options: ModelsOptions = null) { +export function models(options: ModelFactoryOptions = null) { options = { contentDriver: contentDriverMemory, fallbackContentDriver: null, ...options, }; - return modelFactory(db(), options.contentDriver, options.fallbackContentDriver, config()); + return modelFactory(db(), config(), options); } export function parseHtml(html: string): Document { From 560523bdc243edec201cf71e574b9594063228bc Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Tue, 26 Oct 2021 19:09:33 +0100 Subject: [PATCH 07/17] tests --- packages/app-cli/app/command-testing.ts | 28 +++++++++++++++++++ packages/server/src/app.ts | 17 ++++++----- .../src/models/itemModel/ContentDriverFs.ts | 2 +- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/packages/app-cli/app/command-testing.ts b/packages/app-cli/app/command-testing.ts index aa438ba7700..0499ddf9363 100644 --- a/packages/app-cli/app/command-testing.ts +++ b/packages/app-cli/app/command-testing.ts @@ -3,6 +3,8 @@ import { reg } from '@joplin/lib/registry'; import Note from '@joplin/lib/models/Note'; import uuid from '@joplin/lib/uuid'; import populateDatabase from '@joplin/lib/services/debug/populateDatabase'; +import JoplinServerApi from '../../lib/JoplinServerApi'; +import { readCredentialFile } from '../../lib/utils/credentialFiles'; function randomElement(array: any[]): any { if (!array.length) return null; @@ -87,6 +89,32 @@ class Command extends BaseCommand { } } + if (command === 'joplinServerParallelItemUpdate') { + const randomContent = () => { + const charCount = Math.random() * 1000; + return 'a'.repeat(charCount); + }; + + const joplinServerAuth = JSON.parse(await readCredentialFile('joplin-server-test.json')); + + const api = new JoplinServerApi({ + baseUrl: () => joplinServerAuth.baseUrl, + userContentBaseUrl: () => joplinServerAuth.userContentBaseUrl, + username: () => joplinServerAuth.email, + password: () => joplinServerAuth.password, + }); + + const promises = []; + for (let i = 0; i < 100; i++) { + promises.push(void api.exec('PUT', 'api/items/root:/testing:/content', {}, randomContent(), { + 'Content-Type': 'application/octet-stream', + })); + } + await Promise.all(promises); + + console.info(await api.exec('GET', 'api/items/root:/testing:')); + } + await Promise.all(promises); } diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index fa72ee302d8..0a05c2395fb 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -7,7 +7,7 @@ import { argv as yargsArgv } from 'yargs'; import Logger, { LoggerWrapper, TargetType } from '@joplin/lib/Logger'; import config, { initConfig, runningInDocker, EnvVariables } from './config'; import { createDb, dropDb } from './tools/dbTools'; -import { dropTables, connectDb, disconnectDb, migrateLatest, waitForConnection, sqliteDefaultDir, migrateList, migrateUp, migrateDown, clientType } from './db'; +import { dropTables, connectDb, disconnectDb, migrateLatest, waitForConnection, sqliteDefaultDir, migrateList, migrateUp, migrateDown, clientType, DbConnection } from './db'; import { AppContext, Env, KoaNext } from './utils/types'; import FsDriverNode from '@joplin/lib/fs-driver-node'; import routeHandler from './middleware/routeHandler'; @@ -221,6 +221,13 @@ async function main() { fs.writeFileSync(pidFile, `${process.pid}`); } + const newModelFactoryOptions = (db: DbConnection) => { + return { + contentDriver: new ContentDriverDatabase({ dbClientType: clientType(db) }), + // contentDriver: new ContentDriverFs({ basePath: '/Users/laurent/Temp/TestContentDriverFs' }), + }; + }; + let runCommandAndExitApp = true; if (argv.migrateLatest) { @@ -253,9 +260,7 @@ async function main() { // // Also should use yargs command system. const connectionCheck = await waitForConnection(config().database); - const models = newModelFactory(connectionCheck.connection, config(), { - contentDriver: new ContentDriverDatabase({ dbClientType: clientType(connectionCheck.connection) }), - }); + const models = newModelFactory(connectionCheck.connection, config(), newModelFactoryOptions(connectionCheck.connection)); if (argv.deleteOldChanges90) { await deleteOldChanges90({ models }); @@ -282,9 +287,7 @@ async function main() { appLogger().info('Connection check:', connectionCheckLogInfo); const ctx = app.context as AppContext; - await setupAppContext(ctx, env, connectionCheck.connection, appLogger, { - contentDriver: new ContentDriverDatabase({ dbClientType: clientType(connectionCheck.connection) }), - }); + await setupAppContext(ctx, env, connectionCheck.connection, appLogger, newModelFactoryOptions(connectionCheck.connection)); await initializeJoplinUtils(config(), ctx.joplinBase.models, ctx.joplinBase.services.mustache); diff --git a/packages/server/src/models/itemModel/ContentDriverFs.ts b/packages/server/src/models/itemModel/ContentDriverFs.ts index 24345e52bd8..8121d6399d7 100644 --- a/packages/server/src/models/itemModel/ContentDriverFs.ts +++ b/packages/server/src/models/itemModel/ContentDriverFs.ts @@ -27,7 +27,7 @@ export default class ContentDriverFs extends ContentDriverBase { } private itemPath(itemId: string): string { - return `${this.options_.basePath}/${itemId[0]}/${itemId[1]}/${itemId.substr(2)}`; + return `${this.options_.basePath}/${itemId.substr(0, 2).toLowerCase()}/${itemId.substr(2, 2).toLowerCase()}/${itemId}`; } public async write(itemId: string, content: Buffer): Promise { From 9b0a659416e130941ad23b36a7e4ea1120d6870e Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Thu, 4 Nov 2021 18:00:37 +0000 Subject: [PATCH 08/17] connection string --- packages/server/src/app.ts | 16 +- packages/server/src/config.ts | 3 + packages/server/src/env.ts | 141 ++++++++++-------- packages/server/src/models/ItemModel.ts | 4 +- packages/server/src/models/UserModel.test.ts | 12 +- packages/server/src/models/UserModel.ts | 2 +- .../itemModel/contentDriverFromConfig.ts | 24 +++ .../parseContentDriverConnectionString.ts | 35 +++++ .../server/src/services/database/types.ts | 2 +- packages/server/src/utils/types.ts | 13 ++ 10 files changed, 175 insertions(+), 77 deletions(-) create mode 100644 packages/server/src/models/itemModel/contentDriverFromConfig.ts create mode 100644 packages/server/src/models/itemModel/parseContentDriverConnectionString.ts diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 88d59fea0ed..d9905f6432d 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -5,7 +5,7 @@ import * as Koa from 'koa'; import * as fs from 'fs-extra'; import Logger, { LoggerWrapper, TargetType } from '@joplin/lib/Logger'; import config, { initConfig, runningInDocker } from './config'; -import { migrateLatest, waitForConnection, sqliteDefaultDir, latestMigration, DbConnection, clientType } from './db'; +import { migrateLatest, waitForConnection, sqliteDefaultDir, latestMigration, DbConnection } from './db'; import { AppContext, Env, KoaNext } from './utils/types'; import FsDriverNode from '@joplin/lib/fs-driver-node'; import routeHandler from './middleware/routeHandler'; @@ -17,11 +17,11 @@ import startServices from './utils/startServices'; import { credentialFile } from './utils/testing/testUtils'; import apiVersionHandler from './middleware/apiVersionHandler'; import clickJackingHandler from './middleware/clickJackingHandler'; -import ContentDriverDatabase from './models/itemModel/ContentDriverDatabase'; -import newModelFactory from './models/factory'; +import newModelFactory, { Options } from './models/factory'; import setupCommands from './utils/setupCommands'; import { RouteResponseFormat, routeResponseFormat } from './utils/routeUtils'; import { parseEnv } from './env'; +import contentDriverFromConfig from './models/itemModel/contentDriverFromConfig'; interface Argv { env?: Env; @@ -62,6 +62,8 @@ function appLogger(): LoggerWrapper { } function markPasswords(o: Record): Record { + if (!o) return o; + const output: Record = {}; for (const k of Object.keys(o)) { @@ -220,10 +222,10 @@ async function main() { fs.writeFileSync(pidFile, `${process.pid}`); } - const newModelFactoryOptions = (db: DbConnection) => { + const newModelFactoryOptions = (db: DbConnection): Options => { return { - contentDriver: new ContentDriverDatabase({ dbClientType: clientType(db) }), - // contentDriver: new ContentDriverFs({ basePath: '/Users/laurent/Temp/TestContentDriverFs' }), + contentDriver: contentDriverFromConfig(config().contentDriver, db), + fallbackContentDriver: contentDriverFromConfig(config().fallbackContentDriver, db), }; }; @@ -261,6 +263,8 @@ async function main() { appLogger().info('Log dir:', config().logDir); appLogger().info('DB Config:', markPasswords(config().database)); appLogger().info('Mailer Config:', markPasswords(config().mailer)); + appLogger().info('Content driver:', markPasswords(config().contentDriver)); + appLogger().info('Content driver (fallback):', markPasswords(config().fallbackContentDriver)); appLogger().info('Trying to connect to database...'); const connectionCheck = await waitForConnection(config().database); diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts index 90a48765aa0..c718bf09c83 100644 --- a/packages/server/src/config.ts +++ b/packages/server/src/config.ts @@ -3,6 +3,7 @@ import { Config, DatabaseConfig, DatabaseConfigClient, Env, MailerConfig, RouteT import * as pathUtils from 'path'; import { loadStripeConfig, StripePublicConfig } from '@joplin/lib/utils/joplinCloud'; import { EnvVariables } from './env'; +import parseContentDriverConnectionString from './models/itemModel/parseContentDriverConnectionString'; interface PackageJson { version: string; @@ -130,6 +131,8 @@ export async function initConfig(envType: Env, env: EnvVariables, overrides: any supportName: env.SUPPORT_NAME || appName, businessEmail: env.BUSINESS_EMAIL || supportEmail, cookieSecure: env.COOKIES_SECURE, + contentDriver: parseContentDriverConnectionString(env.CONTENT_DRIVER), + fallbackContentDriver: parseContentDriverConnectionString(env.CONTENT_DRIVER_FALLBACK), ...overrides, }; } diff --git a/packages/server/src/env.ts b/packages/server/src/env.ts index f8386f6d210..35dde043b20 100644 --- a/packages/server/src/env.ts +++ b/packages/server/src/env.ts @@ -1,8 +1,83 @@ -export interface EnvVariables { +// The possible env variables and their defaults are listed below. +// +// The env variables can be of type string, integer or boolean. When the type is +// boolean, set the variable to "0" or "1" in your env file. + +const defaultEnvValues: EnvVariables = { // ================================================== // General config // ================================================== + APP_NAME: 'Joplin Server', + APP_PORT: 22300, + SIGNUP_ENABLED: false, + TERMS_ENABLED: false, + ACCOUNT_TYPES_ENABLED: false, + ERROR_STACK_TRACES: false, + COOKIES_SECURE: false, + RUNNING_IN_DOCKER: false, + + // ================================================== + // URL config + // ================================================== + + APP_BASE_URL: '', + USER_CONTENT_BASE_URL: '', + API_BASE_URL: '', + JOPLINAPP_BASE_URL: 'https://joplinapp.org', + + // ================================================== + // Database config + // ================================================== + + DB_CLIENT: 'sqlite3', + DB_SLOW_QUERY_LOG_ENABLED: false, + DB_SLOW_QUERY_LOG_MIN_DURATION: 1000, + DB_AUTO_MIGRATION: true, + + POSTGRES_PASSWORD: 'joplin', + POSTGRES_DATABASE: 'joplin', + POSTGRES_USER: 'joplin', + POSTGRES_HOST: '', + POSTGRES_PORT: 5432, + + // This must be the full path to the database file + SQLITE_DATABASE: '', + + // ================================================== + // Content driver config + // ================================================== + + CONTENT_DRIVER: 'Type=Database', + CONTENT_DRIVER_FALLBACK: '', + CONTENT_DRIVER_FALLBACK_WRITE: false, + + // ================================================== + // Mailer config + // ================================================== + + MAILER_ENABLED: false, + MAILER_HOST: '', + MAILER_PORT: 587, + MAILER_SECURE: true, + MAILER_AUTH_USER: '', + MAILER_AUTH_PASSWORD: '', + MAILER_NOREPLY_NAME: '', + MAILER_NOREPLY_EMAIL: '', + + SUPPORT_EMAIL: 'SUPPORT_EMAIL', // Defaults to "SUPPORT_EMAIL" so that server admin knows they have to set it. + SUPPORT_NAME: '', + BUSINESS_EMAIL: '', + + // ================================================== + // Stripe config + // ================================================== + + STRIPE_SECRET_KEY: '', + STRIPE_WEBHOOK_SECRET: '', +}; + +export interface EnvVariables { APP_NAME: string; APP_PORT: number; SIGNUP_ENABLED: boolean; @@ -12,19 +87,11 @@ export interface EnvVariables { COOKIES_SECURE: boolean; RUNNING_IN_DOCKER: boolean; - // ================================================== - // URL config - // ================================================== - APP_BASE_URL: string; USER_CONTENT_BASE_URL: string; API_BASE_URL: string; JOPLINAPP_BASE_URL: string; - // ================================================== - // Database config - // ================================================== - DB_CLIENT: string; DB_SLOW_QUERY_LOG_ENABLED: boolean; DB_SLOW_QUERY_LOG_MIN_DURATION: number; @@ -36,12 +103,11 @@ export interface EnvVariables { POSTGRES_HOST: string; POSTGRES_PORT: number; - // This must be the full path to the database file SQLITE_DATABASE: string; - // ================================================== - // Mailer config - // ================================================== + CONTENT_DRIVER: string; + CONTENT_DRIVER_FALLBACK: string; + CONTENT_DRIVER_FALLBACK_WRITE: boolean; MAILER_ENABLED: boolean; MAILER_HOST: string; @@ -56,59 +122,10 @@ export interface EnvVariables { SUPPORT_NAME: string; BUSINESS_EMAIL: string; - // ================================================== - // Stripe config - // ================================================== - STRIPE_SECRET_KEY: string; STRIPE_WEBHOOK_SECRET: string; } -const defaultEnvValues: EnvVariables = { - APP_NAME: 'Joplin Server', - APP_PORT: 22300, - SIGNUP_ENABLED: false, - TERMS_ENABLED: false, - ACCOUNT_TYPES_ENABLED: false, - ERROR_STACK_TRACES: false, - COOKIES_SECURE: false, - RUNNING_IN_DOCKER: false, - - APP_BASE_URL: '', - USER_CONTENT_BASE_URL: '', - API_BASE_URL: '', - JOPLINAPP_BASE_URL: 'https://joplinapp.org', - - DB_CLIENT: 'sqlite3', - DB_SLOW_QUERY_LOG_ENABLED: false, - DB_SLOW_QUERY_LOG_MIN_DURATION: 1000, - DB_AUTO_MIGRATION: true, - - POSTGRES_PASSWORD: 'joplin', - POSTGRES_DATABASE: 'joplin', - POSTGRES_USER: 'joplin', - POSTGRES_HOST: '', - POSTGRES_PORT: 5432, - - SQLITE_DATABASE: '', - - MAILER_ENABLED: false, - MAILER_HOST: '', - MAILER_PORT: 587, - MAILER_SECURE: true, - MAILER_AUTH_USER: '', - MAILER_AUTH_PASSWORD: '', - MAILER_NOREPLY_NAME: '', - MAILER_NOREPLY_EMAIL: '', - - SUPPORT_EMAIL: 'SUPPORT_EMAIL', // Defaults to "SUPPORT_EMAIL" so that server admin knows they have to set it. - SUPPORT_NAME: '', - BUSINESS_EMAIL: '', - - STRIPE_SECRET_KEY: '', - STRIPE_WEBHOOK_SECRET: '', -}; - export function parseEnv(rawEnv: any, defaultOverrides: any = null): EnvVariables { const output: EnvVariables = { ...defaultEnvValues, diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index 51b9387cecf..fab829c153c 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -302,9 +302,11 @@ export default class ItemModel extends BaseModel { return this.itemToJoplinItem(raw); } - public async saveFromRawContent(user: User, rawContentItems: SaveFromRawContentItem[], options: ItemSaveOption = null): Promise { + public async saveFromRawContent(user: User, rawContentItems: SaveFromRawContentItem[] | SaveFromRawContentItem, options: ItemSaveOption = null): Promise { options = options || {}; + if (!Array.isArray(rawContentItems)) rawContentItems = [rawContentItems]; + // In this function, first we process the input items, which may be // serialized Joplin items or actual buffers (for resources) and convert // them to database items. Once it's done those db items are saved in diff --git a/packages/server/src/models/UserModel.test.ts b/packages/server/src/models/UserModel.test.ts index eb75fa4390a..6552c486d85 100644 --- a/packages/server/src/models/UserModel.test.ts +++ b/packages/server/src/models/UserModel.test.ts @@ -360,18 +360,18 @@ describe('UserModel', function() { const syncInfo3: any = JSON.parse(JSON.stringify(syncInfo1)); delete syncInfo3.ppk; - await models().item().saveForUser(user1.id, { - content: Buffer.from(JSON.stringify(syncInfo1)), + await models().item().saveFromRawContent(user1, { + body: Buffer.from(JSON.stringify(syncInfo1)), name: 'info.json', }); - await models().item().saveForUser(user2.id, { - content: Buffer.from(JSON.stringify(syncInfo2)), + await models().item().saveFromRawContent(user2, { + body: Buffer.from(JSON.stringify(syncInfo2)), name: 'info.json', }); - await models().item().saveForUser(user3.id, { - content: Buffer.from(JSON.stringify(syncInfo3)), + await models().item().saveFromRawContent(user3, { + body: Buffer.from(JSON.stringify(syncInfo3)), name: 'info.json', }); diff --git a/packages/server/src/models/UserModel.ts b/packages/server/src/models/UserModel.ts index 993d1ac2ac7..1eb945edf81 100644 --- a/packages/server/src/models/UserModel.ts +++ b/packages/server/src/models/UserModel.ts @@ -593,7 +593,7 @@ export default class UserModel extends BaseModel { public async publicPrivateKey(userId: string): Promise { const syncInfo = await this.syncInfo(userId); - return syncInfo.ppk?.value || null;// syncInfo.ppk?.value.publicKey || ''; + return syncInfo.ppk?.value || null; } // Note that when the "password" property is provided, it is going to be diff --git a/packages/server/src/models/itemModel/contentDriverFromConfig.ts b/packages/server/src/models/itemModel/contentDriverFromConfig.ts new file mode 100644 index 00000000000..ebcda045381 --- /dev/null +++ b/packages/server/src/models/itemModel/contentDriverFromConfig.ts @@ -0,0 +1,24 @@ +import { clientType, DbConnection } from '../../db'; +import { ContentDriverConfig, ContentDriverConfigType } from '../../utils/types'; +import ContentDriverBase from './ContentDriverBase'; +import ContentDriverDatabase from './ContentDriverDatabase'; +import ContentDriverFs from './ContentDriverFs'; +import ContentDriverMemory from './ContentDriverMemory'; + +export default function(config: ContentDriverConfig, db: DbConnection): ContentDriverBase | null { + if (!config) return null; + + if (config.type === ContentDriverConfigType.Database) { + return new ContentDriverDatabase({ dbClientType: clientType(db) }); + } + + if (config.type === ContentDriverConfigType.Filesystem) { + return new ContentDriverFs({ basePath: config.path }); + } + + if (config.type === ContentDriverConfigType.Memory) { + return new ContentDriverMemory(); + } + + throw new Error(`Invalid config: ${JSON.stringify(config)}`); +} diff --git a/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts b/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts new file mode 100644 index 00000000000..d9baa0d8e74 --- /dev/null +++ b/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts @@ -0,0 +1,35 @@ +// Type={Database,Filesystem,Memory,S3}; Path={/path/to/dir,https://s3bucket} + +import { ContentDriverConfig, ContentDriverConfigType } from '../../utils/types'; + +const parseType = (type: string): ContentDriverConfigType => { + if (type === 'Database') return ContentDriverConfigType.Database; + if (type === 'Filesystem') return ContentDriverConfigType.Filesystem; + if (type === 'Memory') return ContentDriverConfigType.Memory; + throw new Error(`Invalid type: ${type}`); +}; + +export default function(connectionString: string): ContentDriverConfig | null { + if (!connectionString) return null; + + const output: ContentDriverConfig = { + type: ContentDriverConfigType.Database, + path: '', + }; + + const items = connectionString.split(';').map(i => i.trim()); + + for (const item of items) { + const [key, value] = item.split('=').map(s => s.trim()); + + if (key === 'Type') { + output.type = parseType(value); + } else if (key === 'Path') { + output.path = value; + } else { + throw new Error(`Invalid key: ${key}`); + } + } + + return output; +} diff --git a/packages/server/src/services/database/types.ts b/packages/server/src/services/database/types.ts index 252e5155bf2..9c56a006e33 100644 --- a/packages/server/src/services/database/types.ts +++ b/packages/server/src/services/database/types.ts @@ -1,4 +1,4 @@ -export type Uuid = any; +export type Uuid = string; export enum ItemAddressingType { Id = 1, diff --git a/packages/server/src/utils/types.ts b/packages/server/src/utils/types.ts index d66d04fcfae..7b068bdccbe 100644 --- a/packages/server/src/utils/types.ts +++ b/packages/server/src/utils/types.ts @@ -87,6 +87,17 @@ export interface StripeConfig extends StripePublicConfig { webhookSecret: string; } +export enum ContentDriverConfigType { + Database = 1, + Filesystem = 2, + Memory = 3, +} + +export interface ContentDriverConfig { + type: ContentDriverConfigType; + path: string; +} + export interface Config { appVersion: string; appName: string; @@ -115,6 +126,8 @@ export interface Config { businessEmail: string; isJoplinCloud: boolean; cookieSecure: boolean; + contentDriver: ContentDriverConfig; + fallbackContentDriver: ContentDriverConfig; } export enum HttpMethod { From 20df46c0663907943de9473d95ae9bfc99c4a306 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Fri, 5 Nov 2021 11:43:27 +0000 Subject: [PATCH 09/17] fallback --- packages/server/src/env.ts | 2 - packages/server/src/models/ItemModel.ts | 24 ++++- .../src/models/itemModel/ContentDriverBase.ts | 20 +++++ .../itemModel/ContentDriverDatabase.test.ts | 62 ++----------- .../models/itemModel/ContentDriverDatabase.ts | 6 +- .../src/models/itemModel/ContentDriverFs.ts | 6 +- .../parseContentDriverConnectionString.ts | 11 ++- .../server/src/models/itemModel/testUtils.ts | 87 ++++++++++++++++++- packages/server/src/utils/types.ts | 26 ++++++ 9 files changed, 176 insertions(+), 68 deletions(-) diff --git a/packages/server/src/env.ts b/packages/server/src/env.ts index 35dde043b20..dc1de00fa53 100644 --- a/packages/server/src/env.ts +++ b/packages/server/src/env.ts @@ -50,7 +50,6 @@ const defaultEnvValues: EnvVariables = { CONTENT_DRIVER: 'Type=Database', CONTENT_DRIVER_FALLBACK: '', - CONTENT_DRIVER_FALLBACK_WRITE: false, // ================================================== // Mailer config @@ -107,7 +106,6 @@ export interface EnvVariables { CONTENT_DRIVER: string; CONTENT_DRIVER_FALLBACK: string; - CONTENT_DRIVER_FALLBACK_WRITE: boolean; MAILER_ENABLED: boolean; MAILER_HOST: string; diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index fab829c153c..38c99e5dde5 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -9,7 +9,7 @@ import { ChangePreviousItem } from './ChangeModel'; import { unique } from '../utils/array'; import ContentDriverBase, { Context } from './itemModel/ContentDriverBase'; import { DbConnection } from '../db'; -import { Config } from '../utils/types'; +import { Config, ContentDriverMode } from '../utils/types'; import { NewModelFactoryHandler, Options } from './factory'; const mimeUtils = require('@joplin/lib/mime-utils.js').mime; @@ -135,6 +135,20 @@ export default class ItemModel extends BaseModel { return await query; } + private async contentDriverWrite(itemId: Uuid, content: Buffer, context: Context) { + await this.contentDriver_.write(itemId, content, context); + + if (this.fallbackContentDriver_) { + if (this.fallbackContentDriver_.mode === ContentDriverMode.ReadWrite) { + await this.fallbackContentDriver_.write(itemId, content, context); + } else if (this.fallbackContentDriver_.mode === ContentDriverMode.ReadOnly) { + await this.fallbackContentDriver_.write(itemId, Buffer.from(''), context); + } else { + throw new Error(`Unsupported fallback mode: ${this.fallbackContentDriver_.mode}`); + } + } + } + private async contentDriverRead(itemId: Uuid, context: Context) { if (await this.contentDriver_.exists(itemId, context)) { return this.contentDriver_.read(itemId, context); @@ -430,8 +444,9 @@ export default class ItemModel extends BaseModel { const savedItem = await this.saveForUser(user.id, itemToSave); try { - await this.contentDriver_.write(savedItem.id, content, { models: this.models() }); - if (this.fallbackContentDriver_) await this.fallbackContentDriver_.write(savedItem.id, Buffer.from(''), { models: this.models() }); + await this.contentDriverWrite(savedItem.id, content, { models: this.models() }); + // await this.contentDriver_.write(savedItem.id, content, { models: this.models() }); + // if (this.fallbackContentDriver_) await this.fallbackContentDriver_.write(savedItem.id, Buffer.from(''), { models: this.models() }); await this.releaseSavePoint(savePoint); } catch (error) { await this.rollbackSavePoint(savePoint); @@ -651,6 +666,9 @@ export default class ItemModel extends BaseModel { }, 'ItemModel::makeTestItems'); } + // This method should be private because items should only be saved using + // saveFromRawContent, which is going to deal with the content driver. But + // since it's used in various test units, it's kept public for now. public async saveForUser(userId: Uuid, item: Item, options: SaveOptions = {}): Promise { if (!userId) throw new Error('userId is required'); diff --git a/packages/server/src/models/itemModel/ContentDriverBase.ts b/packages/server/src/models/itemModel/ContentDriverBase.ts index 4535814bc2c..9b99bf3e55a 100644 --- a/packages/server/src/models/itemModel/ContentDriverBase.ts +++ b/packages/server/src/models/itemModel/ContentDriverBase.ts @@ -1,3 +1,4 @@ +import { ContentDriverMode } from '../../utils/types'; import { Models } from '../factory'; // ItemModel passes the models object when calling any of the driver handler. @@ -8,8 +9,27 @@ export interface Context { models: Models; } +export interface Options { + mode?: ContentDriverMode; +} + export default class ContentDriverBase { + private mode_: ContentDriverMode = ContentDriverMode.ReadOnly; + + public constructor(options: Options = null) { + options = { + mode: ContentDriverMode.ReadOnly, + ...options, + }; + + this.mode_ = options.mode; + } + + public get mode(): ContentDriverMode { + return this.mode_; + } + public async write(_itemId: string, _content: Buffer, _context: Context): Promise { throw new Error('Not implemented'); } public async read(_itemId: string, _context: Context): Promise { throw new Error('Not implemented'); } diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts b/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts index d9961b3ce93..0e7fad3c9ce 100644 --- a/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts +++ b/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts @@ -1,9 +1,9 @@ import { clientType } from '../../db'; -import { Item } from '../../services/database/types'; -import { afterAllTests, beforeAllDb, beforeEachDb, createUserAndSession, db, expectNotThrow, expectThrow, makeNoteSerializedBody, models } from '../../utils/testing/testUtils'; +import { afterAllTests, beforeAllDb, beforeEachDb, db, expectNotThrow, expectThrow, models } from '../../utils/testing/testUtils'; +import { ContentDriverMode } from '../../utils/types'; import ContentDriverDatabase from './ContentDriverDatabase'; import ContentDriverMemory from './ContentDriverMemory'; -import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; +import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldSupportFallbackDriver, shouldSupportFallbackDriverInReadWriteMode, shouldWriteToContentAndReadItBack } from './testUtils'; const newDriver = () => { return new ContentDriverDatabase({ @@ -56,59 +56,11 @@ describe('ContentDriverDatabase', function() { }); test('should support fallback content drivers', async function() { - const dbDriver = newDriver(); - const memoryDriver = new ContentDriverMemory(); - - const testModels = models({ - contentDriver: dbDriver, - }); - - const { user } = await createUserAndSession(1); - - const output = await testModels.item().saveFromRawContent(user, [{ - name: '00000000000000000000000000000001.md', - body: Buffer.from(makeNoteSerializedBody({ - id: '00000000000000000000000000000001', - title: 'testing', - })), - }]); - - const itemId = output['00000000000000000000000000000001.md'].item.id; - - let previousByteLength = 0; - - { - const row: Item = await db()('items').select(['id', 'jop_id', 'content']).first(); - expect(row.content.byteLength).toBeGreaterThan(10); - previousByteLength = row.content.byteLength; - } - - const testModelWithFallback = models({ - contentDriver: memoryDriver, - fallbackContentDriver: dbDriver, - }); - - // If the item content is not on the main content driver, it should get - // it from the fallback one. - const itemFromDb = await testModelWithFallback.item().loadWithContent(itemId); - expect(itemFromDb.content.byteLength).toBe(previousByteLength); - - // When writing content, it should use the main content driver, and set - // the content for the fallback one to "". - await testModelWithFallback.item().saveFromRawContent(user, [{ - name: '00000000000000000000000000000001.md', - body: Buffer.from(makeNoteSerializedBody({ - id: '00000000000000000000000000000001', - title: 'testing1234', - })), - }]); + await shouldSupportFallbackDriver(newDriver(), new ContentDriverMemory()); + }); - { - const row: Item = await db()('items').select(['id', 'content']).first(); - expect(row.content.byteLength).toBe(0); - const memContent = await memoryDriver.read(itemId); - expect(memContent.byteLength).toBe(previousByteLength + 4); - } + test('should support fallback content drivers in rw mode', async function() { + await shouldSupportFallbackDriverInReadWriteMode(newDriver(), new ContentDriverMemory({ mode: ContentDriverMode.ReadWrite })); }); }); diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.ts b/packages/server/src/models/itemModel/ContentDriverDatabase.ts index 4e921704cab..d19da7bd66d 100644 --- a/packages/server/src/models/itemModel/ContentDriverDatabase.ts +++ b/packages/server/src/models/itemModel/ContentDriverDatabase.ts @@ -3,9 +3,9 @@ // stored in the same table as the items, as it originally was. import { DatabaseConfigClient } from '../../utils/types'; -import ContentDriverBase, { Context } from './ContentDriverBase'; +import ContentDriverBase, { Context, Options as BaseOptions } from './ContentDriverBase'; -interface Options { +interface Options extends BaseOptions { dbClientType: DatabaseConfigClient; } @@ -14,7 +14,7 @@ export default class ContentDriverDatabase extends ContentDriverBase { private handleReturnedRows_: boolean = null; public constructor(options: Options) { - super(); + super(options); this.handleReturnedRows_ = options.dbClientType === DatabaseConfigClient.PostgreSQL; } diff --git a/packages/server/src/models/itemModel/ContentDriverFs.ts b/packages/server/src/models/itemModel/ContentDriverFs.ts index 8121d6399d7..f7266edb7a5 100644 --- a/packages/server/src/models/itemModel/ContentDriverFs.ts +++ b/packages/server/src/models/itemModel/ContentDriverFs.ts @@ -1,7 +1,7 @@ import { mkdirp, pathExists, readFile, remove, writeFile } from 'fs-extra'; -import ContentDriverBase from './ContentDriverBase'; +import ContentDriverBase, { Options as BaseOptions } from './ContentDriverBase'; -interface Options { +interface Options extends BaseOptions { basePath: string; } @@ -11,7 +11,7 @@ export default class ContentDriverFs extends ContentDriverBase { private pathCreated_: Record = {}; public constructor(options: Options) { - super(); + super(options); this.options_ = options; } diff --git a/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts b/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts index d9baa0d8e74..1e85983c918 100644 --- a/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts +++ b/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts @@ -1,6 +1,6 @@ // Type={Database,Filesystem,Memory,S3}; Path={/path/to/dir,https://s3bucket} -import { ContentDriverConfig, ContentDriverConfigType } from '../../utils/types'; +import { ContentDriverConfig, ContentDriverMode, ContentDriverConfigType } from '../../utils/types'; const parseType = (type: string): ContentDriverConfigType => { if (type === 'Database') return ContentDriverConfigType.Database; @@ -9,12 +9,19 @@ const parseType = (type: string): ContentDriverConfigType => { throw new Error(`Invalid type: ${type}`); }; +const parseMode = (mode: string): ContentDriverMode => { + if (mode === 'rw') return ContentDriverMode.ReadWrite; + if (mode === 'r') return ContentDriverMode.ReadOnly; + throw new Error(`Invalid type: ${mode}`); +}; + export default function(connectionString: string): ContentDriverConfig | null { if (!connectionString) return null; const output: ContentDriverConfig = { type: ContentDriverConfigType.Database, path: '', + mode: ContentDriverMode.ReadWrite, }; const items = connectionString.split(';').map(i => i.trim()); @@ -26,6 +33,8 @@ export default function(connectionString: string): ContentDriverConfig | null { output.type = parseType(value); } else if (key === 'Path') { output.path = value; + } else if (key === 'Mode') { + output.mode = parseMode(value); } else { throw new Error(`Invalid key: ${key}`); } diff --git a/packages/server/src/models/itemModel/testUtils.ts b/packages/server/src/models/itemModel/testUtils.ts index f928a6e68d4..d239e68fb83 100644 --- a/packages/server/src/models/itemModel/testUtils.ts +++ b/packages/server/src/models/itemModel/testUtils.ts @@ -1,6 +1,7 @@ import { Item } from '../../services/database/types'; import { createUserAndSession, makeNoteSerializedBody, models } from '../../utils/testing/testUtils'; -import ContentDriverBase from './ContentDriverBase'; +import { ContentDriverMode } from '../../utils/types'; +import ContentDriverBase, { Context } from './ContentDriverBase'; const testModels = (driver: ContentDriverBase) => { return models({ contentDriver: driver }); @@ -120,3 +121,87 @@ export async function shouldNotUpdateItemIfContentNotSaved(driver: ContentDriver driver.write = previousWrite; } } + +export async function shouldSupportFallbackDriver(driver: ContentDriverBase, fallbackDriver: ContentDriverBase) { + const { user } = await createUserAndSession(1); + + const output = await testModels(driver).item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing', + })), + }]); + + const itemId = output['00000000000000000000000000000001.md'].item.id; + + let previousByteLength = 0; + + { + const content = await driver.read(itemId, { models: models() }); + expect(content.byteLength).toBeGreaterThan(10); + previousByteLength = content.byteLength; + } + + const testModelWithFallback = models({ + contentDriver: driver, + fallbackContentDriver: fallbackDriver, + }); + + // If the item content is not on the main content driver, it should get + // it from the fallback one. + const itemFromDb = await testModelWithFallback.item().loadWithContent(itemId); + expect(itemFromDb.content.byteLength).toBe(previousByteLength); + + // When writing content, it should use the main content driver, and set + // the content for the fallback one to "". + await testModelWithFallback.item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing1234', + })), + }]); + + { + // Check that it has cleared the fallback driver content + const context: Context = { models: models() }; + const fallbackContent = await fallbackDriver.read(itemId, context); + expect(fallbackContent.byteLength).toBe(0); + + // Check that it has written to the main driver content + const mainContent = await driver.read(itemId, context); + expect(mainContent.byteLength).toBe(previousByteLength + 4); + } +} + +export async function shouldSupportFallbackDriverInReadWriteMode(driver: ContentDriverBase, fallbackDriver: ContentDriverBase) { + if (fallbackDriver.mode !== ContentDriverMode.ReadWrite) throw new Error('Content driver must be configured in RW mode for this test'); + + const { user } = await createUserAndSession(1); + + const testModelWithFallback = models({ + contentDriver: driver, + fallbackContentDriver: fallbackDriver, + }); + + const output = await testModelWithFallback.item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing', + })), + }]); + + const itemId = output['00000000000000000000000000000001.md'].item.id; + + { + // Check that it has written the content to both drivers + const context: Context = { models: models() }; + const fallbackContent = await fallbackDriver.read(itemId, context); + expect(fallbackContent.byteLength).toBeGreaterThan(10); + + const mainContent = await driver.read(itemId, context); + expect(mainContent.toString()).toBe(fallbackContent.toString()); + } +} diff --git a/packages/server/src/utils/types.ts b/packages/server/src/utils/types.ts index 7b068bdccbe..3a0aaa89276 100644 --- a/packages/server/src/utils/types.ts +++ b/packages/server/src/utils/types.ts @@ -93,9 +93,35 @@ export enum ContentDriverConfigType { Memory = 3, } +// The driver mode is only used by fallback drivers. Regardless of the mode, the +// fallback always work like this: +// +// When reading, first the app checks if the content exists on the main driver. +// If it does it returns this. Otherwise it reads the content from the fallback +// driver. +// +// When writing, the app writes to the main driver. Then the mode determines how +// it writes to the fallback driver: +// +// - In read-only mode, it's going to clear the fallback driver content. This is +// used to migrate from one driver to another. It means that over time the old +// storage will be cleared and all content will be on the new storage. +// +// - In read/write mode, it's going to write the content to the fallback driver. +// This is purely for safey - it allows deploying the new storage (such as the +// filesystem or S3) but still keep the old content up-to-date. So if +// something goes wrong it's possible to go back to the old storage until the +// new one is working. + +export enum ContentDriverMode { + ReadWrite = 1, + ReadOnly = 2, +} + export interface ContentDriverConfig { type: ContentDriverConfigType; path: string; + mode: ContentDriverMode; } export interface Config { From fa3612405ca29a68afcbe2732175d56dc2fe3a23 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Fri, 5 Nov 2021 12:05:03 +0000 Subject: [PATCH 10/17] tests --- .../itemModel/contentDriverFromConfig.ts | 8 +-- ...parseContentDriverConnectionString.test.ts | 41 ++++++++++++++ .../parseContentDriverConnectionString.ts | 54 +++++++++++-------- packages/server/src/utils/types.ts | 8 +-- 4 files changed, 81 insertions(+), 30 deletions(-) create mode 100644 packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts diff --git a/packages/server/src/models/itemModel/contentDriverFromConfig.ts b/packages/server/src/models/itemModel/contentDriverFromConfig.ts index ebcda045381..6285dc778a5 100644 --- a/packages/server/src/models/itemModel/contentDriverFromConfig.ts +++ b/packages/server/src/models/itemModel/contentDriverFromConfig.ts @@ -1,5 +1,5 @@ import { clientType, DbConnection } from '../../db'; -import { ContentDriverConfig, ContentDriverConfigType } from '../../utils/types'; +import { ContentDriverConfig, ContentDriverType } from '../../utils/types'; import ContentDriverBase from './ContentDriverBase'; import ContentDriverDatabase from './ContentDriverDatabase'; import ContentDriverFs from './ContentDriverFs'; @@ -8,15 +8,15 @@ import ContentDriverMemory from './ContentDriverMemory'; export default function(config: ContentDriverConfig, db: DbConnection): ContentDriverBase | null { if (!config) return null; - if (config.type === ContentDriverConfigType.Database) { + if (config.type === ContentDriverType.Database) { return new ContentDriverDatabase({ dbClientType: clientType(db) }); } - if (config.type === ContentDriverConfigType.Filesystem) { + if (config.type === ContentDriverType.Filesystem) { return new ContentDriverFs({ basePath: config.path }); } - if (config.type === ContentDriverConfigType.Memory) { + if (config.type === ContentDriverType.Memory) { return new ContentDriverMemory(); } diff --git a/packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts b/packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts new file mode 100644 index 00000000000..9b4902e6584 --- /dev/null +++ b/packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts @@ -0,0 +1,41 @@ +import { ContentDriverConfig, ContentDriverType } from '../../utils/types'; +import parseContentDriverConnectionString from './parseContentDriverConnectionString'; + +describe('parseContentDriverConnectionString', function() { + + test('should parse a connection string', async function() { + const testCases: Record = { + 'Type=Database': { + type: ContentDriverType.Database, + }, + ' Type = Database ': { + type: ContentDriverType.Database, + }, + 'Type=Filesystem; Path=/path/to/dir': { + type: ContentDriverType.Filesystem, + path: '/path/to/dir', + }, + ' Type = Filesystem ; Path = /path/to/dir ': { + type: ContentDriverType.Filesystem, + path: '/path/to/dir', + }, + 'Type=Memory;': { + type: ContentDriverType.Memory, + }, + '': null, + }; + + for (const [connectionString, config] of Object.entries(testCases)) { + const actual = parseContentDriverConnectionString(connectionString); + expect(actual).toEqual(config); + } + }); + + test('should detect errors', async function() { + expect(() => parseContentDriverConnectionString('Type=')).toThrow(); + expect(() => parseContentDriverConnectionString('Type;')).toThrow(); + expect(() => parseContentDriverConnectionString('Type=DoesntExist')).toThrow(); + expect(() => parseContentDriverConnectionString('Type=Filesystem')).toThrow(); + }); + +}); diff --git a/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts b/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts index 1e85983c918..0854ba76267 100644 --- a/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts +++ b/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts @@ -1,44 +1,54 @@ // Type={Database,Filesystem,Memory,S3}; Path={/path/to/dir,https://s3bucket} -import { ContentDriverConfig, ContentDriverMode, ContentDriverConfigType } from '../../utils/types'; +import { ContentDriverConfig, ContentDriverMode, ContentDriverType } from '../../utils/types'; -const parseType = (type: string): ContentDriverConfigType => { - if (type === 'Database') return ContentDriverConfigType.Database; - if (type === 'Filesystem') return ContentDriverConfigType.Filesystem; - if (type === 'Memory') return ContentDriverConfigType.Memory; - throw new Error(`Invalid type: ${type}`); +const parseType = (type: string): ContentDriverType => { + if (type === 'Database') return ContentDriverType.Database; + if (type === 'Filesystem') return ContentDriverType.Filesystem; + if (type === 'Memory') return ContentDriverType.Memory; + throw new Error(`Invalid type: "${type}"`); }; const parseMode = (mode: string): ContentDriverMode => { if (mode === 'rw') return ContentDriverMode.ReadWrite; if (mode === 'r') return ContentDriverMode.ReadOnly; - throw new Error(`Invalid type: ${mode}`); + throw new Error(`Invalid type: "${mode}"`); +}; + +const validate = (config: ContentDriverConfig) => { + if (config.type === ContentDriverType.Filesystem && !config.path) throw new Error('Path must be set for filesystem driver'); + return config; }; export default function(connectionString: string): ContentDriverConfig | null { if (!connectionString) return null; const output: ContentDriverConfig = { - type: ContentDriverConfigType.Database, - path: '', - mode: ContentDriverMode.ReadWrite, + type: ContentDriverType.Database, }; const items = connectionString.split(';').map(i => i.trim()); - for (const item of items) { - const [key, value] = item.split('=').map(s => s.trim()); - - if (key === 'Type') { - output.type = parseType(value); - } else if (key === 'Path') { - output.path = value; - } else if (key === 'Mode') { - output.mode = parseMode(value); - } else { - throw new Error(`Invalid key: ${key}`); + try { + for (const item of items) { + if (!item) continue; + + const [key, value] = item.split('=').map(s => s.trim()); + + if (key === 'Type') { + output.type = parseType(value); + } else if (key === 'Path') { + output.path = value; + } else if (key === 'Mode') { + output.mode = parseMode(value); + } else { + throw new Error(`Invalid key: "${key}"`); + } } + } catch (error) { + error.message = `In connection string "${connectionString}": ${error.message}`; + throw error; } - return output; + return validate(output); } diff --git a/packages/server/src/utils/types.ts b/packages/server/src/utils/types.ts index 3a0aaa89276..8818c75d3c9 100644 --- a/packages/server/src/utils/types.ts +++ b/packages/server/src/utils/types.ts @@ -87,7 +87,7 @@ export interface StripeConfig extends StripePublicConfig { webhookSecret: string; } -export enum ContentDriverConfigType { +export enum ContentDriverType { Database = 1, Filesystem = 2, Memory = 3, @@ -119,9 +119,9 @@ export enum ContentDriverMode { } export interface ContentDriverConfig { - type: ContentDriverConfigType; - path: string; - mode: ContentDriverMode; + type: ContentDriverType; + path?: string; + mode?: ContentDriverMode; } export interface Config { From 5d646f7cedb904d951fae700597b0d04c6566e50 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 6 Nov 2021 15:33:07 +0000 Subject: [PATCH 11/17] env --- packages/server/src/env.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/env.ts b/packages/server/src/env.ts index dc1de00fa53..5a4e9ea8e1e 100644 --- a/packages/server/src/env.ts +++ b/packages/server/src/env.ts @@ -140,7 +140,7 @@ export function parseEnv(rawEnv: any, defaultOverrides: any = null): EnvVariable if (isNaN(v)) throw new Error(`Invalid number value for env variable ${key} = ${rawEnvValue}`); (output as any)[key] = v; } else if (typeof value === 'boolean') { - if (rawEnvValue !== '0' && rawEnvValue !== '1') throw new Error(`Invalid boolean for for env variable ${key}: ${rawEnvValue}`); + if (rawEnvValue !== '0' && rawEnvValue !== '1') throw new Error(`Invalid boolean value for env variable ${key}: ${rawEnvValue} (Should be either "0" or "1")`); (output as any)[key] = rawEnvValue === '1'; } else if (typeof value === 'string') { (output as any)[key] = `${rawEnvValue}`; From cc4c50c21910d03751d502f0c99727597cba5ca0 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 6 Nov 2021 16:23:43 +0000 Subject: [PATCH 12/17] rename --- packages/server/schema.sqlite | Bin 311296 -> 311296 bytes packages/server/src/app.ts | 10 ++-- packages/server/src/config.ts | 6 +- packages/server/src/models/ItemModel.ts | 52 +++++++++--------- packages/server/src/models/factory.ts | 8 +-- ...tentDriverBase.ts => StorageDriverBase.ts} | 14 ++--- ....test.ts => StorageDriverDatabase.test.ts} | 16 +++--- ...erDatabase.ts => StorageDriverDatabase.ts} | 4 +- ...iverFs.test.ts => StorageDriverFs.test.ts} | 8 +-- ...{ContentDriverFs.ts => StorageDriverFs.ts} | 4 +- ...ry.test.ts => StorageDriverMemory.test.ts} | 14 ++--- ...DriverMemory.ts => StorageDriverMemory.ts} | 4 +- .../itemModel/contentDriverFromConfig.ts | 24 -------- ...parseContentDriverConnectionString.test.ts | 28 +++++----- ... => parseStorageDriverConnectionString.ts} | 26 ++++----- .../itemModel/storageDriverFromConfig.ts | 24 ++++++++ .../server/src/models/itemModel/testUtils.ts | 30 +++++----- packages/server/src/tools/debugTools.ts | 4 +- .../server/src/utils/testing/testUtils.ts | 10 ++-- packages/server/src/utils/types.ts | 14 ++--- 20 files changed, 149 insertions(+), 151 deletions(-) rename packages/server/src/models/itemModel/{ContentDriverBase.ts => StorageDriverBase.ts} (74%) rename packages/server/src/models/itemModel/{ContentDriverDatabase.test.ts => StorageDriverDatabase.test.ts} (81%) rename packages/server/src/models/itemModel/{ContentDriverDatabase.ts => StorageDriverDatabase.ts} (92%) rename packages/server/src/models/itemModel/{ContentDriverFs.test.ts => StorageDriverFs.test.ts} (93%) rename packages/server/src/models/itemModel/{ContentDriverFs.ts => StorageDriverFs.ts} (90%) rename packages/server/src/models/itemModel/{ContentDriverMemory.test.ts => StorageDriverMemory.test.ts} (73%) rename packages/server/src/models/itemModel/{ContentDriverMemory.ts => StorageDriverMemory.ts} (82%) delete mode 100644 packages/server/src/models/itemModel/contentDriverFromConfig.ts rename packages/server/src/models/itemModel/{parseContentDriverConnectionString.ts => parseStorageDriverConnectionString.ts} (56%) create mode 100644 packages/server/src/models/itemModel/storageDriverFromConfig.ts diff --git a/packages/server/schema.sqlite b/packages/server/schema.sqlite index de0102caa507f18b2642e65f8efc05db487df09f..ab7f2c6a3382ed9073d65a8454e1f7590cf7723a 100644 GIT binary patch delta 1839 zcmc(geN04d3icw8qe`WBqgRq|7B!bP1B&smtI7nI}BnuZwc)(gr* z@RVLK55x0%L3t4VkRA!7sB0>TRt@kfIZS5frRXvJ(f$60CmnReZ$22WwKX(Vce}g# z>Z^LKep_vzH4>|H_Xd2yJ^i5`cfU8pM2b!BVk2GrXg6)C3)?#P5ALnE57=4*`$mrR zMJxO0h}-zMslq$hV!#i`Gx=7CM69piS$w^5*afa~X>&kPWOgW5?Vm~VLu*1-vb5n8b;S#CZU;tyg z(YGn^2k8p9nFv?Ueoq|HOF)bI-v_w__gjb}*9sV9FJ1uK0J}U3cF56JE1-uR{s7pw zQPGk1Cz`zjJ%K8pc^}gmZ|rLKwhZ{|W0t*bKE~XRzP}7~3a)nUoFl¬cc3#JDIM z$3&9hEJXfAo+9%V9r7vJE!ke_6&#b!NF9>ZdmbGz&Lvhu5aZZs_W4!tBBpp2)JoL_ z1>pE0w){G%2Ah&xS_d{lISzhhJ&WLFfX=kxUFfM55Ji@2V4aQr477l~y#y!>ZQlS9 zcH$TCC*U>&8$lB{fS)Jwl~v&60KAR#EK!CoZh!_}tVR^%8 zEW9{|E-!;No_3+v=7}A^EL@CCn=+N8rzWY?B$>mKF6`h zS}wWNB%oN}z$JSo9K;DSiL+#qxUI)?#S2LKE>VJs7r2)6y0~KWX{p+-2G2a?3-)?q zd@EwoT`1j#m(^XJACC=1;ej NKgt63g*k$e{|z{^V2}U+ delta 1666 zcmc(eZ%i9?7{~9qyI%huR|n(P|8)f+bCCk;+9f8g<4?*4wxEncbWmvLZcs``p<^zt zn-B@fNXc*UH{U2O`ob6fp=fW;EpdsyGa6$|jD~M zzvrIk`F)?~eorb@o=TOcYZN3w5YI2|{)|)AvJx7#DWudlP_JoI%lFxvwPcME1x*g- z`r~Um9R_sPWTPcCqK{40G8oWrrdk8$cut2yMd*2u7l2CRiox6zDMK6Lda zdfi$l!v}@>k zD;#@|lIoj)QkM*@A54U%eN>VBwJ)+p5`>VW6 zYK^m(irSJ>HUL{ll^p;GxLFNR30DZtTb3_7sH@a|r#++AslHats+7u?nRl3H7#;lr zeT2HfzCnJ(Cdody!d5`bvRBUn$!bI)12dpGx4l})I~?$A95*b%?`Tn5F6Nfu0kza~ zB3j2oQWzC0uE8tY?4EdNJsp1LEiyDP`fiqHloXSJDOdg!S z3`VPvh(yQx>kF?&$;^6rIBOC{!;$e_N*)hqAdaO*4B^}BkkgzCG%0z50j8f8b>G5X z!1-^WT>Nqa%plI)g1bO-^VE6Vk)>KpbwFBhm{UxU*`#w2c z_kRzzNPSOkVX~#-GK*jrAwV(v0iIK(DN&WC@7kzsOdD3dMORA~+ro%#Sz1*80a}MV AU;qFB diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index d9905f6432d..b4de9913ca8 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -21,7 +21,7 @@ import newModelFactory, { Options } from './models/factory'; import setupCommands from './utils/setupCommands'; import { RouteResponseFormat, routeResponseFormat } from './utils/routeUtils'; import { parseEnv } from './env'; -import contentDriverFromConfig from './models/itemModel/contentDriverFromConfig'; +import storageDriverFromConfig from './models/itemModel/storageDriverFromConfig'; interface Argv { env?: Env; @@ -224,8 +224,8 @@ async function main() { const newModelFactoryOptions = (db: DbConnection): Options => { return { - contentDriver: contentDriverFromConfig(config().contentDriver, db), - fallbackContentDriver: contentDriverFromConfig(config().fallbackContentDriver, db), + storageDriver: storageDriverFromConfig(config().storageDriver, db), + storageDriverFallback: storageDriverFromConfig(config().storageDriverFallback, db), }; }; @@ -263,8 +263,8 @@ async function main() { appLogger().info('Log dir:', config().logDir); appLogger().info('DB Config:', markPasswords(config().database)); appLogger().info('Mailer Config:', markPasswords(config().mailer)); - appLogger().info('Content driver:', markPasswords(config().contentDriver)); - appLogger().info('Content driver (fallback):', markPasswords(config().fallbackContentDriver)); + appLogger().info('Content driver:', markPasswords(config().storageDriver)); + appLogger().info('Content driver (fallback):', markPasswords(config().storageDriverFallback)); appLogger().info('Trying to connect to database...'); const connectionCheck = await waitForConnection(config().database); diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts index c718bf09c83..dcd52844f9f 100644 --- a/packages/server/src/config.ts +++ b/packages/server/src/config.ts @@ -3,7 +3,7 @@ import { Config, DatabaseConfig, DatabaseConfigClient, Env, MailerConfig, RouteT import * as pathUtils from 'path'; import { loadStripeConfig, StripePublicConfig } from '@joplin/lib/utils/joplinCloud'; import { EnvVariables } from './env'; -import parseContentDriverConnectionString from './models/itemModel/parseContentDriverConnectionString'; +import parseStorageDriverConnectionString from './models/itemModel/parseStorageDriverConnectionString'; interface PackageJson { version: string; @@ -131,8 +131,8 @@ export async function initConfig(envType: Env, env: EnvVariables, overrides: any supportName: env.SUPPORT_NAME || appName, businessEmail: env.BUSINESS_EMAIL || supportEmail, cookieSecure: env.COOKIES_SECURE, - contentDriver: parseContentDriverConnectionString(env.CONTENT_DRIVER), - fallbackContentDriver: parseContentDriverConnectionString(env.CONTENT_DRIVER_FALLBACK), + storageDriver: parseStorageDriverConnectionString(env.CONTENT_DRIVER), + storageDriverFallback: parseStorageDriverConnectionString(env.CONTENT_DRIVER_FALLBACK), ...overrides, }; } diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index 38c99e5dde5..7da863c5924 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -7,9 +7,9 @@ import { ApiError, ErrorForbidden, ErrorUnprocessableEntity } from '../utils/err import { Knex } from 'knex'; import { ChangePreviousItem } from './ChangeModel'; import { unique } from '../utils/array'; -import ContentDriverBase, { Context } from './itemModel/ContentDriverBase'; +import StorageDriverBase, { Context } from './itemModel/StorageDriverBase'; import { DbConnection } from '../db'; -import { Config, ContentDriverMode } from '../utils/types'; +import { Config, StorageDriverMode } from '../utils/types'; import { NewModelFactoryHandler, Options } from './factory'; const mimeUtils = require('@joplin/lib/mime-utils.js').mime; @@ -49,14 +49,14 @@ export interface ItemLoadOptions extends LoadOptions { export default class ItemModel extends BaseModel { private updatingTotalSizes_: boolean = false; - private contentDriver_: ContentDriverBase = null; - private fallbackContentDriver_: ContentDriverBase = null; + private storageDriver_: StorageDriverBase = null; + private storageDriverFallback_: StorageDriverBase = null; public constructor(db: DbConnection, modelFactory: NewModelFactoryHandler, config: Config, options: Options) { super(db, modelFactory, config); - this.contentDriver_ = options.contentDriver; - this.fallbackContentDriver_ = options.fallbackContentDriver; + this.storageDriver_ = options.storageDriver; + this.storageDriverFallback_ = options.storageDriverFallback; } protected get tableName(): string { @@ -135,26 +135,26 @@ export default class ItemModel extends BaseModel { return await query; } - private async contentDriverWrite(itemId: Uuid, content: Buffer, context: Context) { - await this.contentDriver_.write(itemId, content, context); + private async storageDriverWrite(itemId: Uuid, content: Buffer, context: Context) { + await this.storageDriver_.write(itemId, content, context); - if (this.fallbackContentDriver_) { - if (this.fallbackContentDriver_.mode === ContentDriverMode.ReadWrite) { - await this.fallbackContentDriver_.write(itemId, content, context); - } else if (this.fallbackContentDriver_.mode === ContentDriverMode.ReadOnly) { - await this.fallbackContentDriver_.write(itemId, Buffer.from(''), context); + if (this.storageDriverFallback_) { + if (this.storageDriverFallback_.mode === StorageDriverMode.ReadWrite) { + await this.storageDriverFallback_.write(itemId, content, context); + } else if (this.storageDriverFallback_.mode === StorageDriverMode.ReadOnly) { + await this.storageDriverFallback_.write(itemId, Buffer.from(''), context); } else { - throw new Error(`Unsupported fallback mode: ${this.fallbackContentDriver_.mode}`); + throw new Error(`Unsupported fallback mode: ${this.storageDriverFallback_.mode}`); } } } - private async contentDriverRead(itemId: Uuid, context: Context) { - if (await this.contentDriver_.exists(itemId, context)) { - return this.contentDriver_.read(itemId, context); + private async storageDriverRead(itemId: Uuid, context: Context) { + if (await this.storageDriver_.exists(itemId, context)) { + return this.storageDriver_.read(itemId, context); } else { - if (!this.fallbackContentDriver_) throw new Error(`Content does not exist but fallback content driver is not defined: ${itemId}`); - return this.fallbackContentDriver_.read(itemId, context); + if (!this.storageDriverFallback_) throw new Error(`Content does not exist but fallback content driver is not defined: ${itemId}`); + return this.storageDriverFallback_.read(itemId, context); } } @@ -173,7 +173,7 @@ export default class ItemModel extends BaseModel { if (options.withContent) { for (const row of rows) { - row.content = await this.contentDriverRead(row.id, { models: this.models() }); + row.content = await this.storageDriverRead(row.id, { models: this.models() }); } } @@ -199,7 +199,7 @@ export default class ItemModel extends BaseModel { if (options.withContent) { for (const row of rows) { - row.content = await this.contentDriverRead(row.id, { models: this.models() }); + row.content = await this.storageDriverRead(row.id, { models: this.models() }); } } @@ -212,7 +212,7 @@ export default class ItemModel extends BaseModel { } public async loadWithContent(id: Uuid, options: ItemLoadOptions = {}): Promise { - const content = await this.contentDriverRead(id, { models: this.models() }); + const content = await this.storageDriverRead(id, { models: this.models() }); return { ...await this @@ -444,9 +444,7 @@ export default class ItemModel extends BaseModel { const savedItem = await this.saveForUser(user.id, itemToSave); try { - await this.contentDriverWrite(savedItem.id, content, { models: this.models() }); - // await this.contentDriver_.write(savedItem.id, content, { models: this.models() }); - // if (this.fallbackContentDriver_) await this.fallbackContentDriver_.write(savedItem.id, Buffer.from(''), { models: this.models() }); + await this.storageDriverWrite(savedItem.id, content, { models: this.models() }); await this.releaseSavePoint(savePoint); } catch (error) { await this.rollbackSavePoint(savePoint); @@ -631,8 +629,8 @@ export default class ItemModel extends BaseModel { await this.models().share().delete(shares.map(s => s.id)); await this.models().userItem().deleteByItemIds(ids); await this.models().itemResource().deleteByItemIds(ids); - await this.contentDriver_.delete(ids, { models: this.models() }); - if (this.fallbackContentDriver_) await this.fallbackContentDriver_.delete(ids, { models: this.models() }); + await this.storageDriver_.delete(ids, { models: this.models() }); + if (this.storageDriverFallback_) await this.storageDriverFallback_.delete(ids, { models: this.models() }); await super.delete(ids, options); }, 'ItemModel::delete'); diff --git a/packages/server/src/models/factory.ts b/packages/server/src/models/factory.ts index 09fad69c947..b1c4b443a93 100644 --- a/packages/server/src/models/factory.ts +++ b/packages/server/src/models/factory.ts @@ -72,12 +72,12 @@ import SubscriptionModel from './SubscriptionModel'; import UserFlagModel from './UserFlagModel'; import EventModel from './EventModel'; import { Config } from '../utils/types'; -import ContentDriverBase from './itemModel/ContentDriverBase'; +import StorageDriverBase from './itemModel/StorageDriverBase'; import LockModel from './LockModel'; export interface Options { - contentDriver: ContentDriverBase; - fallbackContentDriver?: ContentDriverBase; + storageDriver: StorageDriverBase; + storageDriverFallback?: StorageDriverBase; } export type NewModelFactoryHandler = (db: DbConnection)=> Models; @@ -93,7 +93,7 @@ export class Models { this.config_ = config; this.options_ = options; - if (!options.contentDriver) throw new Error('contentDriver is required'); + if (!options.storageDriver) throw new Error('StorageDriver is required'); this.newModelFactory = this.newModelFactory.bind(this); } diff --git a/packages/server/src/models/itemModel/ContentDriverBase.ts b/packages/server/src/models/itemModel/StorageDriverBase.ts similarity index 74% rename from packages/server/src/models/itemModel/ContentDriverBase.ts rename to packages/server/src/models/itemModel/StorageDriverBase.ts index 9b99bf3e55a..e0c3adf2946 100644 --- a/packages/server/src/models/itemModel/ContentDriverBase.ts +++ b/packages/server/src/models/itemModel/StorageDriverBase.ts @@ -1,32 +1,32 @@ -import { ContentDriverMode } from '../../utils/types'; +import { StorageDriverMode } from '../../utils/types'; import { Models } from '../factory'; // ItemModel passes the models object when calling any of the driver handler. // This is so that if there's an active transaction, the driver can use that (as -// required for example by ContentDriverDatabase). +// required for example by StorageDriverDatabase). export interface Context { models: Models; } export interface Options { - mode?: ContentDriverMode; + mode?: StorageDriverMode; } -export default class ContentDriverBase { +export default class StorageDriverBase { - private mode_: ContentDriverMode = ContentDriverMode.ReadOnly; + private mode_: StorageDriverMode = StorageDriverMode.ReadOnly; public constructor(options: Options = null) { options = { - mode: ContentDriverMode.ReadOnly, + mode: StorageDriverMode.ReadOnly, ...options, }; this.mode_ = options.mode; } - public get mode(): ContentDriverMode { + public get mode(): StorageDriverMode { return this.mode_; } diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts b/packages/server/src/models/itemModel/StorageDriverDatabase.test.ts similarity index 81% rename from packages/server/src/models/itemModel/ContentDriverDatabase.test.ts rename to packages/server/src/models/itemModel/StorageDriverDatabase.test.ts index 0e7fad3c9ce..234836781da 100644 --- a/packages/server/src/models/itemModel/ContentDriverDatabase.test.ts +++ b/packages/server/src/models/itemModel/StorageDriverDatabase.test.ts @@ -1,20 +1,20 @@ import { clientType } from '../../db'; import { afterAllTests, beforeAllDb, beforeEachDb, db, expectNotThrow, expectThrow, models } from '../../utils/testing/testUtils'; -import { ContentDriverMode } from '../../utils/types'; -import ContentDriverDatabase from './ContentDriverDatabase'; -import ContentDriverMemory from './ContentDriverMemory'; +import { StorageDriverMode } from '../../utils/types'; +import StorageDatabase from './StorageDriverDatabase'; +import StorageDriverMemory from './StorageDriverMemory'; import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldSupportFallbackDriver, shouldSupportFallbackDriverInReadWriteMode, shouldWriteToContentAndReadItBack } from './testUtils'; const newDriver = () => { - return new ContentDriverDatabase({ + return new StorageDatabase({ dbClientType: clientType(db()), }); }; -describe('ContentDriverDatabase', function() { +describe('StorageDriverDatabase', function() { beforeAll(async () => { - await beforeAllDb('ContentDriverDatabase'); + await beforeAllDb('StorageDriverDatabase'); }); afterAll(async () => { @@ -56,11 +56,11 @@ describe('ContentDriverDatabase', function() { }); test('should support fallback content drivers', async function() { - await shouldSupportFallbackDriver(newDriver(), new ContentDriverMemory()); + await shouldSupportFallbackDriver(newDriver(), new StorageDriverMemory()); }); test('should support fallback content drivers in rw mode', async function() { - await shouldSupportFallbackDriverInReadWriteMode(newDriver(), new ContentDriverMemory({ mode: ContentDriverMode.ReadWrite })); + await shouldSupportFallbackDriverInReadWriteMode(newDriver(), new StorageDriverMemory({ mode: StorageDriverMode.ReadWrite })); }); }); diff --git a/packages/server/src/models/itemModel/ContentDriverDatabase.ts b/packages/server/src/models/itemModel/StorageDriverDatabase.ts similarity index 92% rename from packages/server/src/models/itemModel/ContentDriverDatabase.ts rename to packages/server/src/models/itemModel/StorageDriverDatabase.ts index d19da7bd66d..ff4585a95ad 100644 --- a/packages/server/src/models/itemModel/ContentDriverDatabase.ts +++ b/packages/server/src/models/itemModel/StorageDriverDatabase.ts @@ -3,13 +3,13 @@ // stored in the same table as the items, as it originally was. import { DatabaseConfigClient } from '../../utils/types'; -import ContentDriverBase, { Context, Options as BaseOptions } from './ContentDriverBase'; +import StorageDriverBase, { Context, Options as BaseOptions } from './StorageDriverBase'; interface Options extends BaseOptions { dbClientType: DatabaseConfigClient; } -export default class ContentDriverDatabase extends ContentDriverBase { +export default class StorageDatabase extends StorageDriverBase { private handleReturnedRows_: boolean = null; diff --git a/packages/server/src/models/itemModel/ContentDriverFs.test.ts b/packages/server/src/models/itemModel/StorageDriverFs.test.ts similarity index 93% rename from packages/server/src/models/itemModel/ContentDriverFs.test.ts rename to packages/server/src/models/itemModel/StorageDriverFs.test.ts index f5b59a77d0f..d90e278df5f 100644 --- a/packages/server/src/models/itemModel/ContentDriverFs.test.ts +++ b/packages/server/src/models/itemModel/StorageDriverFs.test.ts @@ -1,18 +1,18 @@ import { pathExists, remove } from 'fs-extra'; import { afterAllTests, beforeAllDb, beforeEachDb, expectNotThrow, expectThrow, tempDirPath } from '../../utils/testing/testUtils'; -import ContentDriverFs from './ContentDriverFs'; +import StorageDriverFs from './StorageDriverFs'; import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; let basePath_: string = ''; const newDriver = () => { - return new ContentDriverFs({ basePath: basePath_ }); + return new StorageDriverFs({ basePath: basePath_ }); }; -describe('ContentDriverFs', function() { +describe('StorageDriverFs', function() { beforeAll(async () => { - await beforeAllDb('ContentDriverFs'); + await beforeAllDb('StorageDriverFs'); }); afterAll(async () => { diff --git a/packages/server/src/models/itemModel/ContentDriverFs.ts b/packages/server/src/models/itemModel/StorageDriverFs.ts similarity index 90% rename from packages/server/src/models/itemModel/ContentDriverFs.ts rename to packages/server/src/models/itemModel/StorageDriverFs.ts index f7266edb7a5..f4e13a66b63 100644 --- a/packages/server/src/models/itemModel/ContentDriverFs.ts +++ b/packages/server/src/models/itemModel/StorageDriverFs.ts @@ -1,11 +1,11 @@ import { mkdirp, pathExists, readFile, remove, writeFile } from 'fs-extra'; -import ContentDriverBase, { Options as BaseOptions } from './ContentDriverBase'; +import StorageDriverBase, { Options as BaseOptions } from './StorageDriverBase'; interface Options extends BaseOptions { basePath: string; } -export default class ContentDriverFs extends ContentDriverBase { +export default class StorageDriverFs extends StorageDriverBase { private options_: Options; private pathCreated_: Record = {}; diff --git a/packages/server/src/models/itemModel/ContentDriverMemory.test.ts b/packages/server/src/models/itemModel/StorageDriverMemory.test.ts similarity index 73% rename from packages/server/src/models/itemModel/ContentDriverMemory.test.ts rename to packages/server/src/models/itemModel/StorageDriverMemory.test.ts index ad9e7a14471..606140a6111 100644 --- a/packages/server/src/models/itemModel/ContentDriverMemory.test.ts +++ b/packages/server/src/models/itemModel/StorageDriverMemory.test.ts @@ -1,11 +1,11 @@ import { afterAllTests, beforeAllDb, beforeEachDb } from '../../utils/testing/testUtils'; -import ContentDriverMemory from './ContentDriverMemory'; +import StorageDriverMemory from './StorageDriverMemory'; import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; -describe('ContentDriverMemory', function() { +describe('StorageDriverMemory', function() { beforeAll(async () => { - await beforeAllDb('ContentDriverMemory'); + await beforeAllDb('StorageDriverMemory'); }); afterAll(async () => { @@ -17,22 +17,22 @@ describe('ContentDriverMemory', function() { }); test('should write to content and read it back', async function() { - const driver = new ContentDriverMemory(); + const driver = new StorageDriverMemory(); await shouldWriteToContentAndReadItBack(driver); }); test('should delete the content', async function() { - const driver = new ContentDriverMemory(); + const driver = new StorageDriverMemory(); await shouldDeleteContent(driver); }); test('should not create the item if the content cannot be saved', async function() { - const driver = new ContentDriverMemory(); + const driver = new StorageDriverMemory(); await shouldNotCreateItemIfContentNotSaved(driver); }); test('should not update the item if the content cannot be saved', async function() { - const driver = new ContentDriverMemory(); + const driver = new StorageDriverMemory(); await shouldNotUpdateItemIfContentNotSaved(driver); }); diff --git a/packages/server/src/models/itemModel/ContentDriverMemory.ts b/packages/server/src/models/itemModel/StorageDriverMemory.ts similarity index 82% rename from packages/server/src/models/itemModel/ContentDriverMemory.ts rename to packages/server/src/models/itemModel/StorageDriverMemory.ts index fc40a5be731..89f40559156 100644 --- a/packages/server/src/models/itemModel/ContentDriverMemory.ts +++ b/packages/server/src/models/itemModel/StorageDriverMemory.ts @@ -1,6 +1,6 @@ -import ContentDriverBase from './ContentDriverBase'; +import StorageDriverBase from './StorageDriverBase'; -export default class ContentDriverMemory extends ContentDriverBase { +export default class StorageDriverMemory extends StorageDriverBase { private data_: Record = {}; diff --git a/packages/server/src/models/itemModel/contentDriverFromConfig.ts b/packages/server/src/models/itemModel/contentDriverFromConfig.ts deleted file mode 100644 index 6285dc778a5..00000000000 --- a/packages/server/src/models/itemModel/contentDriverFromConfig.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { clientType, DbConnection } from '../../db'; -import { ContentDriverConfig, ContentDriverType } from '../../utils/types'; -import ContentDriverBase from './ContentDriverBase'; -import ContentDriverDatabase from './ContentDriverDatabase'; -import ContentDriverFs from './ContentDriverFs'; -import ContentDriverMemory from './ContentDriverMemory'; - -export default function(config: ContentDriverConfig, db: DbConnection): ContentDriverBase | null { - if (!config) return null; - - if (config.type === ContentDriverType.Database) { - return new ContentDriverDatabase({ dbClientType: clientType(db) }); - } - - if (config.type === ContentDriverType.Filesystem) { - return new ContentDriverFs({ basePath: config.path }); - } - - if (config.type === ContentDriverType.Memory) { - return new ContentDriverMemory(); - } - - throw new Error(`Invalid config: ${JSON.stringify(config)}`); -} diff --git a/packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts b/packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts index 9b4902e6584..0b8f2f83ec5 100644 --- a/packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts +++ b/packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts @@ -1,41 +1,41 @@ -import { ContentDriverConfig, ContentDriverType } from '../../utils/types'; -import parseContentDriverConnectionString from './parseContentDriverConnectionString'; +import { StorageDriverConfig, StorageDriverType } from '../../utils/types'; +import parseStorageDriverConnectionString from './parseStorageDriverConnectionString'; -describe('parseContentDriverConnectionString', function() { +describe('parseStorageDriverConnectionString', function() { test('should parse a connection string', async function() { - const testCases: Record = { + const testCases: Record = { 'Type=Database': { - type: ContentDriverType.Database, + type: StorageDriverType.Database, }, ' Type = Database ': { - type: ContentDriverType.Database, + type: StorageDriverType.Database, }, 'Type=Filesystem; Path=/path/to/dir': { - type: ContentDriverType.Filesystem, + type: StorageDriverType.Filesystem, path: '/path/to/dir', }, ' Type = Filesystem ; Path = /path/to/dir ': { - type: ContentDriverType.Filesystem, + type: StorageDriverType.Filesystem, path: '/path/to/dir', }, 'Type=Memory;': { - type: ContentDriverType.Memory, + type: StorageDriverType.Memory, }, '': null, }; for (const [connectionString, config] of Object.entries(testCases)) { - const actual = parseContentDriverConnectionString(connectionString); + const actual = parseStorageDriverConnectionString(connectionString); expect(actual).toEqual(config); } }); test('should detect errors', async function() { - expect(() => parseContentDriverConnectionString('Type=')).toThrow(); - expect(() => parseContentDriverConnectionString('Type;')).toThrow(); - expect(() => parseContentDriverConnectionString('Type=DoesntExist')).toThrow(); - expect(() => parseContentDriverConnectionString('Type=Filesystem')).toThrow(); + expect(() => parseStorageDriverConnectionString('Type=')).toThrow(); + expect(() => parseStorageDriverConnectionString('Type;')).toThrow(); + expect(() => parseStorageDriverConnectionString('Type=DoesntExist')).toThrow(); + expect(() => parseStorageDriverConnectionString('Type=Filesystem')).toThrow(); }); }); diff --git a/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts b/packages/server/src/models/itemModel/parseStorageDriverConnectionString.ts similarity index 56% rename from packages/server/src/models/itemModel/parseContentDriverConnectionString.ts rename to packages/server/src/models/itemModel/parseStorageDriverConnectionString.ts index 0854ba76267..cdd3f89535e 100644 --- a/packages/server/src/models/itemModel/parseContentDriverConnectionString.ts +++ b/packages/server/src/models/itemModel/parseStorageDriverConnectionString.ts @@ -1,30 +1,30 @@ // Type={Database,Filesystem,Memory,S3}; Path={/path/to/dir,https://s3bucket} -import { ContentDriverConfig, ContentDriverMode, ContentDriverType } from '../../utils/types'; +import { StorageDriverConfig, StorageDriverMode, StorageDriverType } from '../../utils/types'; -const parseType = (type: string): ContentDriverType => { - if (type === 'Database') return ContentDriverType.Database; - if (type === 'Filesystem') return ContentDriverType.Filesystem; - if (type === 'Memory') return ContentDriverType.Memory; +const parseType = (type: string): StorageDriverType => { + if (type === 'Database') return StorageDriverType.Database; + if (type === 'Filesystem') return StorageDriverType.Filesystem; + if (type === 'Memory') return StorageDriverType.Memory; throw new Error(`Invalid type: "${type}"`); }; -const parseMode = (mode: string): ContentDriverMode => { - if (mode === 'rw') return ContentDriverMode.ReadWrite; - if (mode === 'r') return ContentDriverMode.ReadOnly; +const parseMode = (mode: string): StorageDriverMode => { + if (mode === 'rw') return StorageDriverMode.ReadWrite; + if (mode === 'r') return StorageDriverMode.ReadOnly; throw new Error(`Invalid type: "${mode}"`); }; -const validate = (config: ContentDriverConfig) => { - if (config.type === ContentDriverType.Filesystem && !config.path) throw new Error('Path must be set for filesystem driver'); +const validate = (config: StorageDriverConfig) => { + if (config.type === StorageDriverType.Filesystem && !config.path) throw new Error('Path must be set for filesystem driver'); return config; }; -export default function(connectionString: string): ContentDriverConfig | null { +export default function(connectionString: string): StorageDriverConfig | null { if (!connectionString) return null; - const output: ContentDriverConfig = { - type: ContentDriverType.Database, + const output: StorageDriverConfig = { + type: StorageDriverType.Database, }; const items = connectionString.split(';').map(i => i.trim()); diff --git a/packages/server/src/models/itemModel/storageDriverFromConfig.ts b/packages/server/src/models/itemModel/storageDriverFromConfig.ts new file mode 100644 index 00000000000..ced40730e18 --- /dev/null +++ b/packages/server/src/models/itemModel/storageDriverFromConfig.ts @@ -0,0 +1,24 @@ +import { clientType, DbConnection } from '../../db'; +import { StorageDriverConfig, StorageDriverType } from '../../utils/types'; +import StorageDriverBase from './StorageDriverBase'; +import StorageDatabase from './StorageDriverDatabase'; +import StorageDriverFs from './StorageDriverFs'; +import StorageDriverMemory from './StorageDriverMemory'; + +export default function(config: StorageDriverConfig, db: DbConnection): StorageDriverBase | null { + if (!config) return null; + + if (config.type === StorageDriverType.Database) { + return new StorageDatabase({ dbClientType: clientType(db) }); + } + + if (config.type === StorageDriverType.Filesystem) { + return new StorageDriverFs({ basePath: config.path }); + } + + if (config.type === StorageDriverType.Memory) { + return new StorageDriverMemory(); + } + + throw new Error(`Invalid config: ${JSON.stringify(config)}`); +} diff --git a/packages/server/src/models/itemModel/testUtils.ts b/packages/server/src/models/itemModel/testUtils.ts index d239e68fb83..fac09f80652 100644 --- a/packages/server/src/models/itemModel/testUtils.ts +++ b/packages/server/src/models/itemModel/testUtils.ts @@ -1,13 +1,13 @@ import { Item } from '../../services/database/types'; import { createUserAndSession, makeNoteSerializedBody, models } from '../../utils/testing/testUtils'; -import { ContentDriverMode } from '../../utils/types'; -import ContentDriverBase, { Context } from './ContentDriverBase'; +import { StorageDriverMode } from '../../utils/types'; +import StorageDriverBase, { Context } from './StorageDriverBase'; -const testModels = (driver: ContentDriverBase) => { - return models({ contentDriver: driver }); +const testModels = (driver: StorageDriverBase) => { + return models({ storageDriver: driver }); }; -export async function shouldWriteToContentAndReadItBack(driver: ContentDriverBase) { +export async function shouldWriteToContentAndReadItBack(driver: StorageDriverBase) { const { user } = await createUserAndSession(1); const noteBody = makeNoteSerializedBody({ id: '00000000000000000000000000000001', @@ -33,7 +33,7 @@ export async function shouldWriteToContentAndReadItBack(driver: ContentDriverBas expect(jopItem.title).toBe('testing driver'); } -export async function shouldDeleteContent(driver: ContentDriverBase) { +export async function shouldDeleteContent(driver: StorageDriverBase) { const { user } = await createUserAndSession(1); const noteBody = makeNoteSerializedBody({ id: '00000000000000000000000000000001', @@ -52,7 +52,7 @@ export async function shouldDeleteContent(driver: ContentDriverBase) { expect((await testModels(driver).item().all()).length).toBe(0); } -export async function shouldNotCreateItemIfContentNotSaved(driver: ContentDriverBase) { +export async function shouldNotCreateItemIfContentNotSaved(driver: StorageDriverBase) { const previousWrite = driver.write; driver.write = () => { throw new Error('not working!'); }; @@ -75,7 +75,7 @@ export async function shouldNotCreateItemIfContentNotSaved(driver: ContentDriver } } -export async function shouldNotUpdateItemIfContentNotSaved(driver: ContentDriverBase) { +export async function shouldNotUpdateItemIfContentNotSaved(driver: StorageDriverBase) { const { user } = await createUserAndSession(1); const noteBody = makeNoteSerializedBody({ id: '00000000000000000000000000000001', @@ -122,7 +122,7 @@ export async function shouldNotUpdateItemIfContentNotSaved(driver: ContentDriver } } -export async function shouldSupportFallbackDriver(driver: ContentDriverBase, fallbackDriver: ContentDriverBase) { +export async function shouldSupportFallbackDriver(driver: StorageDriverBase, fallbackDriver: StorageDriverBase) { const { user } = await createUserAndSession(1); const output = await testModels(driver).item().saveFromRawContent(user, [{ @@ -144,8 +144,8 @@ export async function shouldSupportFallbackDriver(driver: ContentDriverBase, fal } const testModelWithFallback = models({ - contentDriver: driver, - fallbackContentDriver: fallbackDriver, + storageDriver: driver, + storageDriverFallback: fallbackDriver, }); // If the item content is not on the main content driver, it should get @@ -175,14 +175,14 @@ export async function shouldSupportFallbackDriver(driver: ContentDriverBase, fal } } -export async function shouldSupportFallbackDriverInReadWriteMode(driver: ContentDriverBase, fallbackDriver: ContentDriverBase) { - if (fallbackDriver.mode !== ContentDriverMode.ReadWrite) throw new Error('Content driver must be configured in RW mode for this test'); +export async function shouldSupportFallbackDriverInReadWriteMode(driver: StorageDriverBase, fallbackDriver: StorageDriverBase) { + if (fallbackDriver.mode !== StorageDriverMode.ReadWrite) throw new Error('Content driver must be configured in RW mode for this test'); const { user } = await createUserAndSession(1); const testModelWithFallback = models({ - contentDriver: driver, - fallbackContentDriver: fallbackDriver, + storageDriver: driver, + storageDriverFallback: fallbackDriver, }); const output = await testModelWithFallback.item().saveFromRawContent(user, [{ diff --git a/packages/server/src/tools/debugTools.ts b/packages/server/src/tools/debugTools.ts index 7e6067a50ab..ae947f6aa8f 100644 --- a/packages/server/src/tools/debugTools.ts +++ b/packages/server/src/tools/debugTools.ts @@ -1,7 +1,7 @@ import time from '@joplin/lib/time'; import { clientType, DbConnection, dropTables, migrateLatest } from '../db'; import newModelFactory from '../models/factory'; -import ContentDriverDatabase from '../models/itemModel/ContentDriverDatabase'; +import StorageDatabase from '../models/itemModel/StorageDriverDatabase'; import { AccountType } from '../models/UserModel'; import { User, UserFlagType } from '../services/database/types'; import { Config } from '../utils/types'; @@ -36,7 +36,7 @@ export async function createTestUsers(db: DbConnection, config: Config, options: const password = 'hunter1hunter2hunter3'; const models = newModelFactory(db, config, { - contentDriver: new ContentDriverDatabase({ dbClientType: clientType(db) }), + storageDriver: new StorageDatabase({ dbClientType: clientType(db) }), }); if (options.count) { diff --git a/packages/server/src/utils/testing/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts index 128159cc930..3125e970f16 100644 --- a/packages/server/src/utils/testing/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -23,7 +23,7 @@ import MustacheService from '../../services/MustacheService'; import uuidgen from '../uuidgen'; import { createCsrfToken } from '../csrf'; import { cookieSet } from '../cookies'; -import ContentDriverMemory from '../../models/itemModel/ContentDriverMemory'; +import StorageDriverMemory from '../../models/itemModel/StorageDriverMemory'; import { parseEnv } from '../../env'; // Takes into account the fact that this file will be inside the /dist directory @@ -195,7 +195,7 @@ export async function koaAppContext(options: AppContextTestOptions = null): Prom const appLogger = Logger.create('AppTest'); - const baseAppContext = await setupAppContext({} as any, Env.Dev, db_, () => appLogger, { contentDriver: new ContentDriverMemory() }); + const baseAppContext = await setupAppContext({} as any, Env.Dev, db_, () => appLogger, { storageDriver: new StorageDriverMemory() }); // Set type to "any" because the Koa context has many properties and we // don't need to mock all of them. @@ -243,12 +243,12 @@ export function db() { return db_; } -const contentDriverMemory = new ContentDriverMemory(); +const storageDriverMemory = new StorageDriverMemory(); export function models(options: ModelFactoryOptions = null) { options = { - contentDriver: contentDriverMemory, - fallbackContentDriver: null, + storageDriver: storageDriverMemory, + storageDriverFallback: null, ...options, }; diff --git a/packages/server/src/utils/types.ts b/packages/server/src/utils/types.ts index 8818c75d3c9..862f4c045db 100644 --- a/packages/server/src/utils/types.ts +++ b/packages/server/src/utils/types.ts @@ -87,7 +87,7 @@ export interface StripeConfig extends StripePublicConfig { webhookSecret: string; } -export enum ContentDriverType { +export enum StorageDriverType { Database = 1, Filesystem = 2, Memory = 3, @@ -113,15 +113,15 @@ export enum ContentDriverType { // something goes wrong it's possible to go back to the old storage until the // new one is working. -export enum ContentDriverMode { +export enum StorageDriverMode { ReadWrite = 1, ReadOnly = 2, } -export interface ContentDriverConfig { - type: ContentDriverType; +export interface StorageDriverConfig { + type: StorageDriverType; path?: string; - mode?: ContentDriverMode; + mode?: StorageDriverMode; } export interface Config { @@ -152,8 +152,8 @@ export interface Config { businessEmail: string; isJoplinCloud: boolean; cookieSecure: boolean; - contentDriver: ContentDriverConfig; - fallbackContentDriver: ContentDriverConfig; + storageDriver: StorageDriverConfig; + storageDriverFallback: StorageDriverConfig; } export enum HttpMethod { From e3d6334372980f993e0ac7d29d9c2e1b80a22ab2 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 6 Nov 2021 19:51:32 +0000 Subject: [PATCH 13/17] id --- packages/server/schema.sqlite | Bin 311296 -> 319488 bytes packages/server/src/app.ts | 12 ++--- packages/server/src/config.ts | 2 +- .../src/migrations/20211105183559_storage.ts | 32 +++++++++++++ .../server/src/models/ChangeModel.test.ts | 10 ++--- packages/server/src/models/ItemModel.ts | 9 +++- packages/server/src/models/StorageModel.ts | 18 ++++++++ packages/server/src/models/factory.ts | 9 +++- .../itemModel/storageDriverFromConfig.ts | 24 ---------- .../storage}/StorageDriverBase.ts | 29 ++++++------ .../storage}/StorageDriverDatabase.test.ts | 14 +++--- .../storage}/StorageDriverDatabase.ts | 14 +++--- .../storage}/StorageDriverFs.test.ts | 4 +- .../storage}/StorageDriverFs.ts | 16 +++---- .../storage}/StorageDriverMemory.test.ts | 10 ++--- .../storage}/StorageDriverMemory.ts | 5 +++ ...arseStorageDriverConnectionString.test.ts} | 2 +- .../parseStorageDriverConnectionString.ts | 2 +- .../items/storage/serializeStorageConfig.ts | 30 +++++++++++++ .../items/storage/storageDriverFromConfig.ts | 42 ++++++++++++++++++ .../{itemModel => items/storage}/testUtils.ts | 6 +-- .../server/src/models/items/storage/utils.ts | 0 .../server/src/services/database/types.ts | 11 +++++ packages/server/src/tools/debugTools.ts | 7 +-- .../server/src/utils/testing/testUtils.ts | 6 +-- packages/server/src/utils/types.ts | 2 +- 26 files changed, 219 insertions(+), 97 deletions(-) create mode 100644 packages/server/src/migrations/20211105183559_storage.ts create mode 100644 packages/server/src/models/StorageModel.ts delete mode 100644 packages/server/src/models/itemModel/storageDriverFromConfig.ts rename packages/server/src/models/{itemModel => items/storage}/StorageDriverBase.ts (62%) rename packages/server/src/models/{itemModel => items/storage}/StorageDriverDatabase.test.ts (84%) rename packages/server/src/models/{itemModel => items/storage}/StorageDriverDatabase.ts (78%) rename packages/server/src/models/{itemModel => items/storage}/StorageDriverFs.test.ts (95%) rename packages/server/src/models/{itemModel => items/storage}/StorageDriverFs.ts (74%) rename packages/server/src/models/{itemModel => items/storage}/StorageDriverMemory.test.ts (83%) rename packages/server/src/models/{itemModel => items/storage}/StorageDriverMemory.ts (75%) rename packages/server/src/models/{itemModel/parseContentDriverConnectionString.test.ts => items/storage/parseStorageDriverConnectionString.test.ts} (94%) rename packages/server/src/models/{itemModel => items/storage}/parseStorageDriverConnectionString.ts (97%) create mode 100644 packages/server/src/models/items/storage/serializeStorageConfig.ts create mode 100644 packages/server/src/models/items/storage/storageDriverFromConfig.ts rename packages/server/src/models/{itemModel => items/storage}/testUtils.ts (97%) create mode 100644 packages/server/src/models/items/storage/utils.ts diff --git a/packages/server/schema.sqlite b/packages/server/schema.sqlite index ab7f2c6a3382ed9073d65a8454e1f7590cf7723a..76012734799ec9953151bab0253a129227e0499e 100644 GIT binary patch delta 1846 zcmc(gUrbw79LMj!Qrb&*J0a-|z3p}UlYm?Q14hFNqr)N{wB0BW(?4)2mzCSDE&moK z)NIM*!Bvm(KKLNuwg-hwi|g&&3qI_@L?tmPTkuCJvP zNV`g5F{p zH$dJ5-^uoM@JDqcd|wAO`Z}3C`X*fB`?uYd}p zH+mVb$8=1BT@|{j?=0= zAySnX-h`gx+1cPE7fpuKT%1qQIuy67K=x2o&UDGRz_G6BiG(T6CH&n(BjyWj*0EXJ zP}|tJ#WgdS=$;9WPICN-SbQWL8;_^bwU%(L*;MP8GLOAB?q<&h9Q~f@({1PaV?NL6 zldgeNeF1Ok*sR4-gZHXR#a1K82Hv+#Y4$m8>;`}Rmnkk#|5>T24nE@Ti6sME+%{%A z?w_?goYJnPOEmD)(^)QcahqcHm0CovTz3AWkhjx2a=|yzYi*qyo$8TeC@{H509chDVU$;hi&)9NylTPC$48N%)bOCC#p z6T5W$|Gq&Y{tI@1D+D)}TSgfX?Zcn6f54z+#6yI7D5|sgkJNKo6h|8Ta|!mBzQ7TVQK?dmcV<>>=+BSf5dw@;}+VT*8!gyPAcBgS*_@j6Q+!@LAj-* z*~vsO980rFligOt)Z6Lpa{9X%zq8}@E+$mOBP2d55H74LwAZhK+-Qj5_%u78Q3&7O zA}(SXLg>j8JeI+QFY`nM%U}X666diDDqIzbVJw3PKa0c}bU|HLujsG@4=M zI-j+{XMA~oMSEu9t6|(xHxlQ0c0}rs@Hdj=_-Kepv$JW2kHaw)i#3u^cVX)mQG+$U zDeM+deFFy-NCnL@JeO-ODs?&?x>P5pmW}e%D{dEvp(2}%vPq%&4q+&RM|5h?Dm*Hn WM;eQ$6f_qxdPMgfB2TPlnZE$97Jn81 delta 1489 zcmZoTAl%R(JVBn(xtSD{7%qx3+a+tIl zn68&r0@CLuFOgOU(q|{11B;)T{9RfT$Ui+dCEgAbXZiUMpt+q?b;9AP2I4$z&dRkh;pr=3u&Fa*{kqUD@O=d62%M$?N4o z>Ix^{mRI8EmCfY^`kIOV5(EEp{!5z$4UX|k2{SQkGiK+dR>bFKrWYlaWaj4;%ZM{F zn{rN`z$dcFphdpkC)+J5z@Q}CIJnZ;(#^{(DKRq5)ilLC+tN8Vu%yBzF(o%M&mkv2 zIWZ?AzqmxjC{e}GK*b};z{I7;nc0_< zkl9=e44at)?(*}ZDBS!u{xgP9{&aqtIn}(lO0Uewz{}X%(J#OxPv5b^-x1_%#+q-u zvZ4?Y?9_(4{0b;e#S&hdCe+MNaB(gzGYYp1^mflR2=_}(GpJ1Q&2}-&PtM7&uyiXf zB4h|5{gdrkC3ujXx2fR)zXBm$L@C-9z(|Bq+Y%UeR!gj_0=KuB+1Gu;IFJSq|KQVx1(E$OVuNN&4 z05gCg0AVOBOJH#TawZ*M(Gq4o&A@+^zlL9u?*Q)^UK^fh?tk1%xV<Q8Re#b+QwqaTngkhyl0f1?!KMHf=RPv`r93hGSdTg zu<%HGx3Uzoi;IghcBYpkCgr4NmZatug9)$g { + const newModelFactoryOptions = async (db: DbConnection): Promise => { return { - storageDriver: storageDriverFromConfig(config().storageDriver, db), - storageDriverFallback: storageDriverFromConfig(config().storageDriverFallback, db), + storageDriver: await storageDriverFromConfig(config().storageDriver, db), + storageDriverFallback: await storageDriverFromConfig(config().storageDriverFallback, db), }; }; @@ -245,7 +245,7 @@ async function main() { }); } else { const connectionCheck = await waitForConnection(config().database); - const models = newModelFactory(connectionCheck.connection, config(), newModelFactoryOptions(connectionCheck.connection)); + const models = newModelFactory(connectionCheck.connection, config(), await newModelFactoryOptions(connectionCheck.connection)); await selectedCommand.run(commandArgv, { db: connectionCheck.connection, @@ -275,7 +275,7 @@ async function main() { appLogger().info('Connection check:', connectionCheckLogInfo); const ctx = app.context as AppContext; - await setupAppContext(ctx, env, connectionCheck.connection, appLogger, newModelFactoryOptions(connectionCheck.connection)); + await setupAppContext(ctx, env, connectionCheck.connection, appLogger, await newModelFactoryOptions(connectionCheck.connection)); await initializeJoplinUtils(config(), ctx.joplinBase.models, ctx.joplinBase.services.mustache); diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts index dcd52844f9f..6ed1e8e389a 100644 --- a/packages/server/src/config.ts +++ b/packages/server/src/config.ts @@ -3,7 +3,7 @@ import { Config, DatabaseConfig, DatabaseConfigClient, Env, MailerConfig, RouteT import * as pathUtils from 'path'; import { loadStripeConfig, StripePublicConfig } from '@joplin/lib/utils/joplinCloud'; import { EnvVariables } from './env'; -import parseStorageDriverConnectionString from './models/itemModel/parseStorageDriverConnectionString'; +import parseStorageDriverConnectionString from './models/items/storage/parseStorageDriverConnectionString'; interface PackageJson { version: string; diff --git a/packages/server/src/migrations/20211105183559_storage.ts b/packages/server/src/migrations/20211105183559_storage.ts new file mode 100644 index 00000000000..9e06ec5e658 --- /dev/null +++ b/packages/server/src/migrations/20211105183559_storage.ts @@ -0,0 +1,32 @@ +import { Knex } from 'knex'; +import { DbConnection } from '../db'; + +export async function up(db: DbConnection): Promise { + await db.schema.createTable('storages', (table: Knex.CreateTableBuilder) => { + table.increments('id').unique().primary().notNullable(); + table.text('connection_string').notNullable(); + }); + + await db('storages').insert({ + connection_string: 'Type=Database', + }); + + // First we create the column and set a default so as to populate the + // storage_id field. + await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => { + table.integer('storage_id').defaultTo(1).notNullable(); + }); + + // Once it's set, we remove the default as that should be explicitly set. + await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => { + table.integer('storage_id').notNullable().alter(); + }); +} + +export async function down(db: DbConnection): Promise { + await db.schema.dropTable('storages'); + + await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => { + table.dropColumn('storage_id'); + }); +} diff --git a/packages/server/src/models/ChangeModel.test.ts b/packages/server/src/models/ChangeModel.test.ts index ceb8fff66d6..b8a6ed8794c 100644 --- a/packages/server/src/models/ChangeModel.test.ts +++ b/packages/server/src/models/ChangeModel.test.ts @@ -38,12 +38,12 @@ describe('ChangeModel', function() { const changeModel = models().change(); await msleep(1); const item1 = await models().item().makeTestItem(user.id, 1); // [1] CREATE 1 - await msleep(1); await itemModel.saveForUser(user.id, { id: item1.id, name: '0000000000000000000000000000001A.md' }); // [2] UPDATE 1a - await msleep(1); await itemModel.saveForUser(user.id, { id: item1.id, name: '0000000000000000000000000000001B.md' }); // [3] UPDATE 1b + await msleep(1); await itemModel.saveForUser(user.id, { id: item1.id, name: '0000000000000000000000000000001A.md', content: Buffer.from('') }); // [2] UPDATE 1a + await msleep(1); await itemModel.saveForUser(user.id, { id: item1.id, name: '0000000000000000000000000000001B.md', content: Buffer.from('') }); // [3] UPDATE 1b await msleep(1); const item2 = await models().item().makeTestItem(user.id, 2); // [4] CREATE 2 - await msleep(1); await itemModel.saveForUser(user.id, { id: item2.id, name: '0000000000000000000000000000002A.md' }); // [5] UPDATE 2a + await msleep(1); await itemModel.saveForUser(user.id, { id: item2.id, name: '0000000000000000000000000000002A.md', content: Buffer.from('') }); // [5] UPDATE 2a await msleep(1); await itemModel.delete(item1.id); // [6] DELETE 1 - await msleep(1); await itemModel.saveForUser(user.id, { id: item2.id, name: '0000000000000000000000000000002B.md' }); // [7] UPDATE 2b + await msleep(1); await itemModel.saveForUser(user.id, { id: item2.id, name: '0000000000000000000000000000002B.md', content: Buffer.from('') }); // [7] UPDATE 2b await msleep(1); const item3 = await models().item().makeTestItem(user.id, 3); // [8] CREATE 3 // Check that the 8 changes were created @@ -120,7 +120,7 @@ describe('ChangeModel', function() { let i = 1; await msleep(1); const item1 = await models().item().makeTestItem(user.id, 1); // CREATE 1 - await msleep(1); await itemModel.saveForUser(user.id, { id: item1.id, name: `test_mod${i++}` }); // UPDATE 1 + await msleep(1); await itemModel.saveForUser(user.id, { id: item1.id, name: `test_mod${i++}`, content: Buffer.from('') }); // UPDATE 1 await expectThrow(async () => changeModel.delta(user.id, { limit: 1, cursor: 'invalid' }), 'resyncRequired'); }); diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index 7da863c5924..d8743bc0f11 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -7,7 +7,7 @@ import { ApiError, ErrorForbidden, ErrorUnprocessableEntity } from '../utils/err import { Knex } from 'knex'; import { ChangePreviousItem } from './ChangeModel'; import { unique } from '../utils/array'; -import StorageDriverBase, { Context } from './itemModel/StorageDriverBase'; +import StorageDriverBase, { Context } from './items/storage/StorageDriverBase'; import { DbConnection } from '../db'; import { Config, StorageDriverMode } from '../utils/types'; import { NewModelFactoryHandler, Options } from './factory'; @@ -417,6 +417,7 @@ export default class ItemModel extends BaseModel { try { const content = itemToSave.content; delete itemToSave.content; + itemToSave.storage_id = this.storageDriver_.storageId; itemToSave.content_size = content ? content.byteLength : 0; @@ -651,6 +652,7 @@ export default class ItemModel extends BaseModel { public async makeTestItem(userId: Uuid, num: number) { return this.saveForUser(userId, { name: `${num.toString().padStart(32, '0')}.md`, + content: Buffer.from(''), }); } @@ -659,6 +661,7 @@ export default class ItemModel extends BaseModel { for (let i = 1; i <= count; i++) { await this.saveForUser(userId, { name: `${i.toString().padStart(32, '0')}.md`, + content: Buffer.from(''), }); } }, 'ItemModel::makeTestItems'); @@ -675,6 +678,10 @@ export default class ItemModel extends BaseModel { let previousItem: ChangePreviousItem = null; + if (item.content && !item.storage_id) { + item.storage_id = this.storageDriver_.storageId; + } + if (isNew) { if (!item.mime_type) item.mime_type = mimeUtils.fromFilename(item.name) || ''; if (!item.owner_id) item.owner_id = userId; diff --git a/packages/server/src/models/StorageModel.ts b/packages/server/src/models/StorageModel.ts new file mode 100644 index 00000000000..ae0ac1f853d --- /dev/null +++ b/packages/server/src/models/StorageModel.ts @@ -0,0 +1,18 @@ +import { Storage } from '../services/database/types'; +import BaseModel from './BaseModel'; + +export default class StorageModel extends BaseModel { + + public get tableName(): string { + return 'storages'; + } + + protected hasUuid(): boolean { + return false; + } + + public async byConnectionString(connectionString: string): Promise { + return this.db(this.tableName).where('connection_string', connectionString).first(); + } + +} diff --git a/packages/server/src/models/factory.ts b/packages/server/src/models/factory.ts index b1c4b443a93..581394400ce 100644 --- a/packages/server/src/models/factory.ts +++ b/packages/server/src/models/factory.ts @@ -72,8 +72,9 @@ import SubscriptionModel from './SubscriptionModel'; import UserFlagModel from './UserFlagModel'; import EventModel from './EventModel'; import { Config } from '../utils/types'; -import StorageDriverBase from './itemModel/StorageDriverBase'; +import StorageDriverBase from './items/storage/StorageDriverBase'; import LockModel from './LockModel'; +import StorageModel from './StorageModel'; export interface Options { storageDriver: StorageDriverBase; @@ -93,7 +94,7 @@ export class Models { this.config_ = config; this.options_ = options; - if (!options.storageDriver) throw new Error('StorageDriver is required'); + // if (!options.storageDriver) throw new Error('StorageDriver is required'); this.newModelFactory = this.newModelFactory.bind(this); } @@ -170,6 +171,10 @@ export class Models { return new LockModel(this.db_, this.newModelFactory, this.config_); } + public storage() { + return new StorageModel(this.db_, this.newModelFactory, this.config_); + } + } export default function newModelFactory(db: DbConnection, config: Config, options: Options): Models { diff --git a/packages/server/src/models/itemModel/storageDriverFromConfig.ts b/packages/server/src/models/itemModel/storageDriverFromConfig.ts deleted file mode 100644 index ced40730e18..00000000000 --- a/packages/server/src/models/itemModel/storageDriverFromConfig.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { clientType, DbConnection } from '../../db'; -import { StorageDriverConfig, StorageDriverType } from '../../utils/types'; -import StorageDriverBase from './StorageDriverBase'; -import StorageDatabase from './StorageDriverDatabase'; -import StorageDriverFs from './StorageDriverFs'; -import StorageDriverMemory from './StorageDriverMemory'; - -export default function(config: StorageDriverConfig, db: DbConnection): StorageDriverBase | null { - if (!config) return null; - - if (config.type === StorageDriverType.Database) { - return new StorageDatabase({ dbClientType: clientType(db) }); - } - - if (config.type === StorageDriverType.Filesystem) { - return new StorageDriverFs({ basePath: config.path }); - } - - if (config.type === StorageDriverType.Memory) { - return new StorageDriverMemory(); - } - - throw new Error(`Invalid config: ${JSON.stringify(config)}`); -} diff --git a/packages/server/src/models/itemModel/StorageDriverBase.ts b/packages/server/src/models/items/storage/StorageDriverBase.ts similarity index 62% rename from packages/server/src/models/itemModel/StorageDriverBase.ts rename to packages/server/src/models/items/storage/StorageDriverBase.ts index e0c3adf2946..4dfb06ebd5f 100644 --- a/packages/server/src/models/itemModel/StorageDriverBase.ts +++ b/packages/server/src/models/items/storage/StorageDriverBase.ts @@ -1,5 +1,5 @@ -import { StorageDriverMode } from '../../utils/types'; -import { Models } from '../factory'; +import { StorageDriverConfig, StorageDriverMode } from '../../../utils/types'; +import { Models } from '../../factory'; // ItemModel passes the models object when calling any of the driver handler. // This is so that if there's an active transaction, the driver can use that (as @@ -9,25 +9,26 @@ export interface Context { models: Models; } -export interface Options { - mode?: StorageDriverMode; -} - export default class StorageDriverBase { - private mode_: StorageDriverMode = StorageDriverMode.ReadOnly; + private storageId_: number; + private config_: StorageDriverConfig; - public constructor(options: Options = null) { - options = { - mode: StorageDriverMode.ReadOnly, - ...options, - }; + public constructor(storageId: number, config: StorageDriverConfig) { + this.storageId_ = storageId; + this.config_ = config; + } + + public get storageId(): number { + return this.storageId_; + } - this.mode_ = options.mode; + public get config(): StorageDriverConfig { + return this.config_; } public get mode(): StorageDriverMode { - return this.mode_; + return this.config.mode || StorageDriverMode.ReadOnly; } public async write(_itemId: string, _content: Buffer, _context: Context): Promise { throw new Error('Not implemented'); } diff --git a/packages/server/src/models/itemModel/StorageDriverDatabase.test.ts b/packages/server/src/models/items/storage/StorageDriverDatabase.test.ts similarity index 84% rename from packages/server/src/models/itemModel/StorageDriverDatabase.test.ts rename to packages/server/src/models/items/storage/StorageDriverDatabase.test.ts index 234836781da..6cbcc84b26c 100644 --- a/packages/server/src/models/itemModel/StorageDriverDatabase.test.ts +++ b/packages/server/src/models/items/storage/StorageDriverDatabase.test.ts @@ -1,12 +1,12 @@ -import { clientType } from '../../db'; -import { afterAllTests, beforeAllDb, beforeEachDb, db, expectNotThrow, expectThrow, models } from '../../utils/testing/testUtils'; -import { StorageDriverMode } from '../../utils/types'; -import StorageDatabase from './StorageDriverDatabase'; +import { clientType } from '../../../db'; +import { afterAllTests, beforeAllDb, beforeEachDb, db, expectNotThrow, expectThrow, models } from '../../../utils/testing/testUtils'; +import { StorageDriverMode } from '../../../utils/types'; +import StorageDriverDatabase from './StorageDriverDatabase'; import StorageDriverMemory from './StorageDriverMemory'; import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldSupportFallbackDriver, shouldSupportFallbackDriverInReadWriteMode, shouldWriteToContentAndReadItBack } from './testUtils'; const newDriver = () => { - return new StorageDatabase({ + return new StorageDriverDatabase(1, { dbClientType: clientType(db()), }); }; @@ -56,11 +56,11 @@ describe('StorageDriverDatabase', function() { }); test('should support fallback content drivers', async function() { - await shouldSupportFallbackDriver(newDriver(), new StorageDriverMemory()); + await shouldSupportFallbackDriver(newDriver(), new StorageDriverMemory(2)); }); test('should support fallback content drivers in rw mode', async function() { - await shouldSupportFallbackDriverInReadWriteMode(newDriver(), new StorageDriverMemory({ mode: StorageDriverMode.ReadWrite })); + await shouldSupportFallbackDriverInReadWriteMode(newDriver(), new StorageDriverMemory(2, { mode: StorageDriverMode.ReadWrite })); }); }); diff --git a/packages/server/src/models/itemModel/StorageDriverDatabase.ts b/packages/server/src/models/items/storage/StorageDriverDatabase.ts similarity index 78% rename from packages/server/src/models/itemModel/StorageDriverDatabase.ts rename to packages/server/src/models/items/storage/StorageDriverDatabase.ts index ff4585a95ad..627ebc2151b 100644 --- a/packages/server/src/models/itemModel/StorageDriverDatabase.ts +++ b/packages/server/src/models/items/storage/StorageDriverDatabase.ts @@ -2,21 +2,21 @@ // database (as a binary blob). For now the driver expects that the content is // stored in the same table as the items, as it originally was. -import { DatabaseConfigClient } from '../../utils/types'; -import StorageDriverBase, { Context, Options as BaseOptions } from './StorageDriverBase'; +import { DatabaseConfigClient, StorageDriverConfig, StorageDriverType } from '../../../utils/types'; +import StorageDriverBase, { Context } from './StorageDriverBase'; -interface Options extends BaseOptions { +interface StorageDriverDatabaseConfig extends StorageDriverConfig { dbClientType: DatabaseConfigClient; } -export default class StorageDatabase extends StorageDriverBase { +export default class StorageDriverDatabase extends StorageDriverBase { private handleReturnedRows_: boolean = null; - public constructor(options: Options) { - super(options); + public constructor(id: number, config: StorageDriverDatabaseConfig) { + super(id, { type: StorageDriverType.Database, ...config }); - this.handleReturnedRows_ = options.dbClientType === DatabaseConfigClient.PostgreSQL; + this.handleReturnedRows_ = config.dbClientType === DatabaseConfigClient.PostgreSQL; } public async write(itemId: string, content: Buffer, context: Context): Promise { diff --git a/packages/server/src/models/itemModel/StorageDriverFs.test.ts b/packages/server/src/models/items/storage/StorageDriverFs.test.ts similarity index 95% rename from packages/server/src/models/itemModel/StorageDriverFs.test.ts rename to packages/server/src/models/items/storage/StorageDriverFs.test.ts index d90e278df5f..74ecac39e22 100644 --- a/packages/server/src/models/itemModel/StorageDriverFs.test.ts +++ b/packages/server/src/models/items/storage/StorageDriverFs.test.ts @@ -1,12 +1,12 @@ import { pathExists, remove } from 'fs-extra'; -import { afterAllTests, beforeAllDb, beforeEachDb, expectNotThrow, expectThrow, tempDirPath } from '../../utils/testing/testUtils'; +import { afterAllTests, beforeAllDb, beforeEachDb, expectNotThrow, expectThrow, tempDirPath } from '../../../utils/testing/testUtils'; import StorageDriverFs from './StorageDriverFs'; import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; let basePath_: string = ''; const newDriver = () => { - return new StorageDriverFs({ basePath: basePath_ }); + return new StorageDriverFs(1, { path: basePath_ }); }; describe('StorageDriverFs', function() { diff --git a/packages/server/src/models/itemModel/StorageDriverFs.ts b/packages/server/src/models/items/storage/StorageDriverFs.ts similarity index 74% rename from packages/server/src/models/itemModel/StorageDriverFs.ts rename to packages/server/src/models/items/storage/StorageDriverFs.ts index f4e13a66b63..762ca7cb38d 100644 --- a/packages/server/src/models/itemModel/StorageDriverFs.ts +++ b/packages/server/src/models/items/storage/StorageDriverFs.ts @@ -1,19 +1,13 @@ import { mkdirp, pathExists, readFile, remove, writeFile } from 'fs-extra'; -import StorageDriverBase, { Options as BaseOptions } from './StorageDriverBase'; - -interface Options extends BaseOptions { - basePath: string; -} +import { StorageDriverConfig, StorageDriverType } from '../../../utils/types'; +import StorageDriverBase from './StorageDriverBase'; export default class StorageDriverFs extends StorageDriverBase { - private options_: Options; private pathCreated_: Record = {}; - public constructor(options: Options) { - super(options); - - this.options_ = options; + public constructor(id: number, config: StorageDriverConfig) { + super(id, { type: StorageDriverType.Filesystem, ...config }); } private async createParentDirectories(path: string) { @@ -27,7 +21,7 @@ export default class StorageDriverFs extends StorageDriverBase { } private itemPath(itemId: string): string { - return `${this.options_.basePath}/${itemId.substr(0, 2).toLowerCase()}/${itemId.substr(2, 2).toLowerCase()}/${itemId}`; + return `${this.config.path}/${itemId.substr(0, 2).toLowerCase()}/${itemId.substr(2, 2).toLowerCase()}/${itemId}`; } public async write(itemId: string, content: Buffer): Promise { diff --git a/packages/server/src/models/itemModel/StorageDriverMemory.test.ts b/packages/server/src/models/items/storage/StorageDriverMemory.test.ts similarity index 83% rename from packages/server/src/models/itemModel/StorageDriverMemory.test.ts rename to packages/server/src/models/items/storage/StorageDriverMemory.test.ts index 606140a6111..d382f76b933 100644 --- a/packages/server/src/models/itemModel/StorageDriverMemory.test.ts +++ b/packages/server/src/models/items/storage/StorageDriverMemory.test.ts @@ -1,4 +1,4 @@ -import { afterAllTests, beforeAllDb, beforeEachDb } from '../../utils/testing/testUtils'; +import { afterAllTests, beforeAllDb, beforeEachDb } from '../../../utils/testing/testUtils'; import StorageDriverMemory from './StorageDriverMemory'; import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; @@ -17,22 +17,22 @@ describe('StorageDriverMemory', function() { }); test('should write to content and read it back', async function() { - const driver = new StorageDriverMemory(); + const driver = new StorageDriverMemory(1); await shouldWriteToContentAndReadItBack(driver); }); test('should delete the content', async function() { - const driver = new StorageDriverMemory(); + const driver = new StorageDriverMemory(1); await shouldDeleteContent(driver); }); test('should not create the item if the content cannot be saved', async function() { - const driver = new StorageDriverMemory(); + const driver = new StorageDriverMemory(1); await shouldNotCreateItemIfContentNotSaved(driver); }); test('should not update the item if the content cannot be saved', async function() { - const driver = new StorageDriverMemory(); + const driver = new StorageDriverMemory(1); await shouldNotUpdateItemIfContentNotSaved(driver); }); diff --git a/packages/server/src/models/itemModel/StorageDriverMemory.ts b/packages/server/src/models/items/storage/StorageDriverMemory.ts similarity index 75% rename from packages/server/src/models/itemModel/StorageDriverMemory.ts rename to packages/server/src/models/items/storage/StorageDriverMemory.ts index 89f40559156..160bce536db 100644 --- a/packages/server/src/models/itemModel/StorageDriverMemory.ts +++ b/packages/server/src/models/items/storage/StorageDriverMemory.ts @@ -1,9 +1,14 @@ +import { StorageDriverConfig, StorageDriverType } from '../../../utils/types'; import StorageDriverBase from './StorageDriverBase'; export default class StorageDriverMemory extends StorageDriverBase { private data_: Record = {}; + public constructor(id: number, config: StorageDriverConfig = null) { + super(id, { type: StorageDriverType.Memory, ...config }); + } + public async write(itemId: string, content: Buffer): Promise { this.data_[itemId] = content; } diff --git a/packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.test.ts similarity index 94% rename from packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts rename to packages/server/src/models/items/storage/parseStorageDriverConnectionString.test.ts index 0b8f2f83ec5..f12f24676f6 100644 --- a/packages/server/src/models/itemModel/parseContentDriverConnectionString.test.ts +++ b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.test.ts @@ -1,4 +1,4 @@ -import { StorageDriverConfig, StorageDriverType } from '../../utils/types'; +import { StorageDriverConfig, StorageDriverType } from '../../../utils/types'; import parseStorageDriverConnectionString from './parseStorageDriverConnectionString'; describe('parseStorageDriverConnectionString', function() { diff --git a/packages/server/src/models/itemModel/parseStorageDriverConnectionString.ts b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts similarity index 97% rename from packages/server/src/models/itemModel/parseStorageDriverConnectionString.ts rename to packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts index cdd3f89535e..86efd7ece5f 100644 --- a/packages/server/src/models/itemModel/parseStorageDriverConnectionString.ts +++ b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts @@ -1,6 +1,6 @@ // Type={Database,Filesystem,Memory,S3}; Path={/path/to/dir,https://s3bucket} -import { StorageDriverConfig, StorageDriverMode, StorageDriverType } from '../../utils/types'; +import { StorageDriverConfig, StorageDriverMode, StorageDriverType } from '../../../utils/types'; const parseType = (type: string): StorageDriverType => { if (type === 'Database') return StorageDriverType.Database; diff --git a/packages/server/src/models/items/storage/serializeStorageConfig.ts b/packages/server/src/models/items/storage/serializeStorageConfig.ts new file mode 100644 index 00000000000..a3ba55b3176 --- /dev/null +++ b/packages/server/src/models/items/storage/serializeStorageConfig.ts @@ -0,0 +1,30 @@ +import { StorageDriverConfig, StorageDriverMode, StorageDriverType } from '../../../utils/types'; + +const serializeType = (type: StorageDriverType): string => { + if (type === StorageDriverType.Database) return 'Database'; + if (type === StorageDriverType.Filesystem) return 'Filesystem'; + if (type === StorageDriverType.Memory) return 'Memory'; + throw new Error(`Invalid type: "${type}"`); +}; + +const serializeMode = (mode: StorageDriverMode): string => { + if (mode === StorageDriverMode.ReadWrite) return 'rw'; + if (mode === StorageDriverMode.ReadOnly) return 'r'; + throw new Error(`Invalid type: "${mode}"`); +}; + +export default function(config: StorageDriverConfig, locationOnly: boolean = true): string { + if (!config) return ''; + + const items: string[] = []; + + items.push(`Type=${serializeType(config.type)}`); + + if (config.path) items.push(`Path=${config.path}`); + + if (!locationOnly && config.mode) items.push(`Mode=${serializeMode(config.mode)}`); + + items.sort(); + + return items.join('; '); +} diff --git a/packages/server/src/models/items/storage/storageDriverFromConfig.ts b/packages/server/src/models/items/storage/storageDriverFromConfig.ts new file mode 100644 index 00000000000..5fb4a00f61a --- /dev/null +++ b/packages/server/src/models/items/storage/storageDriverFromConfig.ts @@ -0,0 +1,42 @@ +import globalConfig from '../../../config'; +import { clientType, DbConnection } from '../../../db'; +import { StorageDriverConfig, StorageDriverType } from '../../../utils/types'; +import newModelFactory from '../../factory'; +import serializeStorageConfig from './serializeStorageConfig'; +import StorageDriverBase from './StorageDriverBase'; +import StorageDriverDatabase from './StorageDriverDatabase'; +import StorageDriverFs from './StorageDriverFs'; +import StorageDriverMemory from './StorageDriverMemory'; + +export default async function(config: StorageDriverConfig, db: DbConnection): Promise { + if (!config) return null; + + const models = newModelFactory(db, globalConfig(), { storageDriver: null }); + + const connectionString = serializeStorageConfig(config); + const existingStorage = await models.storage().byConnectionString(connectionString); + let storageId: number = null; + + if (existingStorage) { + storageId = existingStorage.id; + } else { + const storage = await models.storage().save({ + connection_string: connectionString, + }); + storageId = storage.id; + } + + if (config.type === StorageDriverType.Database) { + return new StorageDriverDatabase(storageId, { ...config, dbClientType: clientType(db) }); + } + + if (config.type === StorageDriverType.Filesystem) { + return new StorageDriverFs(storageId, config); + } + + if (config.type === StorageDriverType.Memory) { + return new StorageDriverMemory(storageId, config); + } + + throw new Error(`Invalid config: ${JSON.stringify(config)}`); +} diff --git a/packages/server/src/models/itemModel/testUtils.ts b/packages/server/src/models/items/storage/testUtils.ts similarity index 97% rename from packages/server/src/models/itemModel/testUtils.ts rename to packages/server/src/models/items/storage/testUtils.ts index fac09f80652..4c854fdb611 100644 --- a/packages/server/src/models/itemModel/testUtils.ts +++ b/packages/server/src/models/items/storage/testUtils.ts @@ -1,6 +1,6 @@ -import { Item } from '../../services/database/types'; -import { createUserAndSession, makeNoteSerializedBody, models } from '../../utils/testing/testUtils'; -import { StorageDriverMode } from '../../utils/types'; +import { Item } from '../../../services/database/types'; +import { createUserAndSession, makeNoteSerializedBody, models } from '../../../utils/testing/testUtils'; +import { StorageDriverMode } from '../../../utils/types'; import StorageDriverBase, { Context } from './StorageDriverBase'; const testModels = (driver: StorageDriverBase) => { diff --git a/packages/server/src/models/items/storage/utils.ts b/packages/server/src/models/items/storage/utils.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/server/src/services/database/types.ts b/packages/server/src/services/database/types.ts index 9c56a006e33..5a98f5078a1 100644 --- a/packages/server/src/services/database/types.ts +++ b/packages/server/src/services/database/types.ts @@ -246,6 +246,11 @@ export interface Event extends WithUuid { created_time?: number; } +export interface Storage { + id?: number; + connection_string?: string; +} + export interface Item extends WithDates, WithUuid { name?: string; mime_type?: string; @@ -258,6 +263,7 @@ export interface Item extends WithDates, WithUuid { jop_encryption_applied?: number; jop_updated_time?: number; owner_id?: Uuid; + storage_id?: number; } export const databaseSchema: DatabaseTables = { @@ -418,6 +424,10 @@ export const databaseSchema: DatabaseTables = { name: { type: 'string' }, created_time: { type: 'string' }, }, + storages: { + id: { type: 'number' }, + connection_string: { type: 'string' }, + }, items: { id: { type: 'string' }, name: { type: 'string' }, @@ -433,6 +443,7 @@ export const databaseSchema: DatabaseTables = { jop_encryption_applied: { type: 'number' }, jop_updated_time: { type: 'string' }, owner_id: { type: 'string' }, + storage_id: { type: 'number' }, }, }; // AUTO-GENERATED-TYPES diff --git a/packages/server/src/tools/debugTools.ts b/packages/server/src/tools/debugTools.ts index ae947f6aa8f..bfc8f71ec5a 100644 --- a/packages/server/src/tools/debugTools.ts +++ b/packages/server/src/tools/debugTools.ts @@ -1,7 +1,7 @@ import time from '@joplin/lib/time'; -import { clientType, DbConnection, dropTables, migrateLatest } from '../db'; +import { DbConnection, dropTables, migrateLatest } from '../db'; import newModelFactory from '../models/factory'; -import StorageDatabase from '../models/itemModel/StorageDriverDatabase'; +import storageDriverFromConfig from '../models/items/storage/storageDriverFromConfig'; import { AccountType } from '../models/UserModel'; import { User, UserFlagType } from '../services/database/types'; import { Config } from '../utils/types'; @@ -36,7 +36,8 @@ export async function createTestUsers(db: DbConnection, config: Config, options: const password = 'hunter1hunter2hunter3'; const models = newModelFactory(db, config, { - storageDriver: new StorageDatabase({ dbClientType: clientType(db) }), + // storageDriver: new StorageDriverDatabase(1, { dbClientType: clientType(db) }), + storageDriver: await storageDriverFromConfig(config.storageDriver, db), // new StorageDriverDatabase(1, { dbClientType: clientType(db) }), }); if (options.count) { diff --git a/packages/server/src/utils/testing/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts index 3125e970f16..c6f936c8770 100644 --- a/packages/server/src/utils/testing/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -23,7 +23,7 @@ import MustacheService from '../../services/MustacheService'; import uuidgen from '../uuidgen'; import { createCsrfToken } from '../csrf'; import { cookieSet } from '../cookies'; -import StorageDriverMemory from '../../models/itemModel/StorageDriverMemory'; +import StorageDriverMemory from '../../models/items/storage/StorageDriverMemory'; import { parseEnv } from '../../env'; // Takes into account the fact that this file will be inside the /dist directory @@ -195,7 +195,7 @@ export async function koaAppContext(options: AppContextTestOptions = null): Prom const appLogger = Logger.create('AppTest'); - const baseAppContext = await setupAppContext({} as any, Env.Dev, db_, () => appLogger, { storageDriver: new StorageDriverMemory() }); + const baseAppContext = await setupAppContext({} as any, Env.Dev, db_, () => appLogger, { storageDriver: new StorageDriverMemory(1) }); // Set type to "any" because the Koa context has many properties and we // don't need to mock all of them. @@ -243,7 +243,7 @@ export function db() { return db_; } -const storageDriverMemory = new StorageDriverMemory(); +const storageDriverMemory = new StorageDriverMemory(1); export function models(options: ModelFactoryOptions = null) { options = { diff --git a/packages/server/src/utils/types.ts b/packages/server/src/utils/types.ts index 862f4c045db..47c05ba8f10 100644 --- a/packages/server/src/utils/types.ts +++ b/packages/server/src/utils/types.ts @@ -119,7 +119,7 @@ export enum StorageDriverMode { } export interface StorageDriverConfig { - type: StorageDriverType; + type?: StorageDriverType; path?: string; mode?: StorageDriverMode; } From 69b413ce2b10c9f58376f5849ade4c35d619bec7 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sun, 7 Nov 2021 11:46:25 +0000 Subject: [PATCH 14/17] storage table --- packages/server/schema.sqlite | Bin 319488 -> 319488 bytes packages/server/src/app.ts | 4 +- .../src/migrations/20211105183559_storage.ts | 8 ++-- packages/server/src/models/ItemModel.ts | 6 +-- .../storage/StorageDriverDatabase.test.ts | 6 ++- .../items/storage/storageDriverFromConfig.ts | 36 +++++++++++------ .../src/models/items/storage/testUtils.ts | 38 ++++++++++++++++++ .../server/src/services/database/types.ts | 4 +- 8 files changed, 78 insertions(+), 24 deletions(-) diff --git a/packages/server/schema.sqlite b/packages/server/schema.sqlite index 76012734799ec9953151bab0253a129227e0499e..79fb1e7e2aa6c397bd0a41014b65e95dded5517d 100644 GIT binary patch delta 1431 zcmZoTAlz_3c!R7o%P-mDt&!lSzjD?ezNUMPu3nrg~NX?u4 zU0MSqGIz4Fj0%L|Eu#fu%$Zy%V-8}>p1eiI6vUV{`K^pLh%s%ljI1$;F?DjVEZD9| zlRIQ}Kq9S^cgTX(G*5mm3%0arGLIbC8I6-o<(p zrN5c6fmujMSb(R2lTW!pd1R`ye}zv{wuM(nM0lDqvo9whv$+@;HZup@<>y6FxcP1T zXAGhI>HIWvYK4=dQF3yGPfBWHRc48Au4zQ6Bgof`HNRwwen3dDQycE`E1)UON58 zc}6)dL?D74KuFWJ2aFHo2^zgdBmZqourg?7dO=Ix9DEOH6Vp&VMgD!u delta 1416 zcmZoTAlz_3c!R7o%eRFQ2PP{@8-f@+Cx=O^LKyYZiXg^@$xEcwK#cX1&q1WtPW~>f z0TNj|Sy@H}!tj>S0x{N1u9PtcF;-9BB4Y|-teX5*MjOOfHd#j27{pjQIan5K*W$?? zvN|A4eEVa$sjnm~1Mi4pP%UIYACA)i=3S4s2=H6pwT4^|vK*&M=%oSY;NRvbRLOCGE}c=CFAu;QS}w;>|Fn-p5~>oZf`%CZU! z@-quUJfe~ft1OMvDomm*(=v?Q$|DOr$`ey^GxHpB@{<#DGV+T{RE!c;3=LGwN)6LI zGrUs6BFqB)%KR;}19O7?{9W9_-2) z2<1=br? z0wHZgDcTmmNQ6<_5*T;nD;PK%nVANs8D~0~TO@j=Mp~E$CAmg~x}_L;7Zw<#yG_4w zo>7hq5sF|35Yn{m0pkOCf<`ZzV6bR`!lnh>0^7M0n6AbXjF@c)m}r>XiA+P=nLjWQ zk%)kv+q8gDp`9^+c{^hOi(CS8EQir{r41~n87C(Q2!wNRvo?tv8#8t}mLw+Sq{e6G zrB=k3q~;dHC+3u-78#hDU=i|l4svx2aa9O$bnh|~@EZI!cFYRK9mjF4#5Q{b2J$JKgR{;Rm4+(ex diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index a041ad9784f..5e1bac201c1 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -224,8 +224,8 @@ async function main() { const newModelFactoryOptions = async (db: DbConnection): Promise => { return { - storageDriver: await storageDriverFromConfig(config().storageDriver, db), - storageDriverFallback: await storageDriverFromConfig(config().storageDriverFallback, db), + storageDriver: await storageDriverFromConfig(config().storageDriver, db, { assignDriverId: env !== 'buildTypes' }), + storageDriverFallback: await storageDriverFromConfig(config().storageDriverFallback, db, { assignDriverId: env !== 'buildTypes' }), }; }; diff --git a/packages/server/src/migrations/20211105183559_storage.ts b/packages/server/src/migrations/20211105183559_storage.ts index 9e06ec5e658..94f6a05fa1d 100644 --- a/packages/server/src/migrations/20211105183559_storage.ts +++ b/packages/server/src/migrations/20211105183559_storage.ts @@ -12,14 +12,14 @@ export async function up(db: DbConnection): Promise { }); // First we create the column and set a default so as to populate the - // storage_id field. + // content_storage_id field. await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => { - table.integer('storage_id').defaultTo(1).notNullable(); + table.integer('content_storage_id').defaultTo(1).notNullable(); }); // Once it's set, we remove the default as that should be explicitly set. await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => { - table.integer('storage_id').notNullable().alter(); + table.integer('content_storage_id').notNullable().alter(); }); } @@ -27,6 +27,6 @@ export async function down(db: DbConnection): Promise { await db.schema.dropTable('storages'); await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => { - table.dropColumn('storage_id'); + table.dropColumn('content_storage_id'); }); } diff --git a/packages/server/src/models/ItemModel.ts b/packages/server/src/models/ItemModel.ts index d8743bc0f11..d532532cb35 100644 --- a/packages/server/src/models/ItemModel.ts +++ b/packages/server/src/models/ItemModel.ts @@ -417,7 +417,7 @@ export default class ItemModel extends BaseModel { try { const content = itemToSave.content; delete itemToSave.content; - itemToSave.storage_id = this.storageDriver_.storageId; + itemToSave.content_storage_id = this.storageDriver_.storageId; itemToSave.content_size = content ? content.byteLength : 0; @@ -678,8 +678,8 @@ export default class ItemModel extends BaseModel { let previousItem: ChangePreviousItem = null; - if (item.content && !item.storage_id) { - item.storage_id = this.storageDriver_.storageId; + if (item.content && !item.content_storage_id) { + item.content_storage_id = this.storageDriver_.storageId; } if (isNew) { diff --git a/packages/server/src/models/items/storage/StorageDriverDatabase.test.ts b/packages/server/src/models/items/storage/StorageDriverDatabase.test.ts index 6cbcc84b26c..026d9fb7810 100644 --- a/packages/server/src/models/items/storage/StorageDriverDatabase.test.ts +++ b/packages/server/src/models/items/storage/StorageDriverDatabase.test.ts @@ -3,7 +3,7 @@ import { afterAllTests, beforeAllDb, beforeEachDb, db, expectNotThrow, expectThr import { StorageDriverMode } from '../../../utils/types'; import StorageDriverDatabase from './StorageDriverDatabase'; import StorageDriverMemory from './StorageDriverMemory'; -import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldSupportFallbackDriver, shouldSupportFallbackDriverInReadWriteMode, shouldWriteToContentAndReadItBack } from './testUtils'; +import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldSupportFallbackDriver, shouldSupportFallbackDriverInReadWriteMode, shouldUpdateContentStorageIdAfterSwitchingDriver, shouldWriteToContentAndReadItBack } from './testUtils'; const newDriver = () => { return new StorageDriverDatabase(1, { @@ -63,4 +63,8 @@ describe('StorageDriverDatabase', function() { await shouldSupportFallbackDriverInReadWriteMode(newDriver(), new StorageDriverMemory(2, { mode: StorageDriverMode.ReadWrite })); }); + test('should update content storage ID after switching driver', async function() { + await shouldUpdateContentStorageIdAfterSwitchingDriver(newDriver(), new StorageDriverMemory(2)); + }); + }); diff --git a/packages/server/src/models/items/storage/storageDriverFromConfig.ts b/packages/server/src/models/items/storage/storageDriverFromConfig.ts index 5fb4a00f61a..c6db08f688b 100644 --- a/packages/server/src/models/items/storage/storageDriverFromConfig.ts +++ b/packages/server/src/models/items/storage/storageDriverFromConfig.ts @@ -8,22 +8,34 @@ import StorageDriverDatabase from './StorageDriverDatabase'; import StorageDriverFs from './StorageDriverFs'; import StorageDriverMemory from './StorageDriverMemory'; -export default async function(config: StorageDriverConfig, db: DbConnection): Promise { +export interface Options { + assignDriverId?: boolean; +} + +export default async function(config: StorageDriverConfig, db: DbConnection, options: Options = null): Promise { if (!config) return null; - const models = newModelFactory(db, globalConfig(), { storageDriver: null }); + options = { + assignDriverId: true, + ...options, + }; + + let storageId: number = 0; + + if (options.assignDriverId) { + const models = newModelFactory(db, globalConfig(), { storageDriver: null }); - const connectionString = serializeStorageConfig(config); - const existingStorage = await models.storage().byConnectionString(connectionString); - let storageId: number = null; + const connectionString = serializeStorageConfig(config); + const existingStorage = await models.storage().byConnectionString(connectionString); - if (existingStorage) { - storageId = existingStorage.id; - } else { - const storage = await models.storage().save({ - connection_string: connectionString, - }); - storageId = storage.id; + if (existingStorage) { + storageId = existingStorage.id; + } else { + const storage = await models.storage().save({ + connection_string: connectionString, + }); + storageId = storage.id; + } } if (config.type === StorageDriverType.Database) { diff --git a/packages/server/src/models/items/storage/testUtils.ts b/packages/server/src/models/items/storage/testUtils.ts index 4c854fdb611..8f72f589833 100644 --- a/packages/server/src/models/items/storage/testUtils.ts +++ b/packages/server/src/models/items/storage/testUtils.ts @@ -24,6 +24,7 @@ export async function shouldWriteToContentAndReadItBack(driver: StorageDriverBas const item = await testModels(driver).item().loadWithContent(result.item.id); expect(item.content.byteLength).toBe(item.content_size); + expect(item.content_storage_id).toBe(driver.storageId); const rawContent = await driver.read(item.id, { models: models() }); expect(rawContent.byteLength).toBe(item.content_size); @@ -205,3 +206,40 @@ export async function shouldSupportFallbackDriverInReadWriteMode(driver: Storage expect(mainContent.toString()).toBe(fallbackContent.toString()); } } + +export async function shouldUpdateContentStorageIdAfterSwitchingDriver(oldDriver: StorageDriverBase, newDriver: StorageDriverBase) { + if (oldDriver.storageId === newDriver.storageId) throw new Error('Drivers must be different for this test'); + + const { user } = await createUserAndSession(1); + + const oldDriverModel = models({ + storageDriver: oldDriver, + }); + + const newDriverModel = models({ + storageDriver: newDriver, + }); + + const output = await oldDriverModel.item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing', + })), + }]); + + const itemId = output['00000000000000000000000000000001.md'].item.id; + + expect((await oldDriverModel.item().load(itemId)).content_storage_id).toBe(oldDriver.storageId); + + await newDriverModel.item().saveFromRawContent(user, [{ + name: '00000000000000000000000000000001.md', + body: Buffer.from(makeNoteSerializedBody({ + id: '00000000000000000000000000000001', + title: 'testing', + })), + }]); + + expect(await newDriverModel.item().count()).toBe(1); + expect((await oldDriverModel.item().load(itemId)).content_storage_id).toBe(newDriver.storageId); +} diff --git a/packages/server/src/services/database/types.ts b/packages/server/src/services/database/types.ts index 5a98f5078a1..f9e25ad5feb 100644 --- a/packages/server/src/services/database/types.ts +++ b/packages/server/src/services/database/types.ts @@ -263,7 +263,7 @@ export interface Item extends WithDates, WithUuid { jop_encryption_applied?: number; jop_updated_time?: number; owner_id?: Uuid; - storage_id?: number; + content_storage_id?: number; } export const databaseSchema: DatabaseTables = { @@ -443,7 +443,7 @@ export const databaseSchema: DatabaseTables = { jop_encryption_applied: { type: 'number' }, jop_updated_time: { type: 'string' }, owner_id: { type: 'string' }, - storage_id: { type: 'number' }, + content_storage_id: { type: 'number' }, }, }; // AUTO-GENERATED-TYPES From 467b1156ccff9609267e5b42faa8973ce892d5ac Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Mon, 8 Nov 2021 14:24:42 +0000 Subject: [PATCH 15/17] s3 --- packages/app-mobile/package.json | 6 +- packages/server/package-lock.json | 6581 +++++++++++++---- packages/server/package.json | 1 + .../models/items/storage/StorageDriverBase.ts | 2 +- .../items/storage/StorageDriverDatabase.ts | 2 +- .../models/items/storage/StorageDriverFs.ts | 2 +- .../items/storage/StorageDriverMemory.ts | 3 +- .../items/storage/StorageDriverS3.test.ts | 85 + .../models/items/storage/StorageDriverS3.ts | 101 + .../parseStorageDriverConnectionString.ts | 8 + packages/server/src/utils/types.ts | 5 + 11 files changed, 5301 insertions(+), 1495 deletions(-) create mode 100644 packages/server/src/models/items/storage/StorageDriverS3.test.ts create mode 100644 packages/server/src/models/items/storage/StorageDriverS3.ts diff --git a/packages/app-mobile/package.json b/packages/app-mobile/package.json index fc36befc9b6..e79ef38e949 100644 --- a/packages/app-mobile/package.json +++ b/packages/app-mobile/package.json @@ -15,8 +15,8 @@ "postinstall": "jetify && npm run build" }, "dependencies": { - "@joplin/lib": "~2.5", - "@joplin/renderer": "~2.5", + "@joplin/lib": "~2.6", + "@joplin/renderer": "~2.6", "@react-native-community/clipboard": "^1.5.0", "@react-native-community/datetimepicker": "^3.0.3", "@react-native-community/geolocation": "^2.0.2", @@ -73,7 +73,7 @@ "@codemirror/lang-markdown": "^0.18.4", "@codemirror/state": "^0.18.7", "@codemirror/view": "^0.18.19", - "@joplin/tools": "~2.5", + "@joplin/tools": "~2.6", "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-typescript": "^8.2.1", "@types/node": "^14.14.6", diff --git a/packages/server/package-lock.json b/packages/server/package-lock.json index a3fc459cf52..522202931a5 100644 --- a/packages/server/package-lock.json +++ b/packages/server/package-lock.json @@ -8,6 +8,7 @@ "name": "@joplin/server", "version": "2.6.2", "dependencies": { + "@aws-sdk/client-s3": "^3.40.0", "@fortawesome/fontawesome-free": "^5.15.1", "@koa/cors": "^3.1.0", "@types/uuid": "^8.3.1", @@ -59,1649 +60,2756 @@ "typescript": "^4.1.2" } }, - "node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, + "node_modules/@aws-crypto/crc32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-2.0.0.tgz", + "integrity": "sha512-TvE1r2CUueyXOuHdEigYjIZVesInd9KN+K/TFFNfkkxRThiNxO6i4ZqqAVMoEjAamZZ1AA8WXJkjCz7YShHPQA==", "dependencies": { - "@babel/highlight": "^7.10.4" + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" } }, - "node_modules/@babel/core": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", - "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", - "dev": true, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", + "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.1", - "@babel/parser": "^7.12.3", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "tslib": "^1.11.1" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", + "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", "dependencies": { - "safe-buffer": "~5.1.1" + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.0", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" } }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", + "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" } }, - "node_modules/@babel/core/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/@babel/generator": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", - "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", - "dev": true, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", + "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", "dependencies": { - "@babel/types": "^7.12.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "tslib": "^1.11.1" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-crypto/util": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.0.tgz", + "integrity": "sha512-YDooyH83m2P5A3h6lNH7hm6mIP93sU/dtzRmXIgtO4BCB7SvtX8ysVKQAE8tVky2DQ3HHxPCjNTuUe7YoAMrNQ==", "dependencies": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" } }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "dependencies": { - "@babel/types": "^7.10.4" + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@aws-sdk/abort-controller": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.40.0.tgz", + "integrity": "sha512-S7LzLvNuwuf0q7r4q7zqGzxd/W2xYsn8cpZ90MMb3ObolhbkLySrikUJujmXae8k+2/KFCOr+FVC0YLrATSUgQ==", + "dependencies": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/chunked-blob-reader": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader/-/chunked-blob-reader-3.37.0.tgz", + "integrity": "sha512-uDacnFaczeO962RnVttwAQddS4rgDfI7nfeY8NV6iZkDv5uxGzHTfH4jT7WvPDM1pSMcOMDx8RJ+Tmtsd1VTsA==", + "dependencies": { + "tslib": "^2.3.0" + } + }, + "node_modules/@aws-sdk/chunked-blob-reader-native": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader-native/-/chunked-blob-reader-native-3.37.0.tgz", + "integrity": "sha512-h9OYq6EvDrpb7SKod+Kow+d3aRNFVBYR1a8G8ahEDDQe3AtmA2Smyvni4kt/ABTiKvYdof2//Pq3BL/IUV9n9Q==", + "dependencies": { + "@aws-sdk/util-base64-browser": "3.37.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.40.0.tgz", + "integrity": "sha512-J7dUG3fZKEx/Mxyik373HIx0XmbIu+ZrHO9Eyea2CxZRTrEdUZiran2IdX5cafLULkUuOcQV6v2S4UZzsAmueA==", + "dependencies": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/client-sts": "3.40.0", + "@aws-sdk/config-resolver": "3.40.0", + "@aws-sdk/credential-provider-node": "3.40.0", + "@aws-sdk/eventstream-serde-browser": "3.40.0", + "@aws-sdk/eventstream-serde-config-resolver": "3.40.0", + "@aws-sdk/eventstream-serde-node": "3.40.0", + "@aws-sdk/fetch-http-handler": "3.40.0", + "@aws-sdk/hash-blob-browser": "3.40.0", + "@aws-sdk/hash-node": "3.40.0", + "@aws-sdk/hash-stream-node": "3.40.0", + "@aws-sdk/invalid-dependency": "3.40.0", + "@aws-sdk/md5-js": "3.40.0", + "@aws-sdk/middleware-apply-body-checksum": "3.40.0", + "@aws-sdk/middleware-bucket-endpoint": "3.40.0", + "@aws-sdk/middleware-content-length": "3.40.0", + "@aws-sdk/middleware-expect-continue": "3.40.0", + "@aws-sdk/middleware-host-header": "3.40.0", + "@aws-sdk/middleware-location-constraint": "3.40.0", + "@aws-sdk/middleware-logger": "3.40.0", + "@aws-sdk/middleware-retry": "3.40.0", + "@aws-sdk/middleware-sdk-s3": "3.40.0", + "@aws-sdk/middleware-serde": "3.40.0", + "@aws-sdk/middleware-signing": "3.40.0", + "@aws-sdk/middleware-ssec": "3.40.0", + "@aws-sdk/middleware-stack": "3.40.0", + "@aws-sdk/middleware-user-agent": "3.40.0", + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/node-http-handler": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/smithy-client": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/url-parser": "3.40.0", + "@aws-sdk/util-base64-browser": "3.37.0", + "@aws-sdk/util-base64-node": "3.37.0", + "@aws-sdk/util-body-length-browser": "3.37.0", + "@aws-sdk/util-body-length-node": "3.37.0", + "@aws-sdk/util-user-agent-browser": "3.40.0", + "@aws-sdk/util-user-agent-node": "3.40.0", + "@aws-sdk/util-utf8-browser": "3.37.0", + "@aws-sdk/util-utf8-node": "3.37.0", + "@aws-sdk/util-waiter": "3.40.0", + "@aws-sdk/xml-builder": "3.37.0", + "entities": "2.2.0", + "fast-xml-parser": "3.19.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@aws-sdk/client-s3/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", - "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.12.1" + "node_modules/@aws-sdk/client-sso": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.40.0.tgz", + "integrity": "sha512-eFQ4yFg8RlPaldv/ja2K3pUUyXauGbo4GJPlbPKYoquwW785au8qECKSl3iqBmUklj6WmdW1rmtlQk2OUcyYSw==", + "dependencies": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/config-resolver": "3.40.0", + "@aws-sdk/fetch-http-handler": "3.40.0", + "@aws-sdk/hash-node": "3.40.0", + "@aws-sdk/invalid-dependency": "3.40.0", + "@aws-sdk/middleware-content-length": "3.40.0", + "@aws-sdk/middleware-host-header": "3.40.0", + "@aws-sdk/middleware-logger": "3.40.0", + "@aws-sdk/middleware-retry": "3.40.0", + "@aws-sdk/middleware-serde": "3.40.0", + "@aws-sdk/middleware-stack": "3.40.0", + "@aws-sdk/middleware-user-agent": "3.40.0", + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/node-http-handler": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/smithy-client": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/url-parser": "3.40.0", + "@aws-sdk/util-base64-browser": "3.37.0", + "@aws-sdk/util-base64-node": "3.37.0", + "@aws-sdk/util-body-length-browser": "3.37.0", + "@aws-sdk/util-body-length-node": "3.37.0", + "@aws-sdk/util-user-agent-browser": "3.40.0", + "@aws-sdk/util-user-agent-node": "3.40.0", + "@aws-sdk/util-utf8-browser": "3.37.0", + "@aws-sdk/util-utf8-node": "3.37.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.40.0.tgz", + "integrity": "sha512-7jBlb1uyq2c0bFqi4ZVnEMNzLTodvIZKoxjh1LYA8OZISbMsDjTxFOYOmuyOhuPy0fLfLL3KRLtengJ23zs3QQ==", + "dependencies": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/config-resolver": "3.40.0", + "@aws-sdk/credential-provider-node": "3.40.0", + "@aws-sdk/fetch-http-handler": "3.40.0", + "@aws-sdk/hash-node": "3.40.0", + "@aws-sdk/invalid-dependency": "3.40.0", + "@aws-sdk/middleware-content-length": "3.40.0", + "@aws-sdk/middleware-host-header": "3.40.0", + "@aws-sdk/middleware-logger": "3.40.0", + "@aws-sdk/middleware-retry": "3.40.0", + "@aws-sdk/middleware-sdk-sts": "3.40.0", + "@aws-sdk/middleware-serde": "3.40.0", + "@aws-sdk/middleware-signing": "3.40.0", + "@aws-sdk/middleware-stack": "3.40.0", + "@aws-sdk/middleware-user-agent": "3.40.0", + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/node-http-handler": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/smithy-client": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/url-parser": "3.40.0", + "@aws-sdk/util-base64-browser": "3.37.0", + "@aws-sdk/util-base64-node": "3.37.0", + "@aws-sdk/util-body-length-browser": "3.37.0", + "@aws-sdk/util-body-length-node": "3.37.0", + "@aws-sdk/util-user-agent-browser": "3.40.0", + "@aws-sdk/util-user-agent-node": "3.40.0", + "@aws-sdk/util-utf8-browser": "3.37.0", + "@aws-sdk/util-utf8-node": "3.37.0", + "entities": "2.2.0", + "fast-xml-parser": "3.19.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "dev": true, + "node_modules/@aws-sdk/config-resolver": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.40.0.tgz", + "integrity": "sha512-QYy6J2k31QL6J74hPBfptnLW1kQYdN+xjwH4UQ1mv7EUhRoJN9ZY2soStJowFy4at6IIOOVWbyG5dyqvrbEovg==", "dependencies": { - "@babel/types": "^7.12.5" + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-config-provider": "3.40.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.40.0.tgz", + "integrity": "sha512-qHZdf2vxhzZkSygjw2I4SEYFL2dMZxxYvO4QlkqQouKY81OVxs/j69oiNCjPasQzGz5jaZZKI8xEAIfkSyr1lg==", "dependencies": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/helper-module-transforms/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-imds": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.40.0.tgz", + "integrity": "sha512-Ty/wVa+BQrCFrP06AGl5S1CeLifDt68YrlYXUnkRn603SX4DvxBgVO7XFeDH58G8ziDCiqxfmVl4yjbncPPeSw==", "dependencies": { - "@babel/types": "^7.10.4" + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/url-parser": "3.40.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", - "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.40.0.tgz", + "integrity": "sha512-lyTlgItJ+wPWIkcnkpmZTG+ApCwZBDjLzCPzhFOG1vT1wb0pF3KyJGmjWaW9C6s84rvWwGv1bY3/KBo92KtcjA==", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@aws-sdk/credential-provider-env": "3.40.0", + "@aws-sdk/credential-provider-imds": "3.40.0", + "@aws-sdk/credential-provider-sso": "3.40.0", + "@aws-sdk/credential-provider-web-identity": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-credentials": "3.37.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.40.0.tgz", + "integrity": "sha512-TANFmUqZwXd2ytA4Ji8IJDC8g42EnogjeIX+ypea/sImY5L7sQpd/sxQlcpIOJlr/6cGL3VhLGh2EGHXEJQEYA==", "dependencies": { - "@babel/types": "^7.12.1" + "@aws-sdk/credential-provider-env": "3.40.0", + "@aws-sdk/credential-provider-imds": "3.40.0", + "@aws-sdk/credential-provider-ini": "3.40.0", + "@aws-sdk/credential-provider-process": "3.40.0", + "@aws-sdk/credential-provider-sso": "3.40.0", + "@aws-sdk/credential-provider-web-identity": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-credentials": "3.37.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=10.0.0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.40.0.tgz", + "integrity": "sha512-qsaNCDesW2GasDbzpeOA371gxugi05JWxt3EKonLbUfkGKBK7kmmL6EgLIxZuNm2/Ve4RS07PKp8yBGm4xIx9w==", "dependencies": { - "@babel/types": "^7.11.0" + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-credentials": "3.37.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.40.0.tgz", + "integrity": "sha512-8XOz1cDsRvmb6UyLKersi+kLx2Bo4nXpsLZDbTuobEqMwtzIIZKW3C8n8icKpiqq1xhJ6hyT80on+HJ8ykrFKA==", + "dependencies": { + "@aws-sdk/client-sso": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-credentials": "3.37.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" + } }, - "node_modules/@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.40.0.tgz", + "integrity": "sha512-A1KT6Ft3k5B6bU2I2jXS4fSoWbWftEysrxT3zyuzhMbsstsHBJ/J9mEsQ4lgZyr6DXEqn7HD3MbdEoaBN2b3sg==", "dependencies": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, + "node_modules/@aws-sdk/eventstream-marshaller": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-marshaller/-/eventstream-marshaller-3.40.0.tgz", + "integrity": "sha512-zHGchfkG3B9M8OOKRpByeS5g1/15YQ0+QQHwxQRtm/CPtKBAIAsCZRQaCNBLu9uQMtBBKj5JsDUcjirhGeSvIg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@aws-crypto/crc32": "2.0.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-hex-encoding": "3.37.0", + "tslib": "^2.3.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, + "node_modules/@aws-sdk/eventstream-serde-browser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.40.0.tgz", + "integrity": "sha512-V0AXAfSkhY0hgxDJ0cNA+r42kL8295U7UTCp2Q2fvCaob3wKWh+54KZ2L4IOYTlK3yNzXJ5V6PP1zUuRlsUTew==", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@aws-sdk/eventstream-marshaller": "3.40.0", + "@aws-sdk/eventstream-serde-universal": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=4" + "node": ">= 10.0.0" } }, - "node_modules/@babel/parser": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", - "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" + "node_modules/@aws-sdk/eventstream-serde-config-resolver": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.40.0.tgz", + "integrity": "sha512-GgGiJBsQ1/SBTpRM/wCdFBCMo1Nybvy46bNVkH1ujCdp8UTLc5PozzNpH+15V2IQbc9sPDYffMab6HSFjDp5vw==", + "dependencies": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=6.0.0" + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, + "node_modules/@aws-sdk/eventstream-serde-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.40.0.tgz", + "integrity": "sha512-CnzX/JZGvhWlg+ooIPVZ78T+5wIm5Ld1BD7jwhlptJa8IjTMvkc8Nh4pAhc7T0ZScy4zZa/oTkqeVYCOVCyd1Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/eventstream-marshaller": "3.40.0", + "@aws-sdk/eventstream-serde-universal": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, + "node_modules/@aws-sdk/eventstream-serde-universal": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.40.0.tgz", + "integrity": "sha512-rkHwVMyZJMhp9iBixkuaAGQNer/DPxZ9kxDDtE+LuAMhepTYQ8c4lUW0QQhYbNMWf48QKD1G4FV3JXIj9JfP9A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/eventstream-marshaller": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", - "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", - "dev": true, + "node_modules/@aws-sdk/fetch-http-handler": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.40.0.tgz", + "integrity": "sha512-w1HiZromoU+/bbEo89uO81l6UO/M+c2uOMnXntZqe6t3ZHUUUo3AbvhKh0QGVFqRQa+Oi0+95KqWmTHa72/9Iw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/querystring-builder": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-base64-browser": "3.37.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@aws-sdk/hash-blob-browser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.40.0.tgz", + "integrity": "sha512-l8xyprVVKKH+720VrQ677X6VkvHttDXB4MxkMuxhSvwYBQwsRzP2Wppo7xIAtWGoS+oqlLmD4LCbHdhFRcN5yA==", + "dependencies": { + "@aws-sdk/chunked-blob-reader": "3.37.0", + "@aws-sdk/chunked-blob-reader-native": "3.37.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@aws-sdk/hash-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.40.0.tgz", + "integrity": "sha512-yOXXK85DdGDktdnQtXgMdaVKii4wtMjEhJ1mrvx2A9nMFNaPhxvERkVVIUKSWlJRa9ZujOw5jWOx8d2R51/Kjg==", + "dependencies": { + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-buffer-from": "3.37.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, + "node_modules/@aws-sdk/hash-stream-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-stream-node/-/hash-stream-node-3.40.0.tgz", + "integrity": "sha512-4yvRwODMGYtj6qrt+fyydV5MwVwPPoyoeqDoXdLo9x75vRY71DT1pMRt8PDOoY/ZwWbIdEt4+V7x0sLt2uy9WA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, + "node_modules/@aws-sdk/invalid-dependency": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.40.0.tgz", + "integrity": "sha512-axIWtDwCBDDqEgAJipX1FB1ZNpWYXquVwKDMo+7G+ftPBZ4FEq4M1ELhXJL3hhNJ9ZmCQzv+4F6Wnt8dwuzUaQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.37.0.tgz", + "integrity": "sha512-XLjA/a6AuGnCvcJZLsMTy2jxF2upgGhqCCkoIJgLlzzXHSihur13KcmPvW/zcaGnCRj0SvKWXiJHl4vDlW75VQ==", + "dependencies": { + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, + "node_modules/@aws-sdk/md5-js": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/md5-js/-/md5-js-3.40.0.tgz", + "integrity": "sha512-P1tzEljMD/MkjSc00TkVBYvfaVv/7S+04YEwE7tpu/jtxWxMHnk3CMKqq/F2iMhY83DRoqoYy+YqnaF4Bzr1uA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-utf8-browser": "3.37.0", + "@aws-sdk/util-utf8-node": "3.37.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@aws-sdk/middleware-apply-body-checksum": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-apply-body-checksum/-/middleware-apply-body-checksum-3.40.0.tgz", + "integrity": "sha512-gNSFlFu/O8cxAM0X64OwiLLN/NPXvK3FsAIJRsfhIW+dX0bEq4lsGPsdU8Tx+9eenaj/Z01uqgWZ6Izar8zVvQ==", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.37.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.40.0.tgz", + "integrity": "sha512-y9m1wF45aL1yuDWe396O2BPiUsWJz4BKmPjIuz+YvvHMzI1eu32IVqA7NXpQ7Y5J9GYEBn1LA5zmYMMbzpHilA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-arn-parser": "3.37.0", + "@aws-sdk/util-config-provider": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, + "node_modules/@aws-sdk/middleware-content-length": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.40.0.tgz", + "integrity": "sha512-sybAJb8v7I/vvL08R3+TI/XDAg9gybQTZ2treC24Ap4+jAOz4QBTHJPMKaUlEeFlMUcq4rj6/u2897ebYH6opw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.40.0.tgz", + "integrity": "sha512-FY6vT0u1ptDZ2bBj1yG/Iyk6HZB7U9fbrpeZNPYzgq8HJxBcTgfLwtB3VLobyhThQm9X2a7R2YZrwtArW8yQfQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/middleware-header-default": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, + "node_modules/@aws-sdk/middleware-header-default": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-header-default/-/middleware-header-default-3.40.0.tgz", + "integrity": "sha512-eXQ13x/AivPZKoG8/akp9g5xdNHuKftl83GMuk9K6tt4+eAa22TdxiFu4R0UVlKAvo2feqxFrNs5DhhhBeAQWA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.40.0.tgz", + "integrity": "sha512-/wocR7JFOLM7/+BQM1DgAd6KCFYcdxYu1P7AhI451GlVNuYa5f89zh7p0gt3SRC6monI5lXgpL7RudhDm8fTrA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", - "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", - "dev": true, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.40.0.tgz", + "integrity": "sha512-9XaVPYsDQVJbWJH96sNdv4HHY3j1raman+lYxMu4528Awp0OdWUeSsGRYRN+CnRPlkHnfNw4m6SKdWYHxdjshw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.40.0.tgz", + "integrity": "sha512-19kx0Xg5ymVRKoupmhdmfTBkROcv3DZj508agpyG2YAo0abOObMlIP4Jltg0VD4PhNjGzNh0jFGJnvhjdwv4/A==", "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", - "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", - "dev": true, + "node_modules/@aws-sdk/middleware-retry": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.40.0.tgz", + "integrity": "sha512-SMUJrukugLL7YJE5X8B2ToukxMWMPwnf7jAFr84ptycCe8bdWv8x8klQ3EtVWpyqochtNlbTi6J/tTQBniUX7A==", "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.5", - "@babel/types": "^7.12.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/service-error-classification": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.40.0.tgz", + "integrity": "sha512-9Z1mGWhCmJqxQYZrg5RIdlMxetGsT4LLV8/yLNxALuOr+GzLoA1r4D2lqz+1lJcDZKphP22xBxoeJG5MS80mig==", "dependencies": { - "ms": "2.1.2" + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-arn-parser": "3.37.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=6.0" + "node": ">= 10.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@aws-sdk/signature-v4-crt": "^3.31.0" } }, - "node_modules/@babel/traverse/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@babel/types": { - "version": "7.12.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", - "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", - "dev": true, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.40.0.tgz", + "integrity": "sha512-TcrbCvj1PkabFZiNczT3yePZtuEm2fAIw1OVnQyLcF2KW+p62Hv5YkK4MPOfx3LA/0lzjOUO1RNl2x7gzV443Q==", "dependencies": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" + "@aws-sdk/middleware-signing": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@babel/types/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "dev": true, + "node_modules/@aws-sdk/middleware-serde": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.40.0.tgz", + "integrity": "sha512-uOWfZjlAoBy6xPqp0d4ka83WNNbEVCWn9WwfqBUXThyoTdTooYSpXe5y2YzN0BJa8b+tEZTyWpgamnBpFLp47g==", "dependencies": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - }, - "bin": { - "watch": "cli.js" + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=0.1.95" + "node": ">= 10.0.0" } }, - "node_modules/@fortawesome/fontawesome-free": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.1.tgz", - "integrity": "sha512-OEdH7SyC1suTdhBGW91/zBfR6qaIhThbcN8PUXtXilY4GYnSBbVqOntdHbC1vXwsDnX0Qix2m2+DSU1J51ybOQ==", - "hasInstallScript": true, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.40.0.tgz", + "integrity": "sha512-RqK5nPbfma0qInMvjtpVkDYY/KkFS6EKlOv3DWTdxbXJ4YuOxgKiuUromhmBUoyjFag0JO7LUWod07H+/DawoA==", + "dependencies": { + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, "engines": { - "node": ">=6" + "node": ">= 10.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.40.0.tgz", + "integrity": "sha512-ZoRpaZeAIQa1Q+NyEh74ATwOR3nFGfcP6Nu0jFzgqoVijCReMnhtlCRx23ccBu1ZLZNUsNk6MhKjY+ZTfNsjEg==", "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.40.0.tgz", + "integrity": "sha512-hby9HvESUYJxpdALX+6Dn2LPmS5jtMVurGB/+j3MWOvIcDYB4bcSXgVRvXzYnTKwbSupIdbX9zOE2ZAx2SJpUQ==", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "tslib": "^2.3.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.40.0.tgz", + "integrity": "sha512-dzC2fxWnanetFJ1oYgil8df3N36bR1yc/OCOpbdfQNiUk1FrXiCXqH5rHNO8zCvnwJAj8GHFwpFGd9a2Qube2w==", "dependencies": { - "p-locate": "^4.1.0" + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, + "node_modules/@aws-sdk/node-config-provider": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.40.0.tgz", + "integrity": "sha512-AmokjgUDECG8osoMfdRsPNweqI+L1pn4bYGk5iTLmzbBi0o4ot0U1FdX8Rf0qJZZwS4t1TXc3s8/PDVknmPxKg==", "dependencies": { - "p-try": "^2.0.0" + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 10.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, + "node_modules/@aws-sdk/node-http-handler": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.40.0.tgz", + "integrity": "sha512-qjda6IbxDhbYr8NHmrMurKkbjgLUkfTMVgagDErDK24Nm3Dn5VaO6J4n6c0Q4OLHlmFaRcUfZSTrOo5DAubqCw==", "dependencies": { - "p-limit": "^2.2.0" + "@aws-sdk/abort-controller": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/querystring-builder": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, + "node_modules/@aws-sdk/property-provider": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.40.0.tgz", + "integrity": "sha512-Mx4lkShjsYRwW9ujHA1pcnuubrWQ4kF5/DXWNfUiXuSIO/0Lojp1qTLheyBm4vzkJIlx5umyP6NvRAUkEHSN4Q==", + "dependencies": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, "engines": { - "node": ">=6" + "node": ">= 10.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.40.0.tgz", + "integrity": "sha512-f4ea7/HZkjpvGBrnRIuzc/bhrExWrgDv7eulj4htPukZGHdTqSJD3Jk8lEXWvFuX2vUKQDGhEhCDsqup7YWJQQ==", + "dependencies": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true, + "node_modules/@aws-sdk/querystring-builder": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.40.0.tgz", + "integrity": "sha512-gO24oipnNaxJRBXB7lhLfa96vIMOd8gtMBqJTjelTjS2e1ZP1YY12CNKKTWwafSk8Ge021erZAG/YTOaXGpv+g==", + "dependencies": { + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-uri-escape": "3.37.0", + "tslib": "^2.3.0" + }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, + "node_modules/@aws-sdk/querystring-parser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.40.0.tgz", + "integrity": "sha512-XZIyaKQIiZAM6zelCBcsLHhVDOLafi7XIOd3jy6SymGN8ajj3HqUJ/vdQ5G6ISTk18OrqgqcCOI9oNzv+nrBcA==", "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">= 10.0.0" } }, - "node_modules/@jest/console/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, + "node_modules/@aws-sdk/service-error-classification": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.40.0.tgz", + "integrity": "sha512-c8btKmkvjXczWudXubGdbO3JgmjySBUVC/gCrZDNfwNGsG8RYJJQYYcnmt1gWjelUZsgMDl/2PIzxTlxVF91rA==", "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@jest/core": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", - "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", - "dev": true, + "node_modules/@aws-sdk/shared-ini-file-loader": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.37.0.tgz", + "integrity": "sha512-+vRBSlfa48R9KL7DpQt3dsu5/+5atjRgoCISblWo3SLpjrx41pKcjKneo7a1u0aP1Xc2oG2TfIyqTWZuOXsmEQ==", "dependencies": { - "@jest/console": "^26.6.2", - "@jest/reporters": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.6.2", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-resolve-dependencies": "^26.6.3", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "jest-watcher": "^26.6.2", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "tslib": "^2.3.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">= 10.0.0" } }, - "node_modules/@jest/core/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, + "node_modules/@aws-sdk/signature-v4": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.40.0.tgz", + "integrity": "sha512-Q1GNZJRCS3W2qsRtDsX/b6EOSfMXfr6TW46N3LnLTGYZ3KAN2SOSJ1DsW59AuGpEZyRmOhJ9L/Q5U403+bZMXQ==", + "dependencies": { + "@aws-sdk/is-array-buffer": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-hex-encoding": "3.37.0", + "@aws-sdk/util-uri-escape": "3.37.0", + "tslib": "^2.3.0" + }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@jest/core/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "node_modules/@aws-sdk/signature-v4-crt": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-crt/-/signature-v4-crt-3.40.0.tgz", + "integrity": "sha512-J0KeUe2pZNolgC/4dYkkaXAYgcJ7OZEfN7+ezCJJ3f1t0X+NVZoh7/9iBR6Kq1x/LzpGy67MdKMXDIdBT0DmNw==", + "peer": true, "dependencies": { - "fill-range": "^7.0.1" + "@aws-sdk/is-array-buffer": "3.37.0", + "@aws-sdk/querystring-parser": "3.40.0", + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/util-hex-encoding": "3.37.0", + "@aws-sdk/util-uri-escape": "3.37.0", + "aws-crt": "^1.9.7", + "tslib": "^2.3.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@jest/core/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "node_modules/@aws-sdk/smithy-client": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.40.0.tgz", + "integrity": "sha512-6x6uvmfhFpkCiT1O/SsFWRyyqs3ZHMB1hVypn9XfT1/XSrfVLhcbBtnX1/UGPkQvA1GJGo5Pkxv3odQfUw7rUg==", "dependencies": { - "to-regex-range": "^5.0.1" + "@aws-sdk/middleware-stack": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@jest/core/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "node_modules/@jest/core/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "node_modules/@aws-sdk/types": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.40.0.tgz", + "integrity": "sha512-KpILcfvRaL88TLvo3SY4OuCCg90SvcNLPyjDwUuBqiOyWODjrKShHtAPJzej4CLp92lofh+ul0UnBfV9Jb/5PA==", "engines": { - "node": ">=0.12.0" + "node": ">= 10.0.0" } }, - "node_modules/@jest/core/node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, + "node_modules/@aws-sdk/url-parser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.40.0.tgz", + "integrity": "sha512-HwNV+HX7bHgLk5FzTOgdXANsC0SeVz5PMC4Nh+TLz2IoeQnrw4H8dsA4YNonncjern5oC5veKRjQeOoCL5SlSQ==", "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "@aws-sdk/querystring-parser": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.37.0.tgz", + "integrity": "sha512-njIYn8gzm7Ms17A2oEu0vN/0GJpgq7cNFFtzBrM1cPtrc1jhMRJx5hzS7uX5h6ll8BM92bA3y00evRZFHxQPVQ==", + "dependencies": { + "tslib": "^2.3.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@jest/core/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, + "node_modules/@aws-sdk/util-base64-browser": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.37.0.tgz", + "integrity": "sha512-o4s/rHVm5k8eC/T7grJQINyYA/mKfDmEWKMA9wk5iBroXlI2rUm7x649TBk5hzoddufk/mffEeNz/1wM7yTmlg==", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "tslib": "^2.3.0" } }, - "node_modules/@jest/core/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, + "node_modules/@aws-sdk/util-base64-node": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.37.0.tgz", + "integrity": "sha512-1UPxly1GPrGZtlIWvbNCDIAund4Oyp8cFi9neA43TeNACvrmEQu/nG01pDbOoo0ENoVSVJrNAVBeqKEpqjH2GA==", + "dependencies": { + "@aws-sdk/util-buffer-from": "3.37.0", + "tslib": "^2.3.0" + }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@jest/core/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, + "node_modules/@aws-sdk/util-body-length-browser": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.37.0.tgz", + "integrity": "sha512-tClmH1uYelqWT43xxmnOsVFbCQJiIwizp6y4E109G2LIof07inxrO0L8nbwBpjhugVplx6NZr9IaqTFqbdM1gA==", "dependencies": { - "ansi-regex": "^5.0.0" + "tslib": "^2.3.0" + } + }, + "node_modules/@aws-sdk/util-body-length-node": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.37.0.tgz", + "integrity": "sha512-aY3mXdbEajruRi9CHgq/heM89R+Gectj/Xrs1naewmamaN8NJrvjDm3s+cw//lqqSOW903LYHXDgm7wvCzUnFA==", + "dependencies": { + "tslib": "^2.3.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@jest/core/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.37.0.tgz", + "integrity": "sha512-aa3SBwjLwImuJoE4+hxDIWQ9REz3UFb3p7KFPe9qopdXb/yB12RTcbrXVb4whUux4i4mO6KRij0ZNjFZrjrKPg==", "dependencies": { - "is-number": "^7.0.0" + "@aws-sdk/is-array-buffer": "3.37.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">=8.0" + "node": ">= 10.0.0" } }, - "node_modules/@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, + "node_modules/@aws-sdk/util-config-provider": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.40.0.tgz", + "integrity": "sha512-NjZGrA4mqhpr6gkVCAUweurP0Z9d3vFyXJCtulC0BFbpKAnKCf/crSK56NwUaNhAEMCkSuBvjRFzkbfT+HO8bA==", "dependencies": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" + "tslib": "^2.3.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">= 10.0.0" } }, - "node_modules/@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, + "node_modules/@aws-sdk/util-credentials": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-credentials/-/util-credentials-3.37.0.tgz", + "integrity": "sha512-zcLhSZDKgBLhUjSU5HoQpuQiP3v8oE86NmV/tiZVPEaO6YVULEAB2Cfj1hpM/b/JXWzjSHfT06KXT7QUODKS+A==", "dependencies": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">= 10.0.0" } }, - "node_modules/@jest/globals": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", - "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", - "dev": true, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.37.0.tgz", + "integrity": "sha512-tn5UpfaeM+rZWqynoNqB8lwtcAXil5YYO3HLGH9himpWAdft/2Z7LK6bsYDpctaAI1WHgMDcL0bw3Id04ZUbhA==", "dependencies": { - "@jest/environment": "^26.6.2", - "@jest/types": "^26.6.2", - "expect": "^26.6.2" + "tslib": "^2.3.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">= 10.0.0" } }, - "node_modules/@jest/reporters": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", - "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", - "dev": true, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.37.0.tgz", + "integrity": "sha512-NvDCfOhLLVHp27oGUUs8EVirhz91aX5gdxGS7J/sh5PF0cNN8rwaR1vSLR7BxPmJHMO7NH7i9EwiELfLfYcq6g==", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.3", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^7.0.0" + "tslib": "^2.3.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.37.0.tgz", + "integrity": "sha512-8pKf4YJTELP5lm/CEgYw2atyJBB1RWWqFa0sZx6YJmTlOtLF5G6raUdAi4iDa2hldGt2B6IAdIIyuusT8zeU8Q==", + "dependencies": { + "tslib": "^2.3.0" }, - "optionalDependencies": { - "node-notifier": "^8.0.0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@jest/reporters/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.40.0.tgz", + "integrity": "sha512-C69sTI26bV2EprTv3DTXu9XP7kD9Wu4YVPBzqztOYArd2GDYw3w+jS8SEg3XRbjAKY/mOPZ2Thw4StjpZlWZiA==", + "dependencies": { + "@aws-sdk/types": "3.40.0", + "bowser": "^2.11.0", + "tslib": "^2.3.0" + } }, - "node_modules/@jest/reporters/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.40.0.tgz", + "integrity": "sha512-cjIzd0hRZFTTh7iLJD6Bciu++Em1iaM1clyG02xRl0JD5DEtDSR1zO02uu+AeM7GSLGOxIvwOkK2j8ySPAOmBA==", + "dependencies": { + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" } }, - "node_modules/@jest/reporters/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==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.37.0.tgz", + "integrity": "sha512-tuiOxzfqet1kKGYzlgpMGfhr64AHJnYsFx2jZiH/O6Yq8XQg43ryjQlbJlim/K/XHGNzY0R+nabeJg34q3Ua1g==", + "dependencies": { + "tslib": "^2.3.0" } }, - "node_modules/@jest/source-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", - "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", - "dev": true, + "node_modules/@aws-sdk/util-utf8-node": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.37.0.tgz", + "integrity": "sha512-fUAgd7UTCULL36j9/vnXHxVhxvswnq23mYgTCIT8NQ7wHN30q2a89ym1e9DwGeQkJEBOkOcKLn6nsMsN7YQMDQ==", "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" + "@aws-sdk/util-buffer-from": "3.37.0", + "tslib": "^2.3.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">= 10.0.0" } }, - "node_modules/@jest/source-map/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "node_modules/@jest/source-map/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==", - "dev": true, + "node_modules/@aws-sdk/util-waiter": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-waiter/-/util-waiter-3.40.0.tgz", + "integrity": "sha512-jdxwNEZdID49ZvyAnxaeNm5w2moIfMLOwj/q6TxKlxYoXMs16FQWkhyfGue0vEASzchS49ewbyt+KBqpT31Ebg==", + "dependencies": { + "@aws-sdk/abort-controller": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.0.0" } }, - "node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.37.0.tgz", + "integrity": "sha512-Vf0f4ZQ+IBo/l9wUaTOXLqqQO9b/Ll5yPbg+EhHx8zlHbTHIm89ettkVCGyT/taGagC1X+ZeTK9maX6ymEOBow==", "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "tslib": "^2.3.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">= 10.0.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", - "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "dev": true, "dependencies": { - "@jest/test-result": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3" - }, - "engines": { - "node": ">= 10.14.2" + "@babel/highlight": "^7.10.4" } }, - "node_modules/@jest/test-sequencer/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "node_modules/@jest/transform": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", - "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "node_modules/@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", "dev": true, "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.6.2", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.6.2", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@jest/transform/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" + "safe-buffer": "~5.1.1" } }, - "node_modules/@jest/transform/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "ms": "2.1.2" }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@jest/transform/node_modules/graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "node_modules/@babel/core/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/@jest/transform/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@babel/generator": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "dev": true, - "engines": { - "node": ">=0.12.0" + "dependencies": { + "@babel/types": "^7.12.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" } }, - "node_modules/@jest/transform/node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "node_modules/@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - }, - "engines": { - "node": ">=8" + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, - "node_modules/@jest/transform/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "@babel/types": "^7.10.4" } }, - "node_modules/@jest/transform/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==", + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@babel/types": "^7.12.1" } }, - "node_modules/@jest/transform/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "dev": true, "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" + "@babel/types": "^7.12.5" } }, - "node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", "dev": true, "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" } }, - "node_modules/@jest/types/node_modules/@types/yargs": { - "version": "15.0.10", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.10.tgz", - "integrity": "sha512-z8PNtlhrj7eJNLmrAivM7rjBESG6JwC5xP3RVk12i/8HVP7Xnx/sEmERnRImyEuUaJfO942X0qMOYsoupaJbZQ==", + "node_modules/@babel/helper-module-transforms/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", "dev": true, "dependencies": { - "@types/yargs-parser": "*" + "@babel/types": "^7.10.4" } }, - "node_modules/@koa/cors": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-3.1.0.tgz", - "integrity": "sha512-7ulRC1da/rBa6kj6P4g2aJfnET3z8Uf3SWu60cjbtxTA5g8lxRdX/Bd2P92EagGwwAhANeNw8T8if99rJliR6Q==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "dev": true, "dependencies": { - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 8.0.0" + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, - "node_modules/@rmp135/sql-ts": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@rmp135/sql-ts/-/sql-ts-1.7.0.tgz", - "integrity": "sha512-F3xxXTH1X7OPEN8+KB83kUfVHFHd8tDf0zgP39ytJGgj8Nv3xTeD7YBKANL1EsCbKGfuTqHBCo54IXvCbkYpGg==", + "node_modules/@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", "dev": true, "dependencies": { - "handlebars": "^4.1.2", - "knex": "^0.20.4", - "yargs": "^15.0.2" - }, - "bin": { - "sql-ts": "bin/sql-ts" - }, - "peerDependencies": { - "mssql": "^6.2.0", - "mysql": "^2.18.1", - "mysql2": "^2.1.0", - "pg": "^8.0.3", - "sqlite3": "^4.1.1" - }, - "peerDependenciesMeta": { - "mssql": { - "optional": true - }, - "mysql": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "pg": { - "optional": true - }, - "sqlite3": { - "optional": true - } - } - }, - "node_modules/@rmp135/sql-ts/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" + "@babel/types": "^7.12.1" } }, - "node_modules/@rmp135/sql-ts/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==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "@babel/types": "^7.11.0" } }, - "node_modules/@rmp135/sql-ts/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } + "node_modules/@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true }, - "node_modules/@rmp135/sql-ts/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==", + "node_modules/@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, - "node_modules/@rmp135/sql-ts/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==", - "dev": true - }, - "node_modules/@rmp135/sql-ts/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "node_modules/@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" } }, - "node_modules/@rmp135/sql-ts/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@rmp135/sql-ts/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/@rmp135/sql-ts/node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "node_modules/@babel/parser": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz", + "integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==", "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, "engines": { - "node": ">= 0.10" + "node": ">=6.0.0" } }, - "node_modules/@rmp135/sql-ts/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@rmp135/sql-ts/node_modules/knex": { - "version": "0.20.15", - "resolved": "https://registry.npmjs.org/knex/-/knex-0.20.15.tgz", - "integrity": "sha512-WHmvgfQfxA5v8pyb9zbskxCS1L1WmYgUbwBhHojlkmdouUOazvroUWlCr6KIKMQ8anXZh1NXOOtIUMnxENZG5Q==", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "dependencies": { - "colorette": "1.1.0", - "commander": "^4.1.1", - "debug": "4.1.1", - "esm": "^3.2.25", - "getopts": "2.2.5", - "inherits": "~2.0.4", - "interpret": "^2.0.0", - "liftoff": "3.1.0", - "lodash": "^4.17.15", - "mkdirp": "^0.5.1", - "pg-connection-string": "2.1.0", - "tarn": "^2.0.0", - "tildify": "2.0.0", - "uuid": "^7.0.1", - "v8flags": "^3.1.3" - }, - "bin": { - "knex": "bin/cli.js" - }, - "engines": { - "node": ">=8" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "mssql": "^6.1.0", - "mysql": "^2.18.1", - "mysql2": "^2.1.0", - "pg": "^7.18.2", - "sqlite3": "^4.1.1" - }, - "peerDependenciesMeta": { - "mssql": { - "optional": true - }, - "mysql": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "pg": { - "optional": true - }, - "sqlite3": { - "optional": true - } + "@babel/core": "^7.0.0-0" } }, - "node_modules/@rmp135/sql-ts/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/@rmp135/sql-ts/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@rmp135/sql-ts/node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/@rmp135/sql-ts/node_modules/string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@rmp135/sql-ts/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@rmp135/sql-ts/node_modules/uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@rmp135/sql-ts/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@rmp135/sql-ts/node_modules/y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, - "node_modules/@rmp135/sql-ts/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@rmp135/sql-ts/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "engines": { - "node": ">=6" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@sinonjs/commons": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "dependencies": { - "type-detect": "4.0.8" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, "dependencies": { - "defer-to-connect": "^1.0.1" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=6" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", "dev": true, "dependencies": { - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/babel__core": { - "version": "7.1.12", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", - "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "node_modules/@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", - "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "node_modules/@babel/traverse": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.5.tgz", + "integrity": "sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==", "dev": true, "dependencies": { - "@babel/types": "^7.0.0" + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.5", + "@babel/types": "^7.12.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" } }, - "node_modules/@types/babel__template": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", - "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@types/babel__traverse": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz", - "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==", + "node_modules/@babel/traverse/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@babel/types": { + "version": "7.12.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz", + "integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" } }, - "node_modules/@types/body-parser": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", - "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", + "node_modules/@babel/types/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", "dev": true, "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "bin": { + "watch": "cli.js" + }, + "engines": { + "node": ">=0.1.95" } }, - "node_modules/@types/connect": { - "version": "3.4.32", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", - "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", - "dev": true, - "dependencies": { - "@types/node": "*" + "node_modules/@fortawesome/fontawesome-free": { + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.1.tgz", + "integrity": "sha512-OEdH7SyC1suTdhBGW91/zBfR6qaIhThbcN8PUXtXilY4GYnSBbVqOntdHbC1vXwsDnX0Qix2m2+DSU1J51ybOQ==", + "hasInstallScript": true, + "engines": { + "node": ">=6" } }, - "node_modules/@types/cookies": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.3.tgz", - "integrity": "sha512-NEkYn8pNsYZIxf3ZrjdPoeyueiPc0RbQClUpTwmdHkpmQQ8iDAlQYKpabuegHy7BJcqTteSTkhURMEs9ZxyEWg==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "dependencies": { - "@types/connect": "*", - "@types/express": "*", - "@types/keygrip": "*", - "@types/node": "*" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.1.tgz", - "integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/serve-static": "*" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/express-serve-static-core": { - "version": "4.16.9", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz", - "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "@types/node": "*", - "@types/range-parser": "*" + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/fs-extra": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.0.tgz", - "integrity": "sha512-bCtL5v9zdbQW86yexOlXWTEGvLNqWxMFyi7gQA7Gcthbezr2cPSOb8SkESVKA937QD5cIwOFLDFt0MQoXOEr9Q==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "@types/node": "*" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", - "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "@types/node": "*" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/highlight.js": { - "version": "9.12.4", - "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.4.tgz", - "integrity": "sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww==", - "dev": true - }, - "node_modules/@types/http-assert": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", - "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==", - "dev": true + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" + "engines": { + "node": ">=8" } }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "node_modules/@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", "dev": true, "dependencies": { - "@types/istanbul-lib-report": "*" + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" } }, - "node_modules/@types/jest": { - "version": "26.0.15", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.15.tgz", - "integrity": "sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog==", + "node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "dependencies": { - "jest-diff": "^26.0.0", - "pretty-format": "^26.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/@types/jsdom": { - "version": "16.2.6", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.6.tgz", - "integrity": "sha512-yQA+HxknGtW9AkRTNyiSH3OKW5V+WzO8OPTdne99XwJkYC+KYxfNIcoJjeiSqP3V00PUUpFP6Myoo9wdIu78DQ==", + "node_modules/@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", "dev": true, "dependencies": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", - "@types/parse5": "*", - "@types/tough-cookie": "*" + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">= 10.14.2" } }, - "node_modules/@types/keygrip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.1.tgz", - "integrity": "sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg=", - "dev": true + "node_modules/@jest/core/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/@types/koa": { - "version": "2.0.49", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.49.tgz", - "integrity": "sha512-WQWpCH8O4Dslk8IcXfazff40aM1jXX7BQRbADIj/fKozVPu76P/wQE4sRe2SCWMn8yNkOcare2MkDrnZqLMkPQ==", + "node_modules/@jest/core/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "@types/accepts": "*", - "@types/cookies": "*", - "@types/http-assert": "*", - "@types/keygrip": "*", - "@types/koa-compose": "*", - "@types/node": "*" + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/koa-compose": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.4.tgz", - "integrity": "sha512-ioou0rxkuWL+yBQYsHUQAzRTfVxAg8Y2VfMftU+Y3RA03/MzuFL0x/M2sXXj3PkfnENbHsjeHR1aMdezLYpTeA==", + "node_modules/@jest/core/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { - "@types/koa": "*" + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/linkify-it": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.0.tgz", - "integrity": "sha512-x9OaQQTb1N2hPZ/LWJsqushexDvz7NgzuZxiRmZio44WPuolTZNHDBCrOxCzRVOMwamJRO2dWax5NbygOf1OTQ==", + "node_modules/@jest/core/node_modules/graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, - "node_modules/@types/markdown-it": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.0.0.tgz", - "integrity": "sha512-+RJNprPSIcEUBzj3nx8WYwRsDdAKF6/dG932OleYKbTqBSJ7VvZK0JbPKeEpIYxoniUhgvgyZjO4vlCd4mFTdw==", + "node_modules/@jest/core/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "dependencies": { - "@types/highlight.js": "^9.7.0", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/@jest/core/node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/core/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "node-notifier": "^8.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "node_modules/@jest/reporters/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/source-map/node_modules/graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "node_modules/@jest/source-map/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-sequencer/node_modules/graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "node_modules/@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/transform/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "node_modules/@jest/transform/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/@jest/transform/node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/transform/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/types/node_modules/@types/yargs": { + "version": "15.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.10.tgz", + "integrity": "sha512-z8PNtlhrj7eJNLmrAivM7rjBESG6JwC5xP3RVk12i/8HVP7Xnx/sEmERnRImyEuUaJfO942X0qMOYsoupaJbZQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@koa/cors": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-3.1.0.tgz", + "integrity": "sha512-7ulRC1da/rBa6kj6P4g2aJfnET3z8Uf3SWu60cjbtxTA5g8lxRdX/Bd2P92EagGwwAhANeNw8T8if99rJliR6Q==", + "dependencies": { + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@rmp135/sql-ts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@rmp135/sql-ts/-/sql-ts-1.7.0.tgz", + "integrity": "sha512-F3xxXTH1X7OPEN8+KB83kUfVHFHd8tDf0zgP39ytJGgj8Nv3xTeD7YBKANL1EsCbKGfuTqHBCo54IXvCbkYpGg==", + "dev": true, + "dependencies": { + "handlebars": "^4.1.2", + "knex": "^0.20.4", + "yargs": "^15.0.2" + }, + "bin": { + "sql-ts": "bin/sql-ts" + }, + "peerDependencies": { + "mssql": "^6.2.0", + "mysql": "^2.18.1", + "mysql2": "^2.1.0", + "pg": "^8.0.3", + "sqlite3": "^4.1.1" + }, + "peerDependenciesMeta": { + "mssql": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/@rmp135/sql-ts/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@rmp135/sql-ts/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/@rmp135/sql-ts/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@rmp135/sql-ts/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==", + "dev": true + }, + "node_modules/@rmp135/sql-ts/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@rmp135/sql-ts/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/knex": { + "version": "0.20.15", + "resolved": "https://registry.npmjs.org/knex/-/knex-0.20.15.tgz", + "integrity": "sha512-WHmvgfQfxA5v8pyb9zbskxCS1L1WmYgUbwBhHojlkmdouUOazvroUWlCr6KIKMQ8anXZh1NXOOtIUMnxENZG5Q==", + "dev": true, + "dependencies": { + "colorette": "1.1.0", + "commander": "^4.1.1", + "debug": "4.1.1", + "esm": "^3.2.25", + "getopts": "2.2.5", + "inherits": "~2.0.4", + "interpret": "^2.0.0", + "liftoff": "3.1.0", + "lodash": "^4.17.15", + "mkdirp": "^0.5.1", + "pg-connection-string": "2.1.0", + "tarn": "^2.0.0", + "tildify": "2.0.0", + "uuid": "^7.0.1", + "v8flags": "^3.1.3" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "mssql": "^6.1.0", + "mysql": "^2.18.1", + "mysql2": "^2.1.0", + "pg": "^7.18.2", + "sqlite3": "^4.1.1" + }, + "peerDependenciesMeta": { + "mssql": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/@rmp135/sql-ts/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/@rmp135/sql-ts/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/@rmp135/sql-ts/node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "node_modules/@rmp135/sql-ts/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@rmp135/sql-ts/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@types/accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz", + "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", + "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookies": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.3.tgz", + "integrity": "sha512-NEkYn8pNsYZIxf3ZrjdPoeyueiPc0RbQClUpTwmdHkpmQQ8iDAlQYKpabuegHy7BJcqTteSTkhURMEs9ZxyEWg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/express": "*", + "@types/keygrip": "*", + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.1.tgz", + "integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.16.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz", + "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.0.tgz", + "integrity": "sha512-bCtL5v9zdbQW86yexOlXWTEGvLNqWxMFyi7gQA7Gcthbezr2cPSOb8SkESVKA937QD5cIwOFLDFt0MQoXOEr9Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/highlight.js": { + "version": "9.12.4", + "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.4.tgz", + "integrity": "sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww==", + "dev": true + }, + "node_modules/@types/http-assert": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", + "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "26.0.15", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.15.tgz", + "integrity": "sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog==", + "dev": true, + "dependencies": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "node_modules/@types/jsdom": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.6.tgz", + "integrity": "sha512-yQA+HxknGtW9AkRTNyiSH3OKW5V+WzO8OPTdne99XwJkYC+KYxfNIcoJjeiSqP3V00PUUpFP6Myoo9wdIu78DQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/parse5": "*", + "@types/tough-cookie": "*" + } + }, + "node_modules/@types/keygrip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.1.tgz", + "integrity": "sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg=", + "dev": true + }, + "node_modules/@types/koa": { + "version": "2.0.49", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.49.tgz", + "integrity": "sha512-WQWpCH8O4Dslk8IcXfazff40aM1jXX7BQRbADIj/fKozVPu76P/wQE4sRe2SCWMn8yNkOcare2MkDrnZqLMkPQ==", + "dev": true, + "dependencies": { + "@types/accepts": "*", + "@types/cookies": "*", + "@types/http-assert": "*", + "@types/keygrip": "*", + "@types/koa-compose": "*", + "@types/node": "*" + } + }, + "node_modules/@types/koa-compose": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.4.tgz", + "integrity": "sha512-ioou0rxkuWL+yBQYsHUQAzRTfVxAg8Y2VfMftU+Y3RA03/MzuFL0x/M2sXXj3PkfnENbHsjeHR1aMdezLYpTeA==", + "dev": true, + "dependencies": { + "@types/koa": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.0.tgz", + "integrity": "sha512-x9OaQQTb1N2hPZ/LWJsqushexDvz7NgzuZxiRmZio44WPuolTZNHDBCrOxCzRVOMwamJRO2dWax5NbygOf1OTQ==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.0.0.tgz", + "integrity": "sha512-+RJNprPSIcEUBzj3nx8WYwRsDdAKF6/dG932OleYKbTqBSJ7VvZK0JbPKeEpIYxoniUhgvgyZjO4vlCd4mFTdw==", + "dev": true, + "dependencies": { + "@types/highlight.js": "^9.7.0", "@types/linkify-it": "*", "@types/mdurl": "*" } @@ -1875,6 +2983,12 @@ "uri-js": "^4.2.2" } }, + "node_modules/ansi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", + "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=", + "peer": true + }, "node_modules/ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -2255,6 +3369,12 @@ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "peer": true + }, "node_modules/async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", @@ -2284,6 +3404,97 @@ "node": ">= 4.5.0" } }, + "node_modules/aws-crt": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/aws-crt/-/aws-crt-1.10.2.tgz", + "integrity": "sha512-Ub4sXoI5TriNGxH2Sc3HKNDTY51Nm7YjN5M5Pdo9Cb8cNAlEDtZhYZBZbfqZVauYA/cD1Zufw6x3bQSxDrYtaQ==", + "hasInstallScript": true, + "peer": true, + "dependencies": { + "axios": "^0.21.4", + "cmake-js": "6.1.0", + "crypto-js": "^4.0.0", + "fastestsmallesttextencoderdecoder": "^1.0.22", + "mqtt": "^4.2.8", + "tar": "^6.1.11", + "websocket-stream": "^5.5.2" + } + }, + "node_modules/aws-crt/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-crt/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aws-crt/node_modules/minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-crt/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "peer": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aws-crt/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-crt/node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "peer": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -2297,6 +3508,15 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "peer": true, + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/babel-jest": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", @@ -2497,6 +3717,26 @@ "node": ">=0.10.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true + }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -2510,6 +3750,28 @@ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" }, + "node_modules/big-integer": { + "version": "1.6.50", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.50.tgz", + "integrity": "sha512-+O2uoQWFRo8ysZNo/rjtri2jIwjr3XfeAgRjAUADRqGG+ZITvyn8J1kvXLTaKVr3hhGXk+f23tKfdzmklVM9vQ==", + "peer": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "peer": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", @@ -2528,6 +3790,42 @@ "file-uri-to-path": "1.0.0" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "peer": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "peer": true + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, "node_modules/boxen": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", @@ -2712,6 +4010,30 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", @@ -2724,8 +4046,22 @@ "node_modules/buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "peer": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", + "peer": true }, "node_modules/buffer-writer": { "version": "2.0.0", @@ -2735,6 +4071,15 @@ "node": ">=4" } }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "peer": true, + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/bulma": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.9.1.tgz", @@ -2856,6 +4201,18 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "peer": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -3159,38 +4516,189 @@ "node": ">=0.8" } }, - "node_modules/clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true, - "engines": { - "node": ">= 0.10" + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/cmake-js": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-6.1.0.tgz", + "integrity": "sha512-utmukLQftpgrCpGRCaHnkv4K27HZNNFqmBl4vnvccy0xp4c1erxjFU/Lq4wn5ngAhFZmpwBPQfoKWKThjSBiwg==", + "peer": true, + "dependencies": { + "debug": "^4", + "fs-extra": "^5.0.0", + "is-iojs": "^1.0.1", + "lodash": "^4", + "memory-stream": "0", + "npmlog": "^1.2.0", + "rc": "^1.2.7", + "request": "^2.54.0", + "semver": "^5.0.3", + "splitargs": "0", + "tar": "^4", + "unzipper": "^0.8.13", + "url-join": "0", + "which": "^1.0.9", + "yargs": "^3.6.0" + }, + "bin": { + "cmake-js": "bin/cmake-js" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cmake-js/node_modules/are-we-there-yet": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz", + "integrity": "sha1-otKMkxAqpsyWJFomy5VN4G7FPww=", + "peer": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.0 || ^1.1.13" + } + }, + "node_modules/cmake-js/node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cmake-js/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "peer": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/cmake-js/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/cmake-js/node_modules/fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/cmake-js/node_modules/gauge": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", + "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", + "peer": true, + "dependencies": { + "ansi": "^0.3.0", + "has-unicode": "^2.0.0", + "lodash.pad": "^4.1.0", + "lodash.padend": "^4.1.0", + "lodash.padstart": "^4.1.0" + } + }, + "node_modules/cmake-js/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "peer": true + }, + "node_modules/cmake-js/node_modules/npmlog": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-1.2.1.tgz", + "integrity": "sha1-KOe+YZYJtT960d0wChDWTXFiaLY=", + "peer": true, + "dependencies": { + "ansi": "~0.3.0", + "are-we-there-yet": "~1.0.0", + "gauge": "~1.2.0" } }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "node_modules/cmake-js/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "peer": true, "dependencies": { - "mimic-response": "^1.0.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true + "node_modules/cmake-js/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "peer": true }, - "node_modules/cloneable-readable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "dev": true, + "node_modules/cmake-js/node_modules/yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "peer": true, "dependencies": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" } }, "node_modules/co": { @@ -3293,6 +4801,25 @@ "node": ">= 6" } }, + "node_modules/commist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", + "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", + "peer": true, + "dependencies": { + "leven": "^2.1.0", + "minimist": "^1.1.0" + } + }, + "node_modules/commist/node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/compare-versions": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", @@ -3474,6 +5001,12 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==", + "peer": true + }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -3558,7 +5091,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3801,6 +5333,15 @@ "node": ">=8" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "peer": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, "node_modules/duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -3810,7 +5351,6 @@ "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", @@ -4383,6 +5923,24 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz", + "integrity": "sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg==", + "bin": { + "xml2js": "cli.js" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + }, + "node_modules/fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==", + "peer": true + }, "node_modules/fb-watchman": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", @@ -4501,6 +6059,26 @@ "readable-stream": "^2.3.6" } }, + "node_modules/follow-redirects": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", + "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "peer": true, + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -4624,6 +6202,21 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5277,6 +6870,30 @@ "node": ">=8" } }, + "node_modules/help-me": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", + "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", + "peer": true, + "dependencies": { + "glob": "^7.1.6", + "readable-stream": "^3.6.0" + } + }, + "node_modules/help-me/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -5404,6 +7021,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -5480,7 +7117,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5711,6 +7347,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-iojs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-iojs/-/is-iojs-1.1.0.tgz", + "integrity": "sha1-TBEDO11dlNbqs3dd7cm+fQCDJfE=", + "peer": true + }, "node_modules/is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -5874,8 +7516,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "node_modules/isobject": { "version": "3.0.1", @@ -8054,7 +9695,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, "dependencies": { "invert-kv": "^1.0.0" }, @@ -8129,6 +9769,12 @@ "uc.micro": "^1.0.1" } }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", + "peer": true + }, "node_modules/load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -8169,8 +9815,25 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=", + "peer": true + }, + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "peer": true + }, + "node_modules/lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=", + "peer": true }, "node_modules/lodash.sortby": { "version": "4.7.0", @@ -8325,6 +9988,39 @@ "node": ">= 0.6" } }, + "node_modules/memory-stream": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", + "integrity": "sha1-6+jdHDuLw4wOeUHp3dWuvmtN6D8=", + "peer": true, + "dependencies": { + "readable-stream": "~1.0.26-2" + } + }, + "node_modules/memory-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "peer": true + }, + "node_modules/memory-stream/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/memory-stream/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "peer": true + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -8502,6 +10198,155 @@ "node": "*" } }, + "node_modules/mqtt": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", + "integrity": "sha512-DJYjlXODVXtSDecN8jnNzi6ItX3+ufGsEs9OB3YV24HtkRrh7kpx8L5M1LuyF0KzaiGtWr2PzDcMGAY60KGOSA==", + "peer": true, + "dependencies": { + "commist": "^1.0.0", + "concat-stream": "^2.0.0", + "debug": "^4.1.1", + "duplexify": "^4.1.1", + "help-me": "^3.0.0", + "inherits": "^2.0.3", + "minimist": "^1.2.5", + "mqtt-packet": "^6.8.0", + "pump": "^3.0.0", + "readable-stream": "^3.6.0", + "reinterval": "^1.1.0", + "split2": "^3.1.0", + "ws": "^7.5.0", + "xtend": "^4.0.2" + }, + "bin": { + "mqtt": "bin/mqtt.js", + "mqtt_pub": "bin/pub.js", + "mqtt_sub": "bin/sub.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", + "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", + "peer": true, + "dependencies": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt-packet/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mqtt-packet/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "peer": true + }, + "node_modules/mqtt/node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/mqtt/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mqtt/node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "peer": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/mqtt/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "peer": true + }, + "node_modules/mqtt/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mqtt/node_modules/ws": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -9150,7 +10995,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, "dependencies": { "lcid": "^1.0.0" }, @@ -10031,6 +11875,12 @@ "node": ">=8" } }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=", + "peer": true + }, "node_modules/remove-bom-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", @@ -10472,6 +12322,12 @@ "node": ">=0.10.0" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "peer": true + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -10798,6 +12654,12 @@ "node": ">= 6" } }, + "node_modules/splitargs": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", + "integrity": "sha1-/p965lc3GzOxDLgNoUPPgknPazs=", + "peer": true + }, "node_modules/sqlite3": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.0.tgz", @@ -10914,8 +12776,7 @@ "node_modules/stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, "node_modules/strict-uri-encode": { "version": "2.0.0", @@ -11379,6 +13240,20 @@ "node": ">=8" } }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -11445,8 +13320,7 @@ "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", @@ -11487,6 +13361,12 @@ "node": ">=0.8.0" } }, + "node_modules/ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "peer": true + }, "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -11640,6 +13520,50 @@ "node": ">=0.10.0" } }, + "node_modules/unzipper": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", + "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", + "peer": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "~1.0.10", + "listenercount": "~1.0.1", + "readable-stream": "~2.1.5", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "peer": true + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", + "peer": true, + "dependencies": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "peer": true + }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -11752,6 +13676,12 @@ "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, + "node_modules/url-join": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", + "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g=", + "peer": true + }, "node_modules/url-parse-lax": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", @@ -11962,6 +13892,31 @@ "node": ">=10.4" } }, + "node_modules/websocket-stream": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.2.tgz", + "integrity": "sha512-8z49MKIHbGk3C4HtuHWDtYX8mYej1wWabjthC/RupM9ngeukU4IWoM46dgth1UOS/T4/IqgEdCDJuMe2039OQQ==", + "peer": true, + "dependencies": { + "duplexify": "^3.5.1", + "inherits": "^2.0.1", + "readable-stream": "^2.3.3", + "safe-buffer": "^5.1.2", + "ws": "^3.2.0", + "xtend": "^4.0.0" + } + }, + "node_modules/websocket-stream/node_modules/ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "peer": true, + "dependencies": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, "node_modules/whatwg-encoding": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", @@ -12007,7 +13962,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -12026,34 +13980,301 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dependencies": { - "string-width": "^1.0.2 || 2" + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/widest-line/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", + "peer": true, + "bin": { + "window-size": "cli.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", + "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "peer": true + }, + "node_modules/yargs": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "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": ">=12" } }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dependencies": { - "string-width": "^4.0.0" - }, + "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": ">=8" + "node": ">=10" } }, - "node_modules/widest-line/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { "node": ">=8" } }, - "node_modules/widest-line/node_modules/emoji-regex": { + "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "node_modules/widest-line/node_modules/is-fullwidth-code-point": { + "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", @@ -12061,294 +14282,979 @@ "node": ">=8" } }, - "node_modules/widest-line/node_modules/string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/widest-line/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/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, + "node_modules/ylru": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz", + "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==", "engines": { - "node": ">=0.10.0" + "node": ">= 4.0.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true + "node_modules/zxcvbn": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", + "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" + } + }, + "dependencies": { + "@aws-crypto/crc32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-2.0.0.tgz", + "integrity": "sha512-TvE1r2CUueyXOuHdEigYjIZVesInd9KN+K/TFFNfkkxRThiNxO6i4ZqqAVMoEjAamZZ1AA8WXJkjCz7YShHPQA==", + "requires": { + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "@aws-crypto/ie11-detection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz", + "integrity": "sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA==", + "requires": { + "tslib": "^1.11.1" + }, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha256-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz", + "integrity": "sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A==", + "requires": { + "@aws-crypto/ie11-detection": "^2.0.0", + "@aws-crypto/sha256-js": "^2.0.0", + "@aws-crypto/supports-web-crypto": "^2.0.0", + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/sha256-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz", + "integrity": "sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig==", + "requires": { + "@aws-crypto/util": "^2.0.0", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" }, - "engines": { - "node": ">=10" + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz", + "integrity": "sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA==", + "requires": { + "tslib": "^1.11.1" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" + "@aws-crypto/util": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.0.tgz", + "integrity": "sha512-YDooyH83m2P5A3h6lNH7hm6mIP93sU/dtzRmXIgtO4BCB7SvtX8ysVKQAE8tVky2DQ3HHxPCjNTuUe7YoAMrNQ==", + "requires": { + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@aws-sdk/abort-controller": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.40.0.tgz", + "integrity": "sha512-S7LzLvNuwuf0q7r4q7zqGzxd/W2xYsn8cpZ90MMb3ObolhbkLySrikUJujmXae8k+2/KFCOr+FVC0YLrATSUgQ==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/chunked-blob-reader": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader/-/chunked-blob-reader-3.37.0.tgz", + "integrity": "sha512-uDacnFaczeO962RnVttwAQddS4rgDfI7nfeY8NV6iZkDv5uxGzHTfH4jT7WvPDM1pSMcOMDx8RJ+Tmtsd1VTsA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@aws-sdk/chunked-blob-reader-native": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader-native/-/chunked-blob-reader-native-3.37.0.tgz", + "integrity": "sha512-h9OYq6EvDrpb7SKod+Kow+d3aRNFVBYR1a8G8ahEDDQe3AtmA2Smyvni4kt/ABTiKvYdof2//Pq3BL/IUV9n9Q==", + "requires": { + "@aws-sdk/util-base64-browser": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/client-s3": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.40.0.tgz", + "integrity": "sha512-J7dUG3fZKEx/Mxyik373HIx0XmbIu+ZrHO9Eyea2CxZRTrEdUZiran2IdX5cafLULkUuOcQV6v2S4UZzsAmueA==", + "requires": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/client-sts": "3.40.0", + "@aws-sdk/config-resolver": "3.40.0", + "@aws-sdk/credential-provider-node": "3.40.0", + "@aws-sdk/eventstream-serde-browser": "3.40.0", + "@aws-sdk/eventstream-serde-config-resolver": "3.40.0", + "@aws-sdk/eventstream-serde-node": "3.40.0", + "@aws-sdk/fetch-http-handler": "3.40.0", + "@aws-sdk/hash-blob-browser": "3.40.0", + "@aws-sdk/hash-node": "3.40.0", + "@aws-sdk/hash-stream-node": "3.40.0", + "@aws-sdk/invalid-dependency": "3.40.0", + "@aws-sdk/md5-js": "3.40.0", + "@aws-sdk/middleware-apply-body-checksum": "3.40.0", + "@aws-sdk/middleware-bucket-endpoint": "3.40.0", + "@aws-sdk/middleware-content-length": "3.40.0", + "@aws-sdk/middleware-expect-continue": "3.40.0", + "@aws-sdk/middleware-host-header": "3.40.0", + "@aws-sdk/middleware-location-constraint": "3.40.0", + "@aws-sdk/middleware-logger": "3.40.0", + "@aws-sdk/middleware-retry": "3.40.0", + "@aws-sdk/middleware-sdk-s3": "3.40.0", + "@aws-sdk/middleware-serde": "3.40.0", + "@aws-sdk/middleware-signing": "3.40.0", + "@aws-sdk/middleware-ssec": "3.40.0", + "@aws-sdk/middleware-stack": "3.40.0", + "@aws-sdk/middleware-user-agent": "3.40.0", + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/node-http-handler": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/smithy-client": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/url-parser": "3.40.0", + "@aws-sdk/util-base64-browser": "3.37.0", + "@aws-sdk/util-base64-node": "3.37.0", + "@aws-sdk/util-body-length-browser": "3.37.0", + "@aws-sdk/util-body-length-node": "3.37.0", + "@aws-sdk/util-user-agent-browser": "3.40.0", + "@aws-sdk/util-user-agent-node": "3.40.0", + "@aws-sdk/util-utf8-browser": "3.37.0", + "@aws-sdk/util-utf8-node": "3.37.0", + "@aws-sdk/util-waiter": "3.40.0", + "@aws-sdk/xml-builder": "3.37.0", + "entities": "2.2.0", + "fast-xml-parser": "3.19.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + } + } + }, + "@aws-sdk/client-sso": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.40.0.tgz", + "integrity": "sha512-eFQ4yFg8RlPaldv/ja2K3pUUyXauGbo4GJPlbPKYoquwW785au8qECKSl3iqBmUklj6WmdW1rmtlQk2OUcyYSw==", + "requires": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/config-resolver": "3.40.0", + "@aws-sdk/fetch-http-handler": "3.40.0", + "@aws-sdk/hash-node": "3.40.0", + "@aws-sdk/invalid-dependency": "3.40.0", + "@aws-sdk/middleware-content-length": "3.40.0", + "@aws-sdk/middleware-host-header": "3.40.0", + "@aws-sdk/middleware-logger": "3.40.0", + "@aws-sdk/middleware-retry": "3.40.0", + "@aws-sdk/middleware-serde": "3.40.0", + "@aws-sdk/middleware-stack": "3.40.0", + "@aws-sdk/middleware-user-agent": "3.40.0", + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/node-http-handler": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/smithy-client": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/url-parser": "3.40.0", + "@aws-sdk/util-base64-browser": "3.37.0", + "@aws-sdk/util-base64-node": "3.37.0", + "@aws-sdk/util-body-length-browser": "3.37.0", + "@aws-sdk/util-body-length-node": "3.37.0", + "@aws-sdk/util-user-agent-browser": "3.40.0", + "@aws-sdk/util-user-agent-node": "3.40.0", + "@aws-sdk/util-utf8-browser": "3.37.0", + "@aws-sdk/util-utf8-node": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/client-sts": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.40.0.tgz", + "integrity": "sha512-7jBlb1uyq2c0bFqi4ZVnEMNzLTodvIZKoxjh1LYA8OZISbMsDjTxFOYOmuyOhuPy0fLfLL3KRLtengJ23zs3QQ==", + "requires": { + "@aws-crypto/sha256-browser": "2.0.0", + "@aws-crypto/sha256-js": "2.0.0", + "@aws-sdk/config-resolver": "3.40.0", + "@aws-sdk/credential-provider-node": "3.40.0", + "@aws-sdk/fetch-http-handler": "3.40.0", + "@aws-sdk/hash-node": "3.40.0", + "@aws-sdk/invalid-dependency": "3.40.0", + "@aws-sdk/middleware-content-length": "3.40.0", + "@aws-sdk/middleware-host-header": "3.40.0", + "@aws-sdk/middleware-logger": "3.40.0", + "@aws-sdk/middleware-retry": "3.40.0", + "@aws-sdk/middleware-sdk-sts": "3.40.0", + "@aws-sdk/middleware-serde": "3.40.0", + "@aws-sdk/middleware-signing": "3.40.0", + "@aws-sdk/middleware-stack": "3.40.0", + "@aws-sdk/middleware-user-agent": "3.40.0", + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/node-http-handler": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/smithy-client": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/url-parser": "3.40.0", + "@aws-sdk/util-base64-browser": "3.37.0", + "@aws-sdk/util-base64-node": "3.37.0", + "@aws-sdk/util-body-length-browser": "3.37.0", + "@aws-sdk/util-body-length-node": "3.37.0", + "@aws-sdk/util-user-agent-browser": "3.40.0", + "@aws-sdk/util-user-agent-node": "3.40.0", + "@aws-sdk/util-utf8-browser": "3.37.0", + "@aws-sdk/util-utf8-node": "3.37.0", + "entities": "2.2.0", + "fast-xml-parser": "3.19.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + } + } + }, + "@aws-sdk/config-resolver": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.40.0.tgz", + "integrity": "sha512-QYy6J2k31QL6J74hPBfptnLW1kQYdN+xjwH4UQ1mv7EUhRoJN9ZY2soStJowFy4at6IIOOVWbyG5dyqvrbEovg==", + "requires": { + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-config-provider": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.40.0.tgz", + "integrity": "sha512-qHZdf2vxhzZkSygjw2I4SEYFL2dMZxxYvO4QlkqQouKY81OVxs/j69oiNCjPasQzGz5jaZZKI8xEAIfkSyr1lg==", + "requires": { + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/credential-provider-imds": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.40.0.tgz", + "integrity": "sha512-Ty/wVa+BQrCFrP06AGl5S1CeLifDt68YrlYXUnkRn603SX4DvxBgVO7XFeDH58G8ziDCiqxfmVl4yjbncPPeSw==", + "requires": { + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/url-parser": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.40.0.tgz", + "integrity": "sha512-lyTlgItJ+wPWIkcnkpmZTG+ApCwZBDjLzCPzhFOG1vT1wb0pF3KyJGmjWaW9C6s84rvWwGv1bY3/KBo92KtcjA==", + "requires": { + "@aws-sdk/credential-provider-env": "3.40.0", + "@aws-sdk/credential-provider-imds": "3.40.0", + "@aws-sdk/credential-provider-sso": "3.40.0", + "@aws-sdk/credential-provider-web-identity": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-credentials": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.40.0.tgz", + "integrity": "sha512-TANFmUqZwXd2ytA4Ji8IJDC8g42EnogjeIX+ypea/sImY5L7sQpd/sxQlcpIOJlr/6cGL3VhLGh2EGHXEJQEYA==", + "requires": { + "@aws-sdk/credential-provider-env": "3.40.0", + "@aws-sdk/credential-provider-imds": "3.40.0", + "@aws-sdk/credential-provider-ini": "3.40.0", + "@aws-sdk/credential-provider-process": "3.40.0", + "@aws-sdk/credential-provider-sso": "3.40.0", + "@aws-sdk/credential-provider-web-identity": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-credentials": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.40.0.tgz", + "integrity": "sha512-qsaNCDesW2GasDbzpeOA371gxugi05JWxt3EKonLbUfkGKBK7kmmL6EgLIxZuNm2/Ve4RS07PKp8yBGm4xIx9w==", + "requires": { + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-credentials": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.40.0.tgz", + "integrity": "sha512-8XOz1cDsRvmb6UyLKersi+kLx2Bo4nXpsLZDbTuobEqMwtzIIZKW3C8n8icKpiqq1xhJ6hyT80on+HJ8ykrFKA==", + "requires": { + "@aws-sdk/client-sso": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-credentials": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.40.0.tgz", + "integrity": "sha512-A1KT6Ft3k5B6bU2I2jXS4fSoWbWftEysrxT3zyuzhMbsstsHBJ/J9mEsQ4lgZyr6DXEqn7HD3MbdEoaBN2b3sg==", + "requires": { + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/eventstream-marshaller": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-marshaller/-/eventstream-marshaller-3.40.0.tgz", + "integrity": "sha512-zHGchfkG3B9M8OOKRpByeS5g1/15YQ0+QQHwxQRtm/CPtKBAIAsCZRQaCNBLu9uQMtBBKj5JsDUcjirhGeSvIg==", + "requires": { + "@aws-crypto/crc32": "2.0.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-hex-encoding": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/eventstream-serde-browser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.40.0.tgz", + "integrity": "sha512-V0AXAfSkhY0hgxDJ0cNA+r42kL8295U7UTCp2Q2fvCaob3wKWh+54KZ2L4IOYTlK3yNzXJ5V6PP1zUuRlsUTew==", + "requires": { + "@aws-sdk/eventstream-marshaller": "3.40.0", + "@aws-sdk/eventstream-serde-universal": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/eventstream-serde-config-resolver": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.40.0.tgz", + "integrity": "sha512-GgGiJBsQ1/SBTpRM/wCdFBCMo1Nybvy46bNVkH1ujCdp8UTLc5PozzNpH+15V2IQbc9sPDYffMab6HSFjDp5vw==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/eventstream-serde-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.40.0.tgz", + "integrity": "sha512-CnzX/JZGvhWlg+ooIPVZ78T+5wIm5Ld1BD7jwhlptJa8IjTMvkc8Nh4pAhc7T0ZScy4zZa/oTkqeVYCOVCyd1Q==", + "requires": { + "@aws-sdk/eventstream-marshaller": "3.40.0", + "@aws-sdk/eventstream-serde-universal": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/eventstream-serde-universal": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.40.0.tgz", + "integrity": "sha512-rkHwVMyZJMhp9iBixkuaAGQNer/DPxZ9kxDDtE+LuAMhepTYQ8c4lUW0QQhYbNMWf48QKD1G4FV3JXIj9JfP9A==", + "requires": { + "@aws-sdk/eventstream-marshaller": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/fetch-http-handler": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.40.0.tgz", + "integrity": "sha512-w1HiZromoU+/bbEo89uO81l6UO/M+c2uOMnXntZqe6t3ZHUUUo3AbvhKh0QGVFqRQa+Oi0+95KqWmTHa72/9Iw==", + "requires": { + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/querystring-builder": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-base64-browser": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/hash-blob-browser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.40.0.tgz", + "integrity": "sha512-l8xyprVVKKH+720VrQ677X6VkvHttDXB4MxkMuxhSvwYBQwsRzP2Wppo7xIAtWGoS+oqlLmD4LCbHdhFRcN5yA==", + "requires": { + "@aws-sdk/chunked-blob-reader": "3.37.0", + "@aws-sdk/chunked-blob-reader-native": "3.37.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/hash-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.40.0.tgz", + "integrity": "sha512-yOXXK85DdGDktdnQtXgMdaVKii4wtMjEhJ1mrvx2A9nMFNaPhxvERkVVIUKSWlJRa9ZujOw5jWOx8d2R51/Kjg==", + "requires": { + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-buffer-from": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/hash-stream-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-stream-node/-/hash-stream-node-3.40.0.tgz", + "integrity": "sha512-4yvRwODMGYtj6qrt+fyydV5MwVwPPoyoeqDoXdLo9x75vRY71DT1pMRt8PDOoY/ZwWbIdEt4+V7x0sLt2uy9WA==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/invalid-dependency": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.40.0.tgz", + "integrity": "sha512-axIWtDwCBDDqEgAJipX1FB1ZNpWYXquVwKDMo+7G+ftPBZ4FEq4M1ELhXJL3hhNJ9ZmCQzv+4F6Wnt8dwuzUaQ==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/is-array-buffer": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.37.0.tgz", + "integrity": "sha512-XLjA/a6AuGnCvcJZLsMTy2jxF2upgGhqCCkoIJgLlzzXHSihur13KcmPvW/zcaGnCRj0SvKWXiJHl4vDlW75VQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@aws-sdk/md5-js": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/md5-js/-/md5-js-3.40.0.tgz", + "integrity": "sha512-P1tzEljMD/MkjSc00TkVBYvfaVv/7S+04YEwE7tpu/jtxWxMHnk3CMKqq/F2iMhY83DRoqoYy+YqnaF4Bzr1uA==", + "requires": { + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-utf8-browser": "3.37.0", + "@aws-sdk/util-utf8-node": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-apply-body-checksum": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-apply-body-checksum/-/middleware-apply-body-checksum-3.40.0.tgz", + "integrity": "sha512-gNSFlFu/O8cxAM0X64OwiLLN/NPXvK3FsAIJRsfhIW+dX0bEq4lsGPsdU8Tx+9eenaj/Z01uqgWZ6Izar8zVvQ==", + "requires": { + "@aws-sdk/is-array-buffer": "3.37.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-bucket-endpoint": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.40.0.tgz", + "integrity": "sha512-y9m1wF45aL1yuDWe396O2BPiUsWJz4BKmPjIuz+YvvHMzI1eu32IVqA7NXpQ7Y5J9GYEBn1LA5zmYMMbzpHilA==", + "requires": { + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-arn-parser": "3.37.0", + "@aws-sdk/util-config-provider": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-content-length": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.40.0.tgz", + "integrity": "sha512-sybAJb8v7I/vvL08R3+TI/XDAg9gybQTZ2treC24Ap4+jAOz4QBTHJPMKaUlEeFlMUcq4rj6/u2897ebYH6opw==", + "requires": { + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-expect-continue": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.40.0.tgz", + "integrity": "sha512-FY6vT0u1ptDZ2bBj1yG/Iyk6HZB7U9fbrpeZNPYzgq8HJxBcTgfLwtB3VLobyhThQm9X2a7R2YZrwtArW8yQfQ==", + "requires": { + "@aws-sdk/middleware-header-default": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-header-default": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-header-default/-/middleware-header-default-3.40.0.tgz", + "integrity": "sha512-eXQ13x/AivPZKoG8/akp9g5xdNHuKftl83GMuk9K6tt4+eAa22TdxiFu4R0UVlKAvo2feqxFrNs5DhhhBeAQWA==", + "requires": { + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.40.0.tgz", + "integrity": "sha512-/wocR7JFOLM7/+BQM1DgAd6KCFYcdxYu1P7AhI451GlVNuYa5f89zh7p0gt3SRC6monI5lXgpL7RudhDm8fTrA==", + "requires": { + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-location-constraint": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.40.0.tgz", + "integrity": "sha512-9XaVPYsDQVJbWJH96sNdv4HHY3j1raman+lYxMu4528Awp0OdWUeSsGRYRN+CnRPlkHnfNw4m6SKdWYHxdjshw==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.40.0.tgz", + "integrity": "sha512-19kx0Xg5ymVRKoupmhdmfTBkROcv3DZj508agpyG2YAo0abOObMlIP4Jltg0VD4PhNjGzNh0jFGJnvhjdwv4/A==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-retry": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.40.0.tgz", + "integrity": "sha512-SMUJrukugLL7YJE5X8B2ToukxMWMPwnf7jAFr84ptycCe8bdWv8x8klQ3EtVWpyqochtNlbTi6J/tTQBniUX7A==", + "requires": { + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/service-error-classification": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0", + "uuid": "^8.3.2" + } + }, + "@aws-sdk/middleware-sdk-s3": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.40.0.tgz", + "integrity": "sha512-9Z1mGWhCmJqxQYZrg5RIdlMxetGsT4LLV8/yLNxALuOr+GzLoA1r4D2lqz+1lJcDZKphP22xBxoeJG5MS80mig==", + "requires": { + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-arn-parser": "3.37.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-sdk-sts": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.40.0.tgz", + "integrity": "sha512-TcrbCvj1PkabFZiNczT3yePZtuEm2fAIw1OVnQyLcF2KW+p62Hv5YkK4MPOfx3LA/0lzjOUO1RNl2x7gzV443Q==", + "requires": { + "@aws-sdk/middleware-signing": "3.40.0", + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-serde": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.40.0.tgz", + "integrity": "sha512-uOWfZjlAoBy6xPqp0d4ka83WNNbEVCWn9WwfqBUXThyoTdTooYSpXe5y2YzN0BJa8b+tEZTyWpgamnBpFLp47g==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-signing": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.40.0.tgz", + "integrity": "sha512-RqK5nPbfma0qInMvjtpVkDYY/KkFS6EKlOv3DWTdxbXJ4YuOxgKiuUromhmBUoyjFag0JO7LUWod07H+/DawoA==", + "requires": { + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-ssec": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.40.0.tgz", + "integrity": "sha512-ZoRpaZeAIQa1Q+NyEh74ATwOR3nFGfcP6Nu0jFzgqoVijCReMnhtlCRx23ccBu1ZLZNUsNk6MhKjY+ZTfNsjEg==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-stack": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.40.0.tgz", + "integrity": "sha512-hby9HvESUYJxpdALX+6Dn2LPmS5jtMVurGB/+j3MWOvIcDYB4bcSXgVRvXzYnTKwbSupIdbX9zOE2ZAx2SJpUQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.40.0.tgz", + "integrity": "sha512-dzC2fxWnanetFJ1oYgil8df3N36bR1yc/OCOpbdfQNiUk1FrXiCXqH5rHNO8zCvnwJAj8GHFwpFGd9a2Qube2w==", + "requires": { + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/node-config-provider": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.40.0.tgz", + "integrity": "sha512-AmokjgUDECG8osoMfdRsPNweqI+L1pn4bYGk5iTLmzbBi0o4ot0U1FdX8Rf0qJZZwS4t1TXc3s8/PDVknmPxKg==", + "requires": { + "@aws-sdk/property-provider": "3.40.0", + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/node-http-handler": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.40.0.tgz", + "integrity": "sha512-qjda6IbxDhbYr8NHmrMurKkbjgLUkfTMVgagDErDK24Nm3Dn5VaO6J4n6c0Q4OLHlmFaRcUfZSTrOo5DAubqCw==", + "requires": { + "@aws-sdk/abort-controller": "3.40.0", + "@aws-sdk/protocol-http": "3.40.0", + "@aws-sdk/querystring-builder": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/property-provider": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.40.0.tgz", + "integrity": "sha512-Mx4lkShjsYRwW9ujHA1pcnuubrWQ4kF5/DXWNfUiXuSIO/0Lojp1qTLheyBm4vzkJIlx5umyP6NvRAUkEHSN4Q==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/protocol-http": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.40.0.tgz", + "integrity": "sha512-f4ea7/HZkjpvGBrnRIuzc/bhrExWrgDv7eulj4htPukZGHdTqSJD3Jk8lEXWvFuX2vUKQDGhEhCDsqup7YWJQQ==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } + }, + "@aws-sdk/querystring-builder": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.40.0.tgz", + "integrity": "sha512-gO24oipnNaxJRBXB7lhLfa96vIMOd8gtMBqJTjelTjS2e1ZP1YY12CNKKTWwafSk8Ge021erZAG/YTOaXGpv+g==", + "requires": { + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-uri-escape": "3.37.0", + "tslib": "^2.3.0" } }, - "node_modules/wrap-ansi/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" + "@aws-sdk/querystring-parser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.40.0.tgz", + "integrity": "sha512-XZIyaKQIiZAM6zelCBcsLHhVDOLafi7XIOd3jy6SymGN8ajj3HqUJ/vdQ5G6ISTk18OrqgqcCOI9oNzv+nrBcA==", + "requires": { + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" } }, - "node_modules/wrap-ansi/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" + "@aws-sdk/service-error-classification": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.40.0.tgz", + "integrity": "sha512-c8btKmkvjXczWudXubGdbO3JgmjySBUVC/gCrZDNfwNGsG8RYJJQYYcnmt1gWjelUZsgMDl/2PIzxTlxVF91rA==" + }, + "@aws-sdk/shared-ini-file-loader": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.37.0.tgz", + "integrity": "sha512-+vRBSlfa48R9KL7DpQt3dsu5/+5atjRgoCISblWo3SLpjrx41pKcjKneo7a1u0aP1Xc2oG2TfIyqTWZuOXsmEQ==", + "requires": { + "tslib": "^2.3.0" } }, - "node_modules/wrap-ansi/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==" + "@aws-sdk/signature-v4": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.40.0.tgz", + "integrity": "sha512-Q1GNZJRCS3W2qsRtDsX/b6EOSfMXfr6TW46N3LnLTGYZ3KAN2SOSJ1DsW59AuGpEZyRmOhJ9L/Q5U403+bZMXQ==", + "requires": { + "@aws-sdk/is-array-buffer": "3.37.0", + "@aws-sdk/types": "3.40.0", + "@aws-sdk/util-hex-encoding": "3.37.0", + "@aws-sdk/util-uri-escape": "3.37.0", + "tslib": "^2.3.0" + } }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "@aws-sdk/signature-v4-crt": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-crt/-/signature-v4-crt-3.40.0.tgz", + "integrity": "sha512-J0KeUe2pZNolgC/4dYkkaXAYgcJ7OZEfN7+ezCJJ3f1t0X+NVZoh7/9iBR6Kq1x/LzpGy67MdKMXDIdBT0DmNw==", + "peer": true, + "requires": { + "@aws-sdk/is-array-buffer": "3.37.0", + "@aws-sdk/querystring-parser": "3.40.0", + "@aws-sdk/signature-v4": "3.40.0", + "@aws-sdk/util-hex-encoding": "3.37.0", + "@aws-sdk/util-uri-escape": "3.37.0", + "aws-crt": "^1.9.7", + "tslib": "^2.3.0" + } }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" + "@aws-sdk/smithy-client": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.40.0.tgz", + "integrity": "sha512-6x6uvmfhFpkCiT1O/SsFWRyyqs3ZHMB1hVypn9XfT1/XSrfVLhcbBtnX1/UGPkQvA1GJGo5Pkxv3odQfUw7rUg==", + "requires": { + "@aws-sdk/middleware-stack": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" } }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "@aws-sdk/types": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.40.0.tgz", + "integrity": "sha512-KpILcfvRaL88TLvo3SY4OuCCg90SvcNLPyjDwUuBqiOyWODjrKShHtAPJzej4CLp92lofh+ul0UnBfV9Jb/5PA==" + }, + "@aws-sdk/url-parser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.40.0.tgz", + "integrity": "sha512-HwNV+HX7bHgLk5FzTOgdXANsC0SeVz5PMC4Nh+TLz2IoeQnrw4H8dsA4YNonncjern5oC5veKRjQeOoCL5SlSQ==", + "requires": { + "@aws-sdk/querystring-parser": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" } }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "@aws-sdk/util-arn-parser": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.37.0.tgz", + "integrity": "sha512-njIYn8gzm7Ms17A2oEu0vN/0GJpgq7cNFFtzBrM1cPtrc1jhMRJx5hzS7uX5h6ll8BM92bA3y00evRZFHxQPVQ==", + "requires": { + "tslib": "^2.3.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "@aws-sdk/util-base64-browser": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.37.0.tgz", + "integrity": "sha512-o4s/rHVm5k8eC/T7grJQINyYA/mKfDmEWKMA9wk5iBroXlI2rUm7x649TBk5hzoddufk/mffEeNz/1wM7yTmlg==", + "requires": { + "tslib": "^2.3.0" + } }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "@aws-sdk/util-base64-node": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.37.0.tgz", + "integrity": "sha512-1UPxly1GPrGZtlIWvbNCDIAund4Oyp8cFi9neA43TeNACvrmEQu/nG01pDbOoo0ENoVSVJrNAVBeqKEpqjH2GA==", + "requires": { + "@aws-sdk/util-buffer-from": "3.37.0", + "tslib": "^2.3.0" } }, - "node_modules/ws": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", - "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "@aws-sdk/util-body-length-browser": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.37.0.tgz", + "integrity": "sha512-tClmH1uYelqWT43xxmnOsVFbCQJiIwizp6y4E109G2LIof07inxrO0L8nbwBpjhugVplx6NZr9IaqTFqbdM1gA==", + "requires": { + "tslib": "^2.3.0" } }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "engines": { - "node": ">=8" + "@aws-sdk/util-body-length-node": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.37.0.tgz", + "integrity": "sha512-aY3mXdbEajruRi9CHgq/heM89R+Gectj/Xrs1naewmamaN8NJrvjDm3s+cw//lqqSOW903LYHXDgm7wvCzUnFA==", + "requires": { + "tslib": "^2.3.0" } }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true + "@aws-sdk/util-buffer-from": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.37.0.tgz", + "integrity": "sha512-aa3SBwjLwImuJoE4+hxDIWQ9REz3UFb3p7KFPe9qopdXb/yB12RTcbrXVb4whUux4i4mO6KRij0ZNjFZrjrKPg==", + "requires": { + "@aws-sdk/is-array-buffer": "3.37.0", + "tslib": "^2.3.0" + } }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true + "@aws-sdk/util-config-provider": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.40.0.tgz", + "integrity": "sha512-NjZGrA4mqhpr6gkVCAUweurP0Z9d3vFyXJCtulC0BFbpKAnKCf/crSK56NwUaNhAEMCkSuBvjRFzkbfT+HO8bA==", + "requires": { + "tslib": "^2.3.0" + } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" + "@aws-sdk/util-credentials": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-credentials/-/util-credentials-3.37.0.tgz", + "integrity": "sha512-zcLhSZDKgBLhUjSU5HoQpuQiP3v8oE86NmV/tiZVPEaO6YVULEAB2Cfj1hpM/b/JXWzjSHfT06KXT7QUODKS+A==", + "requires": { + "@aws-sdk/shared-ini-file-loader": "3.37.0", + "tslib": "^2.3.0" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" + "@aws-sdk/util-hex-encoding": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.37.0.tgz", + "integrity": "sha512-tn5UpfaeM+rZWqynoNqB8lwtcAXil5YYO3HLGH9himpWAdft/2Z7LK6bsYDpctaAI1WHgMDcL0bw3Id04ZUbhA==", + "requires": { + "tslib": "^2.3.0" } }, - "node_modules/yargs": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", - "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", - "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": ">=12" + "@aws-sdk/util-locate-window": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.37.0.tgz", + "integrity": "sha512-NvDCfOhLLVHp27oGUUs8EVirhz91aX5gdxGS7J/sh5PF0cNN8rwaR1vSLR7BxPmJHMO7NH7i9EwiELfLfYcq6g==", + "requires": { + "tslib": "^2.3.0" } }, - "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" + "@aws-sdk/util-uri-escape": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.37.0.tgz", + "integrity": "sha512-8pKf4YJTELP5lm/CEgYw2atyJBB1RWWqFa0sZx6YJmTlOtLF5G6raUdAi4iDa2hldGt2B6IAdIIyuusT8zeU8Q==", + "requires": { + "tslib": "^2.3.0" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" + "@aws-sdk/util-user-agent-browser": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.40.0.tgz", + "integrity": "sha512-C69sTI26bV2EprTv3DTXu9XP7kD9Wu4YVPBzqztOYArd2GDYw3w+jS8SEg3XRbjAKY/mOPZ2Thw4StjpZlWZiA==", + "requires": { + "@aws-sdk/types": "3.40.0", + "bowser": "^2.11.0", + "tslib": "^2.3.0" } }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "@aws-sdk/util-user-agent-node": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.40.0.tgz", + "integrity": "sha512-cjIzd0hRZFTTh7iLJD6Bciu++Em1iaM1clyG02xRl0JD5DEtDSR1zO02uu+AeM7GSLGOxIvwOkK2j8ySPAOmBA==", + "requires": { + "@aws-sdk/node-config-provider": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" + } }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" + "@aws-sdk/util-utf8-browser": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.37.0.tgz", + "integrity": "sha512-tuiOxzfqet1kKGYzlgpMGfhr64AHJnYsFx2jZiH/O6Yq8XQg43ryjQlbJlim/K/XHGNzY0R+nabeJg34q3Ua1g==", + "requires": { + "tslib": "^2.3.0" } }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "@aws-sdk/util-utf8-node": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.37.0.tgz", + "integrity": "sha512-fUAgd7UTCULL36j9/vnXHxVhxvswnq23mYgTCIT8NQ7wHN30q2a89ym1e9DwGeQkJEBOkOcKLn6nsMsN7YQMDQ==", + "requires": { + "@aws-sdk/util-buffer-from": "3.37.0", + "tslib": "^2.3.0" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "@aws-sdk/util-waiter": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-waiter/-/util-waiter-3.40.0.tgz", + "integrity": "sha512-jdxwNEZdID49ZvyAnxaeNm5w2moIfMLOwj/q6TxKlxYoXMs16FQWkhyfGue0vEASzchS49ewbyt+KBqpT31Ebg==", + "requires": { + "@aws-sdk/abort-controller": "3.40.0", + "@aws-sdk/types": "3.40.0", + "tslib": "^2.3.0" } }, - "node_modules/ylru": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz", - "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==", - "engines": { - "node": ">= 4.0.0" + "@aws-sdk/xml-builder": { + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.37.0.tgz", + "integrity": "sha512-Vf0f4ZQ+IBo/l9wUaTOXLqqQO9b/Ll5yPbg+EhHx8zlHbTHIm89ettkVCGyT/taGagC1X+ZeTK9maX6ymEOBow==", + "requires": { + "tslib": "^2.3.0" } }, - "node_modules/zxcvbn": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz", - "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" - } - }, - "dependencies": { "@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -13869,6 +16775,12 @@ "uri-js": "^4.2.2" } }, + "ansi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", + "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=", + "peer": true + }, "ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -14168,6 +17080,12 @@ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "peer": true + }, "async-settle": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", @@ -14188,6 +17106,77 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "aws-crt": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/aws-crt/-/aws-crt-1.10.2.tgz", + "integrity": "sha512-Ub4sXoI5TriNGxH2Sc3HKNDTY51Nm7YjN5M5Pdo9Cb8cNAlEDtZhYZBZbfqZVauYA/cD1Zufw6x3bQSxDrYtaQ==", + "peer": true, + "requires": { + "axios": "^0.21.4", + "cmake-js": "6.1.0", + "crypto-js": "^4.0.0", + "fastestsmallesttextencoderdecoder": "^1.0.22", + "mqtt": "^4.2.8", + "tar": "^6.1.11", + "websocket-stream": "^5.5.2" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "peer": true + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "peer": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "peer": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "peer": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "peer": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + } + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -14198,6 +17187,15 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "peer": true, + "requires": { + "follow-redirects": "^1.14.0" + } + }, "babel-jest": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", @@ -14360,6 +17358,12 @@ } } }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "peer": true + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -14373,6 +17377,22 @@ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" }, + "big-integer": { + "version": "1.6.50", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.50.tgz", + "integrity": "sha512-+O2uoQWFRo8ysZNo/rjtri2jIwjr3XfeAgRjAUADRqGG+ZITvyn8J1kvXLTaKVr3hhGXk+f23tKfdzmklVM9vQ==", + "peer": true + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "peer": true, + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, "binary-extensions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", @@ -14385,9 +17405,44 @@ "dev": true, "optional": true, "requires": { - "file-uri-to-path": "1.0.0" + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "peer": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "peer": true + }, + "bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" + }, "boxen": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", @@ -14534,6 +17589,16 @@ "node-int64": "^0.4.0" } }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "peer": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "buffer-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", @@ -14543,14 +17608,31 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "peer": true + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", + "peer": true }, "buffer-writer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "peer": true + }, "bulma": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.9.1.tgz", @@ -14647,6 +17729,15 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "peer": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -14904,6 +17995,139 @@ "readable-stream": "^2.3.5" } }, + "cmake-js": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-6.1.0.tgz", + "integrity": "sha512-utmukLQftpgrCpGRCaHnkv4K27HZNNFqmBl4vnvccy0xp4c1erxjFU/Lq4wn5ngAhFZmpwBPQfoKWKThjSBiwg==", + "peer": true, + "requires": { + "debug": "^4", + "fs-extra": "^5.0.0", + "is-iojs": "^1.0.1", + "lodash": "^4", + "memory-stream": "0", + "npmlog": "^1.2.0", + "rc": "^1.2.7", + "request": "^2.54.0", + "semver": "^5.0.3", + "splitargs": "0", + "tar": "^4", + "unzipper": "^0.8.13", + "url-join": "0", + "which": "^1.0.9", + "yargs": "^3.6.0" + }, + "dependencies": { + "are-we-there-yet": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.0.6.tgz", + "integrity": "sha1-otKMkxAqpsyWJFomy5VN4G7FPww=", + "peer": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.0 || ^1.1.13" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "peer": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "peer": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "peer": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "gauge": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", + "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", + "peer": true, + "requires": { + "ansi": "^0.3.0", + "has-unicode": "^2.0.0", + "lodash.pad": "^4.1.0", + "lodash.padend": "^4.1.0", + "lodash.padstart": "^4.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "peer": true + }, + "npmlog": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-1.2.1.tgz", + "integrity": "sha1-KOe+YZYJtT960d0wChDWTXFiaLY=", + "peer": true, + "requires": { + "ansi": "~0.3.0", + "are-we-there-yet": "~1.0.0", + "gauge": "~1.2.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "peer": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "peer": true + }, + "yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "peer": true, + "requires": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + } + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -14982,6 +18206,24 @@ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true }, + "commist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", + "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", + "peer": true, + "requires": { + "leven": "^2.1.0", + "minimist": "^1.1.0" + }, + "dependencies": { + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "peer": true + } + } + }, "compare-versions": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", @@ -15128,6 +18370,12 @@ } } }, + "crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==", + "peer": true + }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -15201,8 +18449,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decimal.js": { "version": "10.2.1", @@ -15385,6 +18632,15 @@ "is-obj": "^2.0.0" } }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "peer": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -15394,7 +18650,6 @@ "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, "requires": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", @@ -15860,6 +19115,17 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-xml-parser": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz", + "integrity": "sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg==" + }, + "fastestsmallesttextencoderdecoder": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz", + "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==", + "peer": true + }, "fb-watchman": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", @@ -15961,6 +19227,12 @@ "readable-stream": "^2.3.6" } }, + "follow-redirects": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", + "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "peer": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -16050,6 +19322,18 @@ "dev": true, "optional": true }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "peer": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -16569,6 +19853,29 @@ "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" }, + "help-me": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", + "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", + "peer": true, + "requires": { + "glob": "^7.1.6", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -16673,6 +19980,12 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "peer": true + }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -16733,8 +20046,7 @@ "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, "ip-regex": { "version": "2.1.0", @@ -16902,6 +20214,12 @@ "is-path-inside": "^3.0.1" } }, + "is-iojs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-iojs/-/is-iojs-1.1.0.tgz", + "integrity": "sha1-TBEDO11dlNbqs3dd7cm+fQCDJfE=", + "peer": true + }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -17028,8 +20346,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -18749,7 +22066,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, "requires": { "invert-kv": "^1.0.0" } @@ -18809,6 +22125,12 @@ "uc.micro": "^1.0.1" } }, + "listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", + "peer": true + }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -18842,8 +22164,25 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=", + "peer": true + }, + "lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "peer": true + }, + "lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=", + "peer": true }, "lodash.sortby": { "version": "4.7.0", @@ -18968,6 +22307,41 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memory-stream": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", + "integrity": "sha1-6+jdHDuLw4wOeUHp3dWuvmtN6D8=", + "peer": true, + "requires": { + "readable-stream": "~1.0.26-2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "peer": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "peer": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "peer": true + } + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -19110,6 +22484,115 @@ "moment": ">= 2.9.0" } }, + "mqtt": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.8.tgz", + "integrity": "sha512-DJYjlXODVXtSDecN8jnNzi6ItX3+ufGsEs9OB3YV24HtkRrh7kpx8L5M1LuyF0KzaiGtWr2PzDcMGAY60KGOSA==", + "peer": true, + "requires": { + "commist": "^1.0.0", + "concat-stream": "^2.0.0", + "debug": "^4.1.1", + "duplexify": "^4.1.1", + "help-me": "^3.0.0", + "inherits": "^2.0.3", + "minimist": "^1.2.5", + "mqtt-packet": "^6.8.0", + "pump": "^3.0.0", + "readable-stream": "^3.6.0", + "reinterval": "^1.1.0", + "split2": "^3.1.0", + "ws": "^7.5.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "peer": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "peer": true, + "requires": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "peer": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "ws": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", + "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "peer": true, + "requires": {} + } + } + }, + "mqtt-packet": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", + "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", + "peer": true, + "requires": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "peer": true + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -19622,7 +23105,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, "requires": { "lcid": "^1.0.0" } @@ -20291,6 +23773,12 @@ "rc": "^1.2.8" } }, + "reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc=", + "peer": true + }, "remove-bom-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", @@ -20641,6 +24129,12 @@ } } }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "peer": true + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -20920,6 +24414,12 @@ } } }, + "splitargs": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", + "integrity": "sha1-/p965lc3GzOxDLgNoUPPgknPazs=", + "peer": true + }, "sqlite3": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.0.tgz", @@ -21010,8 +24510,7 @@ "stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, "strict-uri-encode": { "version": "2.0.0", @@ -21373,6 +24872,17 @@ "punycode": "^2.1.1" } }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "peer": true + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -21424,8 +24934,7 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typedarray-to-buffer": { "version": "3.1.5", @@ -21453,6 +24962,12 @@ "dev": true, "optional": true }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "peer": true + }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -21579,6 +25094,52 @@ } } }, + "unzipper": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.8.14.tgz", + "integrity": "sha512-8rFtE7EP5ssOwGpN2dt1Q4njl0N1hUXJ7sSPz0leU2hRdq6+pra57z4YPBlVqm40vcgv6ooKZEAx48fMTv9x4w==", + "peer": true, + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "~1.0.10", + "listenercount": "~1.0.1", + "readable-stream": "~2.1.5", + "setimmediate": "~1.0.4" + }, + "dependencies": { + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "peer": true + }, + "readable-stream": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", + "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", + "peer": true, + "requires": { + "buffer-shims": "^1.0.0", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "peer": true + } + } + }, "upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -21664,6 +25225,12 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url-join": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz", + "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g=", + "peer": true + }, "url-parse-lax": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", @@ -21834,6 +25401,33 @@ "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", "dev": true }, + "websocket-stream": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.2.tgz", + "integrity": "sha512-8z49MKIHbGk3C4HtuHWDtYX8mYej1wWabjthC/RupM9ngeukU4IWoM46dgth1UOS/T4/IqgEdCDJuMe2039OQQ==", + "peer": true, + "requires": { + "duplexify": "^3.5.1", + "inherits": "^2.0.1", + "readable-stream": "^2.3.3", + "safe-buffer": "^5.1.2", + "ws": "^3.2.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "peer": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, "whatwg-encoding": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", @@ -21875,7 +25469,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -21937,6 +25530,12 @@ } } }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", + "peer": true + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -22065,6 +25664,12 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "peer": true + }, "yargs": { "version": "17.2.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", diff --git a/packages/server/package.json b/packages/server/package.json index 152d959a228..ccf83253b66 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -21,6 +21,7 @@ "watch": "tsc --watch --project tsconfig.json" }, "dependencies": { + "@aws-sdk/client-s3": "^3.40.0", "@fortawesome/fontawesome-free": "^5.15.1", "@joplin/lib": "~2.6", "@joplin/renderer": "~2.6", diff --git a/packages/server/src/models/items/storage/StorageDriverBase.ts b/packages/server/src/models/items/storage/StorageDriverBase.ts index 4dfb06ebd5f..195485f862c 100644 --- a/packages/server/src/models/items/storage/StorageDriverBase.ts +++ b/packages/server/src/models/items/storage/StorageDriverBase.ts @@ -33,7 +33,7 @@ export default class StorageDriverBase { public async write(_itemId: string, _content: Buffer, _context: Context): Promise { throw new Error('Not implemented'); } - public async read(_itemId: string, _context: Context): Promise { throw new Error('Not implemented'); } + public async read(_itemId: string, _context: Context): Promise { throw new Error('Not implemented'); } public async delete(_itemId: string | string[], _context: Context): Promise { throw new Error('Not implemented'); } diff --git a/packages/server/src/models/items/storage/StorageDriverDatabase.ts b/packages/server/src/models/items/storage/StorageDriverDatabase.ts index 627ebc2151b..623e2c6f880 100644 --- a/packages/server/src/models/items/storage/StorageDriverDatabase.ts +++ b/packages/server/src/models/items/storage/StorageDriverDatabase.ts @@ -35,7 +35,7 @@ export default class StorageDriverDatabase extends StorageDriverBase { if (updatedRows[0].id !== itemId) throw new Error(`Did not update the right row. Expected: ${itemId}. Got: ${updatedRows[0].id}`); } - public async read(itemId: string, context: Context): Promise { + public async read(itemId: string, context: Context): Promise { const row = await context.models.item().db('items').select('content').where('id', '=', itemId).first(); // Calling code should only call this handler if the row exists, so if diff --git a/packages/server/src/models/items/storage/StorageDriverFs.ts b/packages/server/src/models/items/storage/StorageDriverFs.ts index 762ca7cb38d..a08f7018e1f 100644 --- a/packages/server/src/models/items/storage/StorageDriverFs.ts +++ b/packages/server/src/models/items/storage/StorageDriverFs.ts @@ -30,7 +30,7 @@ export default class StorageDriverFs extends StorageDriverBase { await writeFile(itemPath, content); } - public async read(itemId: string): Promise { + public async read(itemId: string): Promise { return readFile(this.itemPath(itemId)); } diff --git a/packages/server/src/models/items/storage/StorageDriverMemory.ts b/packages/server/src/models/items/storage/StorageDriverMemory.ts index 160bce536db..64fd0828956 100644 --- a/packages/server/src/models/items/storage/StorageDriverMemory.ts +++ b/packages/server/src/models/items/storage/StorageDriverMemory.ts @@ -13,7 +13,8 @@ export default class StorageDriverMemory extends StorageDriverBase { this.data_[itemId] = content; } - public async read(itemId: string): Promise { + public async read(itemId: string): Promise { + if (!(itemId in this.data_)) throw new Error(`No such item: ${itemId}`); return this.data_[itemId]; } diff --git a/packages/server/src/models/items/storage/StorageDriverS3.test.ts b/packages/server/src/models/items/storage/StorageDriverS3.test.ts new file mode 100644 index 00000000000..12f80ac4ed9 --- /dev/null +++ b/packages/server/src/models/items/storage/StorageDriverS3.test.ts @@ -0,0 +1,85 @@ +// Note that these tests require an S3 bucket to be set, with the credentials +// defined in the below config file. If the credentials are missing, all the +// tests are skipped. + +import { afterAllTests, beforeAllDb, beforeEachDb, expectNotThrow, expectThrow, readCredentialFile } from '../../../utils/testing/testUtils'; +import { StorageDriverType } from '../../../utils/types'; +import StorageDriverS3 from './StorageDriverS3'; +import { shouldDeleteContent, shouldNotCreateItemIfContentNotSaved, shouldNotUpdateItemIfContentNotSaved, shouldWriteToContentAndReadItBack } from './testUtils'; + +const s3Config = async () => { + const s = await readCredentialFile('server-s3-test-units.json', ''); + if (!s) return null; + return JSON.parse(s); +}; + +const newDriver = async () => { + return new StorageDriverS3(1, { + type: StorageDriverType.S3, + ...await s3Config(), + }); +}; + +const configIsSet = async () => { + const c = await s3Config(); + return !!c; +}; + +describe('StorageDriverS3', function() { + + beforeAll(async () => { + if (!(await configIsSet())) { + return; + } else { + console.warn('Running S3 unit tests on live environment!'); + await beforeAllDb('StorageDriverS3'); + } + }); + + afterAll(async () => { + if (!(await configIsSet())) return; + await afterAllTests(); + }); + + beforeEach(async () => { + if (!(await configIsSet())) return; + await beforeEachDb(); + }); + + test('should write to content and read it back', async function() { + if (!(await configIsSet())) return; + const driver = await newDriver(); + await shouldWriteToContentAndReadItBack(driver); + }); + + test('should delete the content', async function() { + if (!(await configIsSet())) return; + const driver = await newDriver(); + await shouldDeleteContent(driver); + }); + + test('should not create the item if the content cannot be saved', async function() { + if (!(await configIsSet())) return; + const driver = await newDriver(); + await shouldNotCreateItemIfContentNotSaved(driver); + }); + + test('should not update the item if the content cannot be saved', async function() { + if (!(await configIsSet())) return; + const driver = await newDriver(); + await shouldNotUpdateItemIfContentNotSaved(driver); + }); + + test('should fail if the item row does not exist', async function() { + if (!(await configIsSet())) return; + const driver = await newDriver(); + await expectThrow(async () => driver.read('oops')); + }); + + test('should do nothing if deleting non-existing row', async function() { + if (!(await configIsSet())) return; + const driver = await newDriver(); + await expectNotThrow(async () => driver.delete('oops')); + }); + +}); diff --git a/packages/server/src/models/items/storage/StorageDriverS3.ts b/packages/server/src/models/items/storage/StorageDriverS3.ts new file mode 100644 index 00000000000..850eec7c7b6 --- /dev/null +++ b/packages/server/src/models/items/storage/StorageDriverS3.ts @@ -0,0 +1,101 @@ +import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectsCommand, ObjectIdentifier, HeadObjectCommand } from '@aws-sdk/client-s3'; +import { StorageDriverConfig, StorageDriverType } from '../../../utils/types'; +import StorageDriverBase from './StorageDriverBase'; + +function stream2buffer(stream: any): Promise { + return new Promise((resolve, reject) => { + const buffer: Uint8Array[] = []; + let hasError = false; + + stream.on('data', (chunk: Uint8Array) => { + if (hasError) return; + buffer.push(chunk); + }); + + stream.on('end', () => { + if (hasError) return; + resolve(Buffer.concat(buffer)); + }); + + stream.on('error', (error: any) => { + if (hasError) return; + hasError = true; + reject(error); + }); + }); +} + +export default class StorageDriverS3 extends StorageDriverBase { + + private client_: S3Client; + + public constructor(id: number, config: StorageDriverConfig) { + super(id, { type: StorageDriverType.S3, ...config }); + + this.client_ = new S3Client({ + // We need to set a region. See https://github.com/aws/aws-sdk-js-v3/issues/1845#issuecomment-754832210 + region: this.config.region, + credentials: { + accessKeyId: this.config.accessKeyId, + secretAccessKey: this.config.secretAccessKeyId, + }, + }); + } + + // protected itemPath(itemId: string): string { + // return `${this.config.path}/${super.itemPath(itemId)}`; + // } + + public async write(itemId: string, content: Buffer): Promise { + await this.client_.send(new PutObjectCommand({ + Bucket: this.config.bucket, + Key: itemId, + Body: content, + })); + } + + public async read(itemId: string): Promise { + try { + const response = await this.client_.send(new GetObjectCommand({ + Bucket: this.config.bucket, + Key: itemId, + })); + + return stream2buffer(response.Body); + } catch (error) { + error.message = `Could not get item "${itemId}": ${error.message}`; + throw error; + } + } + + public async delete(itemId: string | string[]): Promise { + const itemIds = Array.isArray(itemId) ? itemId : [itemId]; + + const objects: ObjectIdentifier[] = itemIds.map(id => { + return { Key: this.itemPath(id) }; + }); + + await this.client_.send(new DeleteObjectsCommand({ + Bucket: this.config.bucket, + Delete: { + Objects: objects, + }, + })); + } + + public async exists(itemId: string): Promise { + try { + await this.client_.send(new HeadObjectCommand({ + Bucket: this.config.bucket, + Key: itemId, + })); + + return true; + } catch (error) { + if (error?.$metadata?.httpStatusCode === 404) return false; + error.message = `Could not check if object exists: "${itemId}": ${error.message}`; + throw error; + } + } + +} diff --git a/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts index 86efd7ece5f..317d4fad9f9 100644 --- a/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts +++ b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts @@ -41,6 +41,14 @@ export default function(connectionString: string): StorageDriverConfig | null { output.path = value; } else if (key === 'Mode') { output.mode = parseMode(value); + } else if (key === 'Region') { + output.region = value; + } else if (key === 'AccessKeyId') { + output.accessKeyId = value; + } else if (key === 'SecretAccessKeyId') { + output.secretAccessKeyId = value; + } else if (key === 'Bucket') { + output.bucket = value; } else { throw new Error(`Invalid key: "${key}"`); } diff --git a/packages/server/src/utils/types.ts b/packages/server/src/utils/types.ts index 47c05ba8f10..652f9bfa9d0 100644 --- a/packages/server/src/utils/types.ts +++ b/packages/server/src/utils/types.ts @@ -91,6 +91,7 @@ export enum StorageDriverType { Database = 1, Filesystem = 2, Memory = 3, + S3 = 4, } // The driver mode is only used by fallback drivers. Regardless of the mode, the @@ -122,6 +123,10 @@ export interface StorageDriverConfig { type?: StorageDriverType; path?: string; mode?: StorageDriverMode; + region?: string; + accessKeyId?: string; + secretAccessKeyId?: string; + bucket?: string; } export interface Config { From 0ed0690bf80dbc8e8d6f9da3161fc1f31074de4b Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Mon, 8 Nov 2021 14:58:10 +0000 Subject: [PATCH 16/17] rename --- packages/server/src/config.ts | 4 ++-- packages/server/src/env.ts | 8 ++++---- .../storage/parseStorageDriverConnectionString.test.ts | 1 + .../items/storage/parseStorageDriverConnectionString.ts | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts index 6ed1e8e389a..89a03744957 100644 --- a/packages/server/src/config.ts +++ b/packages/server/src/config.ts @@ -131,8 +131,8 @@ export async function initConfig(envType: Env, env: EnvVariables, overrides: any supportName: env.SUPPORT_NAME || appName, businessEmail: env.BUSINESS_EMAIL || supportEmail, cookieSecure: env.COOKIES_SECURE, - storageDriver: parseStorageDriverConnectionString(env.CONTENT_DRIVER), - storageDriverFallback: parseStorageDriverConnectionString(env.CONTENT_DRIVER_FALLBACK), + storageDriver: parseStorageDriverConnectionString(env.STORAGE_DRIVER), + storageDriverFallback: parseStorageDriverConnectionString(env.STORAGE_DRIVER_FALLBACK), ...overrides, }; } diff --git a/packages/server/src/env.ts b/packages/server/src/env.ts index 5a4e9ea8e1e..dda8f2caa52 100644 --- a/packages/server/src/env.ts +++ b/packages/server/src/env.ts @@ -48,8 +48,8 @@ const defaultEnvValues: EnvVariables = { // Content driver config // ================================================== - CONTENT_DRIVER: 'Type=Database', - CONTENT_DRIVER_FALLBACK: '', + STORAGE_DRIVER: 'Type=Database', + STORAGE_DRIVER_FALLBACK: '', // ================================================== // Mailer config @@ -104,8 +104,8 @@ export interface EnvVariables { SQLITE_DATABASE: string; - CONTENT_DRIVER: string; - CONTENT_DRIVER_FALLBACK: string; + STORAGE_DRIVER: string; + STORAGE_DRIVER_FALLBACK: string; MAILER_ENABLED: boolean; MAILER_HOST: string; diff --git a/packages/server/src/models/items/storage/parseStorageDriverConnectionString.test.ts b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.test.ts index f12f24676f6..eab6af335b9 100644 --- a/packages/server/src/models/items/storage/parseStorageDriverConnectionString.test.ts +++ b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.test.ts @@ -32,6 +32,7 @@ describe('parseStorageDriverConnectionString', function() { }); test('should detect errors', async function() { + expect(() => parseStorageDriverConnectionString('Path=/path/to/dir')).toThrow(); // Type is missing expect(() => parseStorageDriverConnectionString('Type=')).toThrow(); expect(() => parseStorageDriverConnectionString('Type;')).toThrow(); expect(() => parseStorageDriverConnectionString('Type=DoesntExist')).toThrow(); diff --git a/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts index 317d4fad9f9..06abd7ca692 100644 --- a/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts +++ b/packages/server/src/models/items/storage/parseStorageDriverConnectionString.ts @@ -16,6 +16,7 @@ const parseMode = (mode: string): StorageDriverMode => { }; const validate = (config: StorageDriverConfig) => { + if (!config.type) throw new Error('Type must be specified'); if (config.type === StorageDriverType.Filesystem && !config.path) throw new Error('Path must be set for filesystem driver'); return config; }; @@ -24,7 +25,7 @@ export default function(connectionString: string): StorageDriverConfig | null { if (!connectionString) return null; const output: StorageDriverConfig = { - type: StorageDriverType.Database, + type: null, }; const items = connectionString.split(';').map(i => i.trim()); From 68f77f6bbc6dea3edb0d2e434985a3e47d3594c7 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Mon, 8 Nov 2021 17:46:29 +0000 Subject: [PATCH 17/17] fix --- packages/server/src/models/items/storage/StorageDriverS3.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/server/src/models/items/storage/StorageDriverS3.ts b/packages/server/src/models/items/storage/StorageDriverS3.ts index 850eec7c7b6..6f055778a91 100644 --- a/packages/server/src/models/items/storage/StorageDriverS3.ts +++ b/packages/server/src/models/items/storage/StorageDriverS3.ts @@ -42,10 +42,6 @@ export default class StorageDriverS3 extends StorageDriverBase { }); } - // protected itemPath(itemId: string): string { - // return `${this.config.path}/${super.itemPath(itemId)}`; - // } - public async write(itemId: string, content: Buffer): Promise { await this.client_.send(new PutObjectCommand({ Bucket: this.config.bucket, @@ -72,7 +68,7 @@ export default class StorageDriverS3 extends StorageDriverBase { const itemIds = Array.isArray(itemId) ? itemId : [itemId]; const objects: ObjectIdentifier[] = itemIds.map(id => { - return { Key: this.itemPath(id) }; + return { Key: id }; }); await this.client_.send(new DeleteObjectsCommand({