diff --git a/app/server/lib/AppSettings.ts b/app/server/lib/AppSettings.ts
index aff55934..dbb7ef17 100644
--- a/app/server/lib/AppSettings.ts
+++ b/app/server/lib/AppSettings.ts
@@ -1,4 +1,4 @@
-import { isAffirmative } from 'app/common/gutil';
+import { isAffirmative, isNumber } from 'app/common/gutil';
/**
* A bundle of settings for the application. May contain
@@ -23,6 +23,22 @@ export class AppSettings {
return (this._value !== undefined) ? isAffirmative(this._value) : undefined;
}
+ /**
+ * Access the setting as an integer using parseInt. Undefined if not set.
+ * Throws an error if not numberlike.
+ */
+ public getAsInt(): number|undefined {
+ if (this._value === undefined) { return undefined; }
+ const datum = this._value?.valueOf();
+ if (typeof datum === 'number') {
+ return datum;
+ }
+ if (isNumber(String(datum))) {
+ return parseInt(String(datum), 10);
+ }
+ throw new Error(`${datum} does not look like a number`);
+ }
+
/**
* Try to read the setting from the environment. Even if
* we fail, we record information about how we tried to
diff --git a/app/server/lib/ICreate.ts b/app/server/lib/ICreate.ts
index f2d027e6..1ca70ca7 100644
--- a/app/server/lib/ICreate.ts
+++ b/app/server/lib/ICreate.ts
@@ -57,6 +57,9 @@ export function makeSimpleCreator(opts: {
storage?: ICreateStorageOptions[],
billing?: ICreateBillingOptions,
notifier?: ICreateNotifierOptions,
+ sandboxFlavor?: string,
+ shell?: IShell,
+ getExtraHeadHtml?: () => string,
}): ICreate {
const {sessionSecret, storage, notifier, billing} = opts;
return {
@@ -84,7 +87,7 @@ export function makeSimpleCreator(opts: {
return undefined;
},
NSandbox(options) {
- return createSandbox('unsandboxed', options);
+ return createSandbox(opts.sandboxFlavor || 'unsandboxed', options);
},
sessionSecret() {
const secret = process.env.GRIST_SESSION_SECRET || sessionSecret;
@@ -98,7 +101,15 @@ export function makeSimpleCreator(opts: {
if (s.check()) { break; }
}
},
+ ...(opts.shell && {
+ Shell() {
+ return opts.shell as IShell;
+ },
+ }),
getExtraHeadHtml() {
+ if (opts.getExtraHeadHtml) {
+ return opts.getExtraHeadHtml();
+ }
const elements: string[] = [];
if (process.env.APP_STATIC_INCLUDE_CUSTOM_CSS === 'true') {
elements.push('');
diff --git a/app/server/lib/MinIOExternalStorage.ts b/app/server/lib/MinIOExternalStorage.ts
new file mode 100644
index 00000000..d2dad332
--- /dev/null
+++ b/app/server/lib/MinIOExternalStorage.ts
@@ -0,0 +1,164 @@
+import {ApiError} from 'app/common/ApiError';
+import {ObjMetadata, ObjSnapshotWithMetadata, toExternalMetadata, toGristMetadata} from 'app/common/DocSnapshot';
+import {ExternalStorage} from 'app/server/lib/ExternalStorage';
+import {IncomingMessage} from 'http';
+import * as fse from 'fs-extra';
+import * as minio from 'minio';
+
+// The minio typings appear to be quite stale. Extend them here to avoid
+// lots of casts to any.
+type MinIOClient = minio.Client & {
+ statObject(bucket: string, key: string, options: {versionId?: string}): Promise;
+ getObject(bucket: string, key: string, options: {versionId?: string}): Promise;
+ listObjects(bucket: string, key: string, recursive: boolean,
+ options: {IncludeVersion?: boolean}): minio.BucketStream;
+ removeObjects(bucket: string, versions: Array<{name: string, versionId: string}>): Promise;
+};
+
+type MinIOBucketItemStat = minio.BucketItemStat & {
+ versionId?: string;
+ metaData?: Record;
+};
+
+/**
+ * An external store implemented using the MinIO client, which
+ * will work with MinIO and other S3-compatible storage.
+ */
+export class MinIOExternalStorage implements ExternalStorage {
+ private _s3: MinIOClient;
+
+ // Specify bucket to use, and optionally the max number of keys to request
+ // in any call to listObjectVersions (used for testing)
+ constructor(public bucket: string, public options: {
+ endPoint: string,
+ port?: number,
+ useSSL?: boolean,
+ accessKey: string,
+ secretKey: string,
+ }, private _batchSize?: number) {
+ this._s3 = new minio.Client(options) as MinIOClient;
+ }
+
+ public async exists(key: string, snapshotId?: string) {
+ return Boolean(await this.head(key, snapshotId));
+ }
+
+ public async head(key: string, snapshotId?: string): Promise {
+ try {
+ const head = await this._s3.statObject(
+ this.bucket, key,
+ snapshotId ? {versionId: snapshotId} : {},
+ );
+ if (!head.lastModified || !head.versionId) {
+ // AWS documentation says these fields will be present.
+ throw new Error('MinIOExternalStorage.head did not get expected fields');
+ }
+ return {
+ lastModified: head.lastModified.toISOString(),
+ snapshotId: head.versionId,
+ ...head.metaData && { metadata: toGristMetadata(head.metaData) },
+ };
+ } catch (err) {
+ if (!this.isFatalError(err)) { return null; }
+ throw err;
+ }
+ }
+
+ public async upload(key: string, fname: string, metadata?: ObjMetadata) {
+ const stream = fse.createReadStream(fname);
+ const result = await this._s3.putObject(
+ this.bucket, key, stream,
+ metadata ? {Metadata: toExternalMetadata(metadata)} : undefined
+ );
+ // Empirically VersionId is available in result for buckets with versioning enabled.
+ return result.versionId || null;
+ }
+
+ public async download(key: string, fname: string, snapshotId?: string) {
+ const stream = fse.createWriteStream(fname);
+ const request = await this._s3.getObject(
+ this.bucket, key,
+ snapshotId ? {versionId: snapshotId} : {}
+ );
+ const statusCode = request.statusCode || 500;
+ if (statusCode >= 300) {
+ throw new ApiError('download error', statusCode);
+ }
+ // See https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/requests-using-stream-objects.html
+ // for an example of streaming data.
+ const headers = request.headers;
+ // For a versioned bucket, the header 'x-amz-version-id' contains a version id.
+ const downloadedSnapshotId = String(headers['x-amz-version-id'] || '');
+ return new Promise((resolve, reject) => {
+ request
+ .on('error', reject) // handle errors on the read stream
+ .pipe(stream)
+ .on('error', reject) // handle errors on the write stream
+ .on('finish', () => resolve(downloadedSnapshotId));
+ });
+ }
+
+ public async remove(key: string, snapshotIds?: string[]) {
+ if (snapshotIds) {
+ await this._deleteBatch(key, snapshotIds);
+ } else {
+ await this._deleteAllVersions(key);
+ }
+ }
+
+ public async versions(key: string, options?: { includeDeleteMarkers?: boolean }) {
+ const results: minio.BucketItem[] = [];
+ await new Promise((resolve, reject) => {
+ const stream = this._s3.listObjects(this.bucket, key, false, {IncludeVersion: true});
+ stream
+ .on('error', reject)
+ .on('end', () => {
+ resolve(results);
+ })
+ .on('data', data => {
+ results.push(data);
+ });
+ });
+ return results
+ .filter(v => v.name === key &&
+ v.lastModified && (v as any).versionId &&
+ (options?.includeDeleteMarkers || !(v as any).isDeleteMarker))
+ .map(v => ({
+ lastModified: v.lastModified.toISOString(),
+ snapshotId: (v as any).versionId!,
+ }));
+ }
+
+ public url(key: string) {
+ return `minio://${this.bucket}/${key}`;
+ }
+
+ public isFatalError(err: any) {
+ return err.code !== 'NotFound' && err.code !== 'NoSuchKey';
+ }
+
+ public async close() {
+ // nothing to do
+ }
+
+ // Delete all versions of an object.
+ public async _deleteAllVersions(key: string) {
+ const vs = await this.versions(key, {includeDeleteMarkers: true});
+ await this._deleteBatch(key, vs.map(v => v.snapshotId));
+ }
+
+ // Delete a batch of versions for an object.
+ private async _deleteBatch(key: string, versions: Array) {
+ // Max number of keys per request for AWS S3 is 1000, see:
+ // https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html
+ // Stick to this maximum in case we are using this client to talk to AWS.
+ const N = this._batchSize || 1000;
+ for (let i = 0; i < versions.length; i += N) {
+ const iVersions = versions.slice(i, i + N).filter(v => v) as string[];
+ if (iVersions.length === 0) { continue; }
+ await this._s3.removeObjects(this.bucket, iVersions.map(versionId => {
+ return { name: key, versionId };
+ }));
+ }
+ }
+}
diff --git a/app/server/lib/configureMinIOExternalStorage.ts b/app/server/lib/configureMinIOExternalStorage.ts
new file mode 100644
index 00000000..e76f1a18
--- /dev/null
+++ b/app/server/lib/configureMinIOExternalStorage.ts
@@ -0,0 +1,54 @@
+import {wrapWithKeyMappedStorage} from 'app/server/lib/ExternalStorage';
+import {appSettings} from 'app/server/lib/AppSettings';
+import {MinIOExternalStorage} from 'app/server/lib/MinIOExternalStorage';
+
+export function configureMinIOExternalStorage(purpose: 'doc'|'meta', extraPrefix: string) {
+ const options = checkMinIOExternalStorage();
+ if (!options?.bucket) { return undefined; }
+ return wrapWithKeyMappedStorage(new MinIOExternalStorage(options.bucket, options), {
+ basePrefix: options.prefix,
+ extraPrefix,
+ purpose,
+ });
+}
+
+export function checkMinIOExternalStorage() {
+ const settings = appSettings.section('externalStorage').section('minio');
+ const bucket = settings.flag('bucket').readString({
+ envVar: ['GRIST_DOCS_MINIO_BUCKET', 'TEST_MINIO_BUCKET'],
+ preferredEnvVar: 'GRIST_DOCS_MINIO_BUCKET',
+ });
+ if (!bucket) { return undefined; }
+ const prefix = settings.flag('prefix').requireString({
+ envVar: ['GRIST_DOCS_MINIO_PREFIX'],
+ preferredEnvVar: 'GRIST_DOCS_MINIO_PREFIX',
+ defaultValue: 'docs/',
+ });
+ const endPoint = settings.flag('endpoint').requireString({
+ envVar: ['GRIST_DOCS_MINIO_ENDPOINT'],
+ preferredEnvVar: 'GRIST_DOCS_MINIO_ENDPOINT',
+ });
+ const port = settings.flag('port').read({
+ envVar: ['GRIST_DOCS_MINIO_PORT'],
+ preferredEnvVar: 'GRIST_DOCS_MINIO_PORT',
+ }).getAsInt();
+ const useSSL = settings.flag('useSsl').read({
+ envVar: ['GRIST_DOCS_MINIO_USE_SSL'],
+ }).getAsBool();
+ const accessKey = settings.flag('accessKey').requireString({
+ envVar: ['GRIST_DOCS_MINIO_ACCESS_KEY'],
+ });
+ const secretKey = settings.flag('secretKey').requireString({
+ envVar: ['GRIST_DOCS_MINIO_SECRET_KEY'],
+ });
+ settings.flag('url').set(`minio://${bucket}/${prefix}`);
+ settings.flag('active').set(true);
+ return {
+ endPoint,
+ port,
+ bucket, prefix,
+ useSSL,
+ accessKey,
+ secretKey,
+ };
+}
diff --git a/package.json b/package.json
index cb72581e..1da970db 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
"@types/lodash": "4.14.117",
"@types/lru-cache": "5.1.1",
"@types/mime-types": "2.1.0",
+ "@types/minio": "7.0.15",
"@types/mocha": "5.2.5",
"@types/moment-timezone": "0.5.9",
"@types/node": "^14",
@@ -142,6 +143,7 @@
"knockout": "3.5.0",
"locale-currency": "0.0.2",
"lodash": "4.17.21",
+ "minio": "7.0.32",
"moment": "2.29.4",
"moment-timezone": "0.5.35",
"morgan": "1.9.1",
diff --git a/stubs/app/server/lib/create.ts b/stubs/app/server/lib/create.ts
index d0c32887..adea51a4 100644
--- a/stubs/app/server/lib/create.ts
+++ b/stubs/app/server/lib/create.ts
@@ -1,5 +1,15 @@
+import { checkMinIOExternalStorage,
+ configureMinIOExternalStorage } from 'app/server/lib/configureMinIOExternalStorage';
import { makeSimpleCreator } from 'app/server/lib/ICreate';
export const create = makeSimpleCreator({
- sessionSecret: 'Phoo2ag1jaiz6Moo2Iese2xoaphahbai3oNg7diemohlah0ohtae9iengafieS2Hae7quungoCi9iaPh'
+ // This can and should be overridden by GRIST_SESSION_SECRET
+ // (or generated randomly per install, like grist-omnibus does).
+ sessionSecret: 'Phoo2ag1jaiz6Moo2Iese2xoaphahbai3oNg7diemohlah0ohtae9iengafieS2Hae7quungoCi9iaPh',
+ storage: [
+ {
+ check: () => checkMinIOExternalStorage() !== undefined,
+ create: configureMinIOExternalStorage,
+ },
+ ],
});
diff --git a/yarn.lock b/yarn.lock
index e07792cf..00bc9a9f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -469,6 +469,13 @@
resolved "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz"
integrity sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==
+"@types/minio@7.0.15":
+ version "7.0.15"
+ resolved "https://registry.yarnpkg.com/@types/minio/-/minio-7.0.15.tgz#6fbf2e17aeae172cbf181ea52b1faa05a601ce42"
+ integrity sha512-1VR05lWJDuxkn/C7d87MPAJs0p+onKnkUN3nyQ0xrrtaziZQmONy/nxXRaAVWheEyIb6sl0TTi77I/GAQDN5Lw==
+ dependencies:
+ "@types/node" "*"
+
"@types/mocha@5.2.5":
version "5.2.5"
resolved "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.5.tgz"
@@ -770,6 +777,11 @@
resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+"@zxing/text-encoding@0.9.0":
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b"
+ integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==
+
JSONStream@^1.0.3:
version "1.3.5"
resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz"
@@ -1123,6 +1135,11 @@ async@^2.1.5, async@^2.5.0:
dependencies:
lodash "^4.17.14"
+async@^3.1.0:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
+ integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
+
async@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/async/-/async-3.2.0.tgz"
@@ -1138,6 +1155,11 @@ asynckit@^0.4.0:
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+available-typed-arrays@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
+ integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
+
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz"
@@ -1223,6 +1245,13 @@ bl@^4.0.3:
inherits "^2.0.4"
readable-stream "^3.4.0"
+block-stream2@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/block-stream2/-/block-stream2-2.1.0.tgz#ac0c5ef4298b3857796e05be8ebed72196fa054b"
+ integrity sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==
+ dependencies:
+ readable-stream "^3.4.0"
+
bluebird@3.7.2, bluebird@^3.3.3, bluebird@^3.5.0:
version "3.7.2"
resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz"
@@ -1325,6 +1354,11 @@ brorand@^1.0.1, brorand@^1.1.0:
resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz"
integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==
+browser-or-node@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/browser-or-node/-/browser-or-node-1.3.0.tgz#f2a4e8568f60263050a6714b2cc236bb976647a7"
+ integrity sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==
+
browser-pack@^6.0.1:
version "6.1.0"
resolved "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz"
@@ -2094,7 +2128,7 @@ cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
-crypto-browserify@^3.0.0:
+crypto-browserify@^3.0.0, crypto-browserify@^3.12.0:
version "3.12.0"
resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz"
integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
@@ -2241,6 +2275,11 @@ decimal.js@^10.2.1:
resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.0.tgz"
integrity sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==
+decode-uri-component@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
+ integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
+
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz"
@@ -2962,6 +3001,13 @@ fast-text-encoding@^1.0.0:
resolved "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz"
integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==
+fast-xml-parser@^3.17.5:
+ version "3.21.1"
+ resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736"
+ integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==
+ dependencies:
+ strnum "^1.0.4"
+
fastest-levenshtein@^1.0.12:
version "1.0.12"
resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz"
@@ -2990,6 +3036,11 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
+filter-obj@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
+ integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==
+
finalhandler@1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz"
@@ -3035,6 +3086,13 @@ follow-redirects@^1.14.0:
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz"
integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
+for-each@^0.3.3:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
+ integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==
+ dependencies:
+ is-callable "^1.1.3"
+
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz"
@@ -3207,6 +3265,15 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1:
has "^1.0.3"
has-symbols "^1.0.1"
+get-intrinsic@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
+ integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==
+ dependencies:
+ function-bind "^1.1.1"
+ has "^1.0.3"
+ has-symbols "^1.0.3"
+
get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz"
@@ -3387,6 +3454,13 @@ googleapis-common@^5.0.1:
url-template "^2.0.8"
uuid "^8.0.0"
+gopd@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+ integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
+ dependencies:
+ get-intrinsic "^1.1.3"
+
got@5.6.0:
version "5.6.0"
resolved "https://registry.npmjs.org/got/-/got-5.6.0.tgz"
@@ -3513,6 +3587,18 @@ has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2:
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz"
integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
+has-symbols@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+has-tostringtag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
+ integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
+ dependencies:
+ has-symbols "^1.0.2"
+
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz"
@@ -3816,6 +3902,19 @@ ipaddr.js@1.9.1:
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+ipaddr.js@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
+ integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
+
+is-arguments@^1.0.4:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
+ integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
+ dependencies:
+ call-bind "^1.0.2"
+ has-tostringtag "^1.0.0"
+
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz"
@@ -3850,6 +3949,11 @@ is-buffer@~2.0.3:
resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
+is-callable@^1.1.3:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
+ integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
+
is-callable@^1.1.4, is-callable@^1.2.3:
version "1.2.3"
resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz"
@@ -3903,6 +4007,13 @@ is-fullwidth-code-point@^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==
+is-generator-function@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
+ integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz"
@@ -4024,6 +4135,17 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
dependencies:
has-symbols "^1.0.1"
+is-typed-array@^1.1.10, is-typed-array@^1.1.3:
+ version "1.1.10"
+ resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f"
+ integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==
+ dependencies:
+ available-typed-arrays "^1.0.5"
+ call-bind "^1.0.2"
+ for-each "^0.3.3"
+ gopd "^1.0.1"
+ has-tostringtag "^1.0.0"
+
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz"
@@ -4191,6 +4313,11 @@ json-stable-stringify@~0.0.0:
dependencies:
jsonify "~0.0.0"
+json-stream@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/json-stream/-/json-stream-1.0.0.tgz#1a3854e28d2bbeeab31cc7ddf683d2ddc5652708"
+ integrity sha512-H/ZGY0nIAg3QcOwE1QN/rK/Fa7gJn7Ii5obwp6zyPO4xiPNwpIMjqy2gwjBEGqzkF/vSWEIBQCBuN19hYiL6Qg==
+
json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
@@ -4506,7 +4633,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.7.0:
+lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -4625,9 +4752,9 @@ mime-types@^2.1.12, mime-types@~2.1.19:
dependencies:
mime-db "1.46.0"
-mime-types@^2.1.27:
+mime-types@^2.1.14, mime-types@^2.1.27:
version "2.1.35"
- resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
@@ -4701,6 +4828,29 @@ minimist@~1.1.1:
resolved "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz"
integrity sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=
+minio@7.0.32:
+ version "7.0.32"
+ resolved "https://registry.yarnpkg.com/minio/-/minio-7.0.32.tgz#fed6a4679c5954d3efc6df47f73f7e7124446e2c"
+ integrity sha512-txa7Vr0N24MKzeAybP/wY1jxbLnfGHXwZYyfFXuMW55HX2+HOcKEIgH4hU6Qj/kiMgyXs/ozHjAuLIDrR8nwLg==
+ dependencies:
+ async "^3.1.0"
+ block-stream2 "^2.0.0"
+ browser-or-node "^1.3.0"
+ buffer-crc32 "^0.2.13"
+ crypto-browserify "^3.12.0"
+ es6-error "^4.1.1"
+ fast-xml-parser "^3.17.5"
+ ipaddr.js "^2.0.1"
+ json-stream "^1.0.0"
+ lodash "^4.17.21"
+ mime-types "^2.1.14"
+ mkdirp "^0.5.1"
+ query-string "^7.1.1"
+ through2 "^3.0.1"
+ web-encoding "^1.1.5"
+ xml "^1.0.0"
+ xml2js "^0.4.15"
+
minipass@^2.6.0, minipass@^2.9.0:
version "2.9.0"
resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz"
@@ -5689,6 +5839,16 @@ qs@^6.7.0:
dependencies:
side-channel "^1.0.4"
+query-string@^7.1.1:
+ version "7.1.3"
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328"
+ integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==
+ dependencies:
+ decode-uri-component "^0.2.2"
+ filter-obj "^1.1.0"
+ split-on-first "^1.0.0"
+ strict-uri-encode "^2.0.0"
+
querystring-es3@~0.2.0:
version "0.2.1"
resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz"
@@ -5769,6 +5929,15 @@ read-only-stream@^2.0.0:
dependencies:
readable-stream "^2.0.2"
+"readable-stream@2 || 3", readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
+ version "3.6.0"
+ resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz"
+ integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz"
@@ -5782,15 +5951,6 @@ readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
-readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
- version "3.6.0"
- resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz"
- integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
- dependencies:
- inherits "^2.0.3"
- string_decoder "^1.1.1"
- util-deprecate "^1.0.1"
-
readable-stream@~2.0.0:
version "2.0.6"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz"
@@ -6354,6 +6514,11 @@ source-map@~0.5.3:
resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+split-on-first@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
+ integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
+
split2@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz"
@@ -6456,6 +6621,11 @@ stream-transform@^1.0.7:
resolved "https://registry.npmjs.org/stream-transform/-/stream-transform-1.0.8.tgz"
integrity sha512-1q+dL790Ps0NV33rISMq9OLtfDA9KMJZdo1PHZXE85orrWsM4FAh8CVyAOTHO0rhyeM138KNPngBPrx33bFsxw==
+strict-uri-encode@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
+ integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==
+
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz"
@@ -6591,6 +6761,11 @@ strip-json-comments@2.0.1, strip-json-comments@~2.0.1:
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+strnum@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db"
+ integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==
+
strtok3@^6.2.4:
version "6.3.0"
resolved "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz"
@@ -6767,6 +6942,14 @@ through2@^2.0.0:
readable-stream "~2.3.6"
xtend "~4.0.1"
+through2@^3.0.1:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4"
+ integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==
+ dependencies:
+ inherits "^2.0.4"
+ readable-stream "2 || 3"
+
"through@>=2.2.7 <3", through@~2.3.4:
version "2.3.8"
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
@@ -7186,6 +7369,17 @@ util@0.10.3:
dependencies:
inherits "2.0.1"
+util@^0.12.3:
+ version "0.12.5"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc"
+ integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==
+ dependencies:
+ inherits "^2.0.3"
+ is-arguments "^1.0.4"
+ is-generator-function "^1.0.7"
+ is-typed-array "^1.1.3"
+ which-typed-array "^1.1.2"
+
util@~0.10.1:
version "0.10.4"
resolved "https://registry.npmjs.org/util/-/util-0.10.4.tgz"
@@ -7256,6 +7450,15 @@ watchpack@^2.3.1:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"
+web-encoding@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.1.5.tgz#fc810cf7667364a6335c939913f5051d3e0c4864"
+ integrity sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==
+ dependencies:
+ util "^0.12.3"
+ optionalDependencies:
+ "@zxing/text-encoding" "0.9.0"
+
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
@@ -7385,6 +7588,18 @@ which-module@^2.0.0:
resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+which-typed-array@^1.1.2:
+ version "1.1.9"
+ resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"
+ integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==
+ dependencies:
+ available-typed-arrays "^1.0.5"
+ call-bind "^1.0.2"
+ for-each "^0.3.3"
+ gopd "^1.0.1"
+ has-tostringtag "^1.0.0"
+ is-typed-array "^1.1.10"
+
which@1.3.1:
version "1.3.1"
resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz"
@@ -7535,14 +7750,19 @@ xml-name-validator@^3.0.0:
resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
-xml2js@^0.4.0, xml2js@^0.4.17, xml2js@^0.4.23:
+xml2js@^0.4.0, xml2js@^0.4.15, xml2js@^0.4.17, xml2js@^0.4.23:
version "0.4.23"
- resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz"
+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
dependencies:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
+xml@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
+ integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==
+
xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz"