From a665f791e5a26522fd77fa121b8e9c40e8ac2897 Mon Sep 17 00:00:00 2001 From: Clint Zirker Date: Fri, 20 May 2022 17:12:16 -0600 Subject: [PATCH 1/5] Fixing cron types and added better reporting for errors --- lib/cron.d.ts | 47 ++++++++++++++++++++++++++++++++++++----------- lib/cron.js | 4 ++-- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/lib/cron.d.ts b/lib/cron.d.ts index deaa4bcb..9f549252 100644 --- a/lib/cron.d.ts +++ b/lib/cron.d.ts @@ -1,5 +1,6 @@ import { AWSError } from "aws-sdk"; import moment, { Moment } from "moment"; +import { Checkpoints, Cron } from "./types"; /** * Defines a callback function that takes an error object. @@ -14,28 +15,52 @@ export declare type Callback = (err: E) => void; */ declare type DataCallback = (err: E, data: T) => void; +type InstanceStatus = "error" | "complete"; + /** * @todo question What is this here for? */ -interface CronData { +export interface InstanceData { + + invokeTime?: number; + startTime?: number; + completedTime?: number; + maxDuration?: number; + + status?: InstanceStatus; + requestId?: string; + log?: Buffer; + result?: Buffer; } -/** - * @todo question What is this here for? - */ -interface InstanceData { } +type ExecutionType = "lambda" | "fargate"; /** * @todo question What is this here for? */ -interface BotData { } +export interface BotData { + id: string; + checkpoints: Checkpoints, + + lambdaName: string, + name?: string, + description?: string, + lambda: { + settings: Settings[] + } + time?: string, + triggers?: string[], + executionType: ExecutionType, + instances: Record, + requested_kinesis: Record +} /** * Options for reporting a bot is done. * @todo review */ -interface ReportCompleteOptions { +export interface ReportCompleteOptions { /** If true, force the bot to be marked completed. */ forceComplete?: boolean; } @@ -154,7 +179,7 @@ export interface LeoCron { * @param callback A callback that will be called if something goes wrong * @todo question cron is the first arg but it's an empty type definition, what is it for? */ - checkLock: (cron: CronData, runid: string, remainingTime: Milliseconds, callback: Callback) => void; + checkLock: (cron: Cron, runid: string, remainingTime: Milliseconds, callback: Callback) => void; /** * Mark a bot done running. @@ -168,7 +193,7 @@ export interface LeoCron { * * @todo question what does forcing a bot to complete mean, remove the lock? */ - reportComplete: (cron: CronData, runid: string, status: string, log: any, opts: ReportCompleteOptions, callback: Callback) => void; + reportComplete: (cron: Cron, runid: string, status: string, log: any, opts: ReportCompleteOptions, callback: Callback) => void; /** * Lock the given bot, marking it as currently running. Only one instance of a bot is meant to be running @@ -267,7 +292,7 @@ export interface LeoCron { * @internal Don't use. * @todo docbug preg/opts missing types */ - buildPayloads: (cron: CronData, prev, opts) => void; + buildPayloads: (cron: Cron, prev, opts) => void; /** * @internal Don't use. @@ -305,7 +330,7 @@ export interface LeoCron { * * @todo docbug types missing? */ - createBot: (id: string, bot, opts) => void; + createBot: (id: string, bot, opts?) => Promise; } export default function (config: any): LeoCron; diff --git a/lib/cron.js b/lib/cron.js index 7a2ffd24..89a4fcf8 100644 --- a/lib/cron.js +++ b/lib/cron.js @@ -205,7 +205,7 @@ module.exports = function(configure) { ":value": { completedTime: Date.now(), status: status, - log: zlib.gzipSync(JSON.stringify(log)), + log: zlib.gzipSync(JSON.stringify(log, log instanceof Error ? Object.getOwnPropertyNames(log) : undefined)), result: (cron.result != undefined && cron.result != null) ? zlib.gzipSync(JSON.stringify(cron.result)) : null, message: cron.message || configure.registry.cron_message || null } @@ -240,7 +240,7 @@ module.exports = function(configure) { //":requestId": runid, ":token": cron.ts, ":status": status, - ":log": zlib.gzipSync(JSON.stringify(log)), + ":log": zlib.gzipSync(JSON.stringify(log, log instanceof Error ? Object.getOwnPropertyNames(log) : undefined)), ":result": (cron.result != undefined && cron.result != null) ? zlib.gzipSync(JSON.stringify(cron.result)) : null }, "ReturnConsumedCapacity": 'TOTAL' From 52d5cac1efa7550ccf7e7dc71d7f2260c0082169 Mon Sep 17 00:00:00 2001 From: Clint Zirker Date: Fri, 20 May 2022 17:16:23 -0600 Subject: [PATCH 2/5] Added cp_eid for filtering a read stream but allowing checkpointing to work as expected for the filtered events Added Smart Wrapper to do all the wrappers function with 1 entry point --- lib/stream/helper/chunkEventStream.js | 25 +- lib/stream/leo-stream.js | 161 ++++- lib/streams.js | 6 +- lib/types.d.ts | 2 +- wrappers/cron.d.ts | 11 +- wrappers/smart.ts | 932 ++++++++++++++++++++++++++ 6 files changed, 1107 insertions(+), 30 deletions(-) create mode 100644 wrappers/smart.ts diff --git a/lib/stream/helper/chunkEventStream.js b/lib/stream/helper/chunkEventStream.js index a356f682..6f8a796a 100644 --- a/lib/stream/helper/chunkEventStream.js +++ b/lib/stream/helper/chunkEventStream.js @@ -110,7 +110,7 @@ module.exports = function(ls, event, opts) { }; } let correlation = item.correlations[source]; - correlation.end = c.end || c.start; + correlation.end = c.cp_eid || c.end || c.start; correlation.records += c.units || 1; if (!correlation.start) { correlation.start = c.start; @@ -127,6 +127,7 @@ module.exports = function(ls, event, opts) { c.end = c.partial_end; delete c.partial_end; } + delete c.cp_eid; } } @@ -198,19 +199,19 @@ module.exports = function(ls, event, opts) { } } }, - function emit(done, data) { - emitChunk(data.isLast, (value) => { - if (value && (value.size || (value.correlations && Object.keys(value.correlations).length))) { - this.push(value); - } + function emit(done, data) { + emitChunk(data.isLast, (value) => { + if (value && (value.size || (value.correlations && Object.keys(value.correlations).length))) { + this.push(value); + } + done(); + }); + }, + function end(done) { + logger.debug("done chunking"); + logger.debug("total", totalWrites, totalRecords, totalWrites / totalRecords); done(); }); - }, - function end(done) { - logger.debug("done chunking"); - logger.debug("total", totalWrites, totalRecords, totalWrites / totalRecords); - done(); - }); return eventStream; }; diff --git a/lib/stream/leo-stream.js b/lib/stream/leo-stream.js index 3ca20ef4..f456e13c 100644 --- a/lib/stream/leo-stream.js +++ b/lib/stream/leo-stream.js @@ -26,6 +26,42 @@ const twoHundredK = 1024 * 200; const FIVE_MB_IN_BYTES = 1024 * 1024 * 5; +/** + * @param {string|number} str Converts {str} to a hash code value + */ +function hashCode(str) { + if (typeof str === "number") { + return str; + } else if (Array.isArray(str)) { + let h = 0; + for (let a = 0; a < str.length; a++) { + h += hashCode(str[a]); + } + return h; + } + let hash = 0, + i, chr; + if (str.length === 0) return hash; + for (i = 0; i < str.length; i++) { + chr = str.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +} + +function min(...args) { + var current = args[0]; + for (var i = 1; i < args.length; ++i) { + if (current == undefined) { + current = args[i]; + } else if (args[i] != null && args[i] != undefined) { + current = current < args[i] ? current : args[i]; + } + } + return current; +} + module.exports = function(configure) { configure = configure || {}; if (!configure.validate) { @@ -452,7 +488,8 @@ module.exports = function(configure) { correlation_id: { source: rOpts.event || lastEvent.event, start: rOpts.eid || lastEvent.eid, - units: rOpts.units || lastEvent.units || 1 + units: rOpts.units || lastEvent.units || 1, + cp_eid: lastEvent.cp_eid } }); } @@ -505,7 +542,8 @@ module.exports = function(configure) { correlation_id: { source: obj.event, start: rOpts.eid || obj.eid, - units: rOpts.units || obj.units || 1 + units: rOpts.units || obj.units || 1, + cp_eid: obj.cp_eid } }); } else if (r) { //then we are actually writing an object @@ -518,7 +556,8 @@ module.exports = function(configure) { correlation_id: { source: obj.event, [rOpts.partial === true ? 'partial_start' : 'start']: rOpts.eid || obj.eid, - units: rOpts.units || obj.units || 1 + units: rOpts.units || obj.units || 1, + cp_eid: obj.cp_eid } }); } @@ -549,7 +588,8 @@ module.exports = function(configure) { correlation_id: { source: obj.event, start: rOpts.eid || obj.eid, - units: rOpts.units || obj.units || 1 + units: rOpts.units || obj.units || 1, + cp_eid: obj.cp_eid } }); } else if (r) { //then we are actually writing an object @@ -562,7 +602,8 @@ module.exports = function(configure) { correlation_id: { source: obj.event, start: rOpts.eid || obj.eid, - units: rOpts.units || obj.units || 1 + units: rOpts.units || obj.units || 1, + cp_eid: obj.cp_eid } }); } else { @@ -590,7 +631,7 @@ module.exports = function(configure) { done(); return; } else if (r != null && typeof r == "object") { - extra.event = rOpts.queue || outQueue + extra.event = rOpts.queue || outQueue; if (Array.isArray(r)) { r.slice(0, -1).forEach(data => { context.push(data, { partial: true }); @@ -615,7 +656,8 @@ module.exports = function(configure) { correlation_id: { source: obj.event, start: rOpts.eid || obj.eid, - units: rOpts.units || units + units: rOpts.units || units, + cp_eid: obj.cp_eid } }); }).catch(done); @@ -629,7 +671,8 @@ module.exports = function(configure) { correlation_id: { source: obj.event, start: obj.eid, - units: obj.units || 1 + units: obj.units || 1, + cp_eid: obj.cp_eid } }); } @@ -1103,7 +1146,7 @@ module.exports = function(configure) { correlations = [{ [update.correlation_id.source]: { start: update.correlation_id.start || undefined, - end: update.correlation_id.end || update.correlation_id.start, + end: update.correlation_id.cp_eid || update.correlation_id.end || update.correlation_id.start, records: update.correlation_id.units || 1, source_timestamp: start, timestamp: timestamp @@ -1187,6 +1230,94 @@ module.exports = function(configure) { highWaterMark: opts.buffer, objectMode: true }); + + + let filterData = { + iid: 0, + icount: 1, + instance_groups: [], + maxeid: null, + ...global.rstreamsLeoReadFilterData + }; + let isFiltered = filterData.icount > 1; + if (isFiltered) { + + opts.maxOverride = min(opts.maxOverride, filterData.maxeid); + let filterLogger = logger.sub("filter-" + filterData.iid); + filterLogger.log(`Reading Queue Filtered. Bot: ${ID}, IID: ${filterData.iid}, ICount: ${filterData.icount}, Queue: ${queue}, Max: ${opts.maxOverride}`); + let lastEvent = undefined; + let checkpointMap = { + eid: "", + units: 0, + cpEid: "" + }; + cron.checkpointOverrides = []; + + // { + // id: "UNKNOWN", + // event: queue + // }; + let igroupIndex = 0; + let instance_groups = filterData.instance_groups || []; + const eventPartition = global.rstreamsLeoReadFilterEventPartition; + pass = ls.pipeline(pass, ls.through((obj, done) => { + + // Find current instance group + for (; igroupIndex + 1 < instance_groups.length && obj.eid >= instance_groups[igroupIndex + 1].eid; igroupIndex++) { + console.log("looking for block", igroupIndex); + } + + + let icount = (instance_groups[igroupIndex] || {}).icount || filterData.icount; + let partitionValue = eventPartition(obj); + let partition = Math.abs(hashCode(partitionValue != null ? partitionValue : obj.eid)) % icount; + filterLogger.debug("[partition]", partition, "[iid]", filterData.iid, "[eid]", obj.eid, "[icount]", icount); + if (partition == filterData.iid) { + filterLogger.debug(`------ PROCESSING: matched on partition ------ ${obj.eid}`); + let toEmit = lastEvent; + lastEvent = obj; + if (checkpointMap.eid) { + if (checkpointMap.eid !== checkpointMap.cpEid) { + cron.checkpointOverrides.push(checkpointMap); + toEmit.cp_eid = checkpointMap.cpEid; + } + checkpointMap = { + units: 0 + }; + } + checkpointMap.eid = obj.eid; + checkpointMap.units++; + checkpointMap.cpEid = obj.eid; + + done(null, toEmit); + //lastEvent = obj; + //lastEvent.cp_eid = obj.eid; + // todo: need to fix correlation_id to use eid but pass cp_eid + //done(null, toEmit.cp_eid ? toEmit : undefined); + } else { + filterLogger.debug(`------ NOT PROCESSING: no match on partition ------ ${obj.eid}`); + checkpointMap.units++; + checkpointMap.cpEid = obj.eid; + //lastEvent.cp_eid = obj.eid; + done(); + } + }, function emit(done) { + let last = lastEvent; + if (checkpointMap.eid && checkpointMap.eid !== checkpointMap.cpEid) { + cron.checkpointOverrides.push(checkpointMap); + last.cp_eid = checkpointMap.cpEid; + } + filterLogger.log(JSON.stringify(cron.checkpointOverrides, null, 2)); + + done(null, last); + // lastEvent = { + // id: "UNKNOWN", + // event: queue + // }; + //done(null, (last.eid || last.cp_eid) ? last : undefined); + })); + } + var hasTime = true; // tracks if we've passed destroy on the passthrough @@ -1355,6 +1486,9 @@ module.exports = function(configure) { var count = 0; pass.throttledWrite = (obj, callback) => { + if (isPassDestroyed) { + return callback(); + } count++; start = obj.eid + " "; //we want it to continue after this one updateStats(obj); @@ -2220,13 +2354,13 @@ module.exports = function(configure) { } else if (opts == null && units == null) { // Single event to use as correlation opts = endEvent; - units = (startEvent != null && typeof startEvent.units === "number") ? startEvent.units : 1 + units = (startEvent != null && typeof startEvent.units === "number") ? startEvent.units : 1; } opts = { partial: false, ...opts - } + }; if (startEvent == null) { throw new Error(isArray ? "startEvent must not be empty" : "startEvent is required"); @@ -2235,8 +2369,9 @@ module.exports = function(configure) { source: startEvent.event, [opts.partial ? 'partial_start' : 'start']: startEvent.eid, [opts.partial ? 'partial_end' : 'end']: endEvent && endEvent.eid, - units: units - } + units: units, + cp_eid: (endEvent || startEvent).cp_eid + }; } }; diff --git a/lib/streams.js b/lib/streams.js index c6ca6bb2..d069ac48 100644 --- a/lib/streams.js +++ b/lib/streams.js @@ -699,6 +699,9 @@ let ls = module.exports = { let payload = buffer.splice(0); let first = payload[0].correlation_id || {}; let last = payload[payload.length - 1].correlation_id || {}; + + // TODO: Should this be the actual number of units? + let units = payload.reduce((sum, e) => sum + (e.units || 1), 0); let correlation_id = { source: first.source, start: first.start, @@ -714,7 +717,8 @@ let ls = module.exports = { timestamp: payload[payload.length - 1].timestamp, event: payload[0].event, eid: payload[payload.length - 1].eid, - units: payload.length + units: payload.length, + cp_eid: payload[payload.length - 1].cp_eid }); } }; diff --git a/lib/types.d.ts b/lib/types.d.ts index 4be13163..4718335d 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -473,7 +473,7 @@ export interface Cron { force?: boolean; /** For local dev, skip the locking process altogether for guaranteing only one instance of the bot runs */ - ignore_lock?: boolean; + ignoreLock?: boolean; // time: i64; diff --git a/wrappers/cron.d.ts b/wrappers/cron.d.ts index 6796b681..ff60a7ca 100644 --- a/wrappers/cron.d.ts +++ b/wrappers/cron.d.ts @@ -1,7 +1,12 @@ -import { Handler } from "aws-lambda"; +import { Callback, Handler } from "aws-lambda"; import { BotInvocationEvent, RStreamsContext } from "../lib/types"; -export declare type BotHandler = (event: T, context: RStreamsContext) => Promise; -export declare function CronWrapper(handler: BotHandler): Handler +export declare type BotHandler = (event: T, context: RStreamsContext) => Promise; + + +/** @deprecated Don't use. */ +export declare type LegacyBotHandler = (event: T, context: RStreamsContext, callback: Callback) => void | Promise; + +export declare function CronWrapper(handler: BotHandler | LegacyBotHandler): Handler export default CronWrapper; diff --git a/wrappers/smart.ts b/wrappers/smart.ts new file mode 100644 index 00000000..c016ab83 --- /dev/null +++ b/wrappers/smart.ts @@ -0,0 +1,932 @@ +import { APIGatewayEventDefaultAuthorizerContext, APIGatewayEventIdentity, APIGatewayEventRequestContext, APIGatewayEventRequestContextWithAuthorizer, APIGatewayProxyEventHeaders, APIGatewayProxyEventMultiValueHeaders, APIGatewayProxyEventMultiValueQueryStringParameters, APIGatewayProxyEventPathParameters, APIGatewayProxyEventQueryStringParameters, APIGatewayProxyEventStageVariables, Context } from "aws-lambda"; +import { BotData, InstanceData, LeoCron, Milliseconds, ReportCompleteOptions } from "../lib/cron"; +import { BotInvocationEvent, Checkpoint, Checkpoints, Cron, RStreamsContext, RStreamsSdk, throughAsync } from "../index"; +import refUtil from "../lib/reference"; +import { promisify } from "util"; +import config from "../leoConfigure"; +import leoLogger from "leo-logger"; +import moment from "moment"; +import aws, { AWSError } from "aws-sdk"; +import { InvocationResponse } from "aws-sdk/clients/lambda"; + +const logger = leoLogger("smart.wrapper"); + +interface RStreamsProcess extends NodeJS.Process { + __config: unknown; + resources: Record; +} + +const rstreamsProcess = process as unknown as RStreamsProcess; + +rstreamsProcess.__config = config; +rstreamsProcess.env.TZ = config.timezone; +rstreamsProcess.resources = process.env.Resources && JSON.parse(process.env.Resources) || {}; + +const botId = config.name; +const settings = config.cron && config.cron.settings || {}; + +enum InstanceStatus { + error = "error", + complete = "complete" +} + +type EventPartitionFn = (event: any) => string | number | string[] | number[]; +type InstancesFn = ((invocationEvent: InvocationEvent, cronData: FanoutEvent) => number); +type InitializeFn = (invoker: RStreamsBot) => Promise; +type HandlerFn = (inputEvent: InvocationEvent, context: RStreamsContext) => Promise; +type ReduceFn = (responses: FanoutInvokeResponse[]) => Result; + +export interface RStreamsBotHooks { + initialize?: InitializeFn; + handler: HandlerFn; + eventPartition?: EventPartitionFn; + instances?: number | InstancesFn }>; + maxInstances?: number; // Defaults to 20 todo: should there be a default + invokeType?: "process" | "lambda"; // run as child process or invoke new lamba + reduce?: ReduceFn +} + + +// let d: { +// checkpoints: { +// read: { +// "someQueue": { +// checkpoint: "z/234234" +// } +// } +// }, +// instances: { +// "1": { +// "someQueue": { +// checkpoint: "z/234234" +// } +// }, +// "2": { +// "someQueue": { +// checkpoint: "z/234234" +// } +// } +// } +// }; + +/** + * what is the env runner_keep_cmd? + * + * what are instance_groups? + * A range of events and how many instances were running for that range + * Does eid represent the start or end of the block? i think it is the start + * How is this block being set? + * + * instance_group structure: + * [{ + eid: "123", + icount: 3 + }, { + eid: "200", + icount: 4 + }]; + + * + * Fanout Event Structure + * { + * instances?:{ + * [instanceId]: Checkpoint; // This is where we keep positions + * } + * checkpoints?:{ + * + * }, + * __cron: { + * iid?: number // id of this fanout instance, 0=master, 1-n=additional workers + * icount?: number // total number of workers, including the master + * maxeid?: string // Max eid to read from any queue, set to start time of when the master was invoked + * cploc?: "instances"|"checkpoints" // where to save checkpoints + * } + * } + */ + +interface ProcessorParameters { + inputEvent: E, + context: RStreamsContext, + //init: (event: E, context: RStreamsContext) => Promise, + init: Promise, + fn: (event: E, context: RStreamsContext) => Promise +} + +interface IProcessor { + run(): Promise; +} + + +class Processor implements IProcessor { + protected inputEvent: E; + protected context: RStreamsContext; + private init: Promise; + private fn: (event: E, context: RStreamsContext) => Promise; + constructor( + params: ProcessorParameters + ) { + this.inputEvent = params.inputEvent; + this.context = params.context; + this.init = params.init; + this.fn = params.fn; + } + + initialize() { + this.setupRegistory(); + } + + public async run(): Promise { + const startTime = Date.now(); + try { + await this.init; // Init requester first to setup any dependencies + this.initialize(); // Init self + return await this.fn(this.inputEvent, this.context); // Run function + } finally { + let duration = Date.now() - startTime; + logger.debug(`[PROCESSOR]:duration:${duration}`); + } + } + + protected setupRegistory() { + if (!config.registry.id) { + config.registry.id = process.env.AWS_LAMBDA_FUNCTION_NAME; + } + } +} + +class CronProcessor extends Processor { + public static HandlesEvent(event: any): boolean { + return event && event.__cron != null; + } + + id: string; + cron: Cron; + checkLock: (cron: Cron, runid: string, remainingTime: Milliseconds) => Promise; + reportComplete: (cron: Cron, runid: string, status: string, log: any, opts: ReportCompleteOptions) => Promise; + + constructor( + params: ProcessorParameters + ) { + super(params); + this.cron = this.inputEvent.__cron; + console.log("IID:", this.cron.iid); + this.id = `${this.cron.id}:${this.cron.iid}:${this.cron.ts}:${this.context.awsRequestId}`; + this.checkLock = promisify(this.context.sdk.bot.checkLock).bind(this.context.sdk.bot); + this.reportComplete = promisify(this.context.sdk.bot.reportComplete).bind(this.context.sdk.bot); + + this.setupUncaughtExceptions(); + } + + public setupUncaughtExceptions() { + for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener + process.removeListener('uncaughtException', x); + } + process.on('uncaughtException', async (err) => { + console.log(`[LEOCRON]:end:${this.id}`); + logger.error((new Date).toUTCString() + ' uncaughtException:', err.message); + logger.error(err.stack); + await this.releaseLock(err); + }); + } + + public async obtainLock() { + try { + console.log("[LEOCRON]:check:" + this.id); + await this.checkLock(this.cron, this.id, this.context.getRemainingTimeInMillis()); + console.log("[LEOCRON]:start:" + this.id); + } catch (err) { + if (err.code === "ConditionalCheckFailedException") { + throw new Error("already running"); + } else { + throw new Error("failed getting lock"); + } + } + } + + public async releaseLock(error: any) { + console.log("[LEOCRON]:complete:" + this.id); + await this.reportComplete(this.cron, this.id, error ? InstanceStatus.error : InstanceStatus.complete, error ? error : "", {}); + } + + override setupRegistory() { + + config.registry.__cron = this.cron; + config.registry.id = this.cron.id; + } + + public override async run( + ): Promise { + await this.obtainLock(); + let error: Error; + try { + //return await super.run(inputEvent, init, fn); + return await super.run(); + } catch (err) { + error = err; + } finally { + await this.releaseLock(error); + } + } +} + +interface APIGatewayProxyEvent { + body: T | null; + headers: APIGatewayProxyEventHeaders; + multiValueHeaders: APIGatewayProxyEventMultiValueHeaders; + httpMethod: string; + isBase64Encoded: boolean; + path: string; + pathParameters: APIGatewayProxyEventPathParameters | null; + queryStringParameters: APIGatewayProxyEventQueryStringParameters | null; + multiValueQueryStringParameters: APIGatewayProxyEventMultiValueQueryStringParameters | null; + stageVariables: APIGatewayProxyEventStageVariables | null; + requestContext: APIGatewayEventRequestContextWithAuthorizer; + resource: string; +} + +interface APIGatewayProxyResult { + statusCode: number; + headers?: { + [header: string]: boolean | number | string; + } | undefined; + multiValueHeaders?: { + [header: string]: Array; + } | undefined; + body: string | T; + isBase64Encoded?: boolean | undefined; +} + +type APIGatewayProxyResultOrData = APIGatewayProxyResult | T; + +class ApiProcessor, T, S> extends Processor, APIGatewayProxyResultOrData, S>{ + + public static HandlesEvent(event: any): boolean { + return event && event.httpMethod || event.headers; + } + + constructor( + params: ProcessorParameters, T, S> + ) { + super(params); + this.setupUncaughtExceptions(); + } + + public setupUncaughtExceptions() { + for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener + process.removeListener('uncaughtException', x); + } + process.on('uncaughtException', (err) => { + console.error((new Date).toUTCString() + ' uncaughtException:', err.message); + console.error(err.stack); + this.context.done(null, { + statusCode: 500, + 'Content-Type': 'application/json', + body: JSON.stringify("Application Error") + }); + }); + } + + public override async run(): Promise> { + try { + this.inputEvent = this.transformEvent(this.inputEvent); + let response = await super.run(); + + + if (response && typeof response === "object" && "statusCode" in response) { + let data = response as unknown as APIGatewayProxyResult; + if (config.cors && !("Access-Control-Allow-Origin" in data.headers)) { + data.headers["Access-Control-Allow-Origin"] = config.cors; + } + return data; + } else { + let data = response as T; + return { + statusCode: 200, + headers: { + 'Content-Type': config.ContentType || 'application/json', + "Access-Control-Allow-Origin": config.cors ? config.cors : undefined + }, + body: JSON.stringify(data) + }; + } + + } catch (err) { + if (err === "Access Denied" || err === "Error: Access Denied") { + return { + statusCode: 403, + headers: { + 'Content-Type': config.ErrorContentType || 'text/html', + "Access-Control-Allow-Origin": config.cors ? config.cors : undefined + }, + body: err.toString() + }; + } else { + if (typeof err === "object" && "statusCode" in err) { + if (config.cors && err.headers && !("Access-Control-Allow-Origin" in err.headers)) { + err.headers["Access-Control-Allow-Origin"] = config.cors; + } + return err; + } else { + return { + statusCode: 500, + headers: { + 'Content-Type': config.ErrorContentType || 'text/html', + "Access-Control-Allow-Origin": config.cors ? config.cors : undefined + }, + body: err.toString() + }; + } + } + } + } + + transformEvent(inputEvent: unknown): APIGatewayProxyEvent { + + let outEvent: APIGatewayProxyEvent; + + if (this.context.identity) { + // Called Directly not via Api Gateway + let event = inputEvent as APIGatewayProxyEvent; + outEvent = { + body: event.body, + httpMethod: event.httpMethod, + queryStringParameters: event.queryStringParameters, + pathParameters: null, + multiValueHeaders: null, + multiValueQueryStringParameters: null, + isBase64Encoded: false, + path: "", + resource: "", + stageVariables: null, + headers: { + Cookie: event.headers && event.headers.Cookie, + }, + requestContext: { + requestId: this.context.awsRequestId, + identity: this.context.identity as APIGatewayEventIdentity + } as APIGatewayEventRequestContext + }; + + } else { + outEvent = inputEvent as APIGatewayProxyEvent; + } + + + if (outEvent.isBase64Encoded) { + outEvent.body = Buffer.from(outEvent.body as string, 'base64').toString(); + } + if (outEvent.body && typeof outEvent.body !== "object") { + outEvent.body = JSON.parse(outEvent.body); + } + Object.keys(outEvent.pathParameters).map((key) => { + outEvent.pathParameters[key] = decodeURIComponent(outEvent.pathParameters[key]); + }); + outEvent.pathParameters = outEvent.pathParameters || {}; + outEvent.queryStringParameters = outEvent.queryStringParameters || {}; + + return outEvent as APIGatewayProxyEvent; + } +} + + +interface FanoutEvent { + iid?: number; + + /** The total number of bot instances */ + icount?: number; + + /** Starting positions for a given queue */ + starteid?: { + [key: string]: Checkpoint | string // not sure which this is yet + }, + + /** */ + instance_groups?: []; + + ignoreLock?: boolean; + + + /** List of read/write positions of sources and destinations for this bot */ + checkpoints?: Checkpoints; + instances?: Record + + maxeid: string + cploc: "checkpoints" | "instances" + +} + +interface FanoutInvokeResponse { + error: any; + data: Result; + iid: number; +} + +class FanoutProcessor implements IProcessor{ + + + + static eventIdFormat = "[z/]YYYY/MM/DD/HH/mm/"; + + reduce: ReduceFn; + // eventPartition: EventPartitionFn; + getNeedNeedInstances: InstancesFn; + iid: number; + icount: number; + container: FanoutEvent; + + constructor( + private processor: IProcessor, + private inputEvent: any, + private context: RStreamsContext, + private hooks: RStreamsBotHooks + ) { + this.getNeedNeedInstances = typeof this.hooks.instances === "function" ? this.hooks.instances.bind(this.hooks) : (_) => this.hooks.instances as number || 1; + + //this.fixInstanceForLocal(inputEvent); + let { iid, icount, container } = this.getMetadata(inputEvent); + this.iid = iid; + this.icount = icount; + this.container = container; + + // Setup Global RStreamsSdk Filtering + global.rstreamsLeoReadFilterEventPartition = this.hooks.eventPartition ? this.hooks.eventPartition.bind(this.hooks) : (event => event.eid); + global.rstreamsLeoReadFilterData = this.container; + } + + getMetadata(inputEvent) { + let iid = 0; + let icount = 1; + + let container: FanoutEvent = inputEvent.__cron || inputEvent || {}; + + if (container && container.iid) { + //container.iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; + iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; + icount = container.icount; + } + + return { iid, icount, container }; + } + + static fixInstanceForLocal(event: any) { + event.__FANOUT__ = true; + // Get fanout data from process env if running locally + console.log("fixInstanceForLocal", process.env.FANOUT_data); + if (process.env.FANOUT_data) { + Object.assign(event, JSON.parse(process.env.FANOUT_data)); + } + } + + isMaster(): boolean { + return this.iid == 0 || this.icount == null; + } + + isWorker(): boolean { + return !this.isMaster(); + } + + async invokeLambda(iid: number, count: number, newEvent: any): Promise> { + + let data: InvocationResponse;//FanoutInvokeResponse; + let error; + try { + let lambdaApi = new aws.Lambda({ + region: process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION, + httpOptions: { + timeout: this.context.getRemainingTimeInMillis() // Default: 120000 // Two minutes + } + }); + + // todo: Should we invoke a new function or just run a new local process + logger.log("[lambda]", process.env.AWS_LAMBDA_FUNCTION_NAME); + data = await lambdaApi.invoke({ + FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME, + InvocationType: 'RequestResponse', + Payload: JSON.stringify(newEvent), + Qualifier: process.env.AWS_LAMBDA_FUNCTION_VERSION + }).promise(); + + } catch (err) { + error = err; + } + + + logger.log(`Done with Lambda instance ${iid + 1}/${count}`); + logger.log("[lambda err]", error); + logger.log("[lambda data]", data); + + let response: FanoutInvokeResponse; + if (error) { + throw error; + } else if (data.FunctionError) { + throw data.Payload; + } else if (data.Payload != undefined && data.Payload != 'null') { + response = JSON.parse(data.Payload as string); + } else { + response = data as any; + } + + logger.debug("[lambda invoked invocation/payload]", data, JSON.stringify(newEvent, null, 2)); + return response; + } + + async invokeProcess(iid: number, count: number, newEvent: any): Promise> { + // Fork process with event + let worker = require("child_process").fork(process.argv[1], process.argv.slice(2), { + cwd: process.cwd(), + env: Object.assign({}, process.env, { + FANOUT_data: JSON.stringify(newEvent), + runner_keep_cmd: true + }), + execArgv: process.execArgv + }); + + // setup communication between processes + let responseData: FanoutInvokeResponse; + worker.once("message", (response: FanoutInvokeResponse) => { + logger.log(`Got Response with instance ${iid + 1}/${count}`); + responseData = response; + }); + + return new Promise(resolve => { + worker.once("exit", () => { + logger.log(`Done with child instance ${iid + 1}/${count}`); + logger.log("[responseData]", responseData); + resolve(responseData); + }); + }); + } + + async invokeSelf(event: any, iid: number, count: number): Promise> { + logger.log(`Invoking ${iid + 1}/${count}`); + + // Deep copy the invocation event and set instances variables + let newEvent = JSON.parse(JSON.stringify(event)); + let { container } = this.getMetadata(newEvent); + container.iid = iid; + container.icount = count; + container.ignoreLock = true; + + // Add starting points for all queues. + // leo-sdk will look to this before the default checkpoints.read[queue] + let myInstance = (event.instances || {})[iid] || {}; + container.starteid = Object.keys(myInstance).reduce((all, key) => { + if (key.match(/^queue:/) && myInstance[key] && myInstance[key].checkpoint) { + all[key] = myInstance[key].checkpoint; + } + return all; + }, {}); + + // If running in lamba + if (process.env.AWS_LAMBDA_FUNCTION_NAME && !process.env.IS_LOCAL && this.hooks.invokeType !== "process") { + return await this.invokeLambda(iid, count, newEvent); + // try { + // let lambdaApi = new aws.Lambda({ + // region: process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION, + // httpOptions: { + // timeout: context.getRemainingTimeInMillis() // Default: 120000 // Two minutes + // } + // }); + + + // // todo: Should we invoke a new function or just run a new local process + // logger.log("[lambda]", process.env.AWS_LAMBDA_FUNCTION_NAME); + // const lambdaInvocation = lambdaApi.invoke({ + // FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME, + // InvocationType: 'RequestResponse', + // Payload: JSON.stringify(newEvent), + // Qualifier: process.env.AWS_LAMBDA_FUNCTION_VERSION + // }, (err: any, data: any) => { + // logger.log(`Done with Lambda instance ${iid + 1}/${count}`); + // try { + // logger.log("[lambda err]", err); + // logger.log("[lambda data]", data); + // if (err) { + // return reject(err); + // } else if (!err && data.FunctionError) { + // err = data.Payload; + // return reject(err); + // } else if (!err && data.Payload != undefined && data.Payload != 'null') { + // data = JSON.parse(data.Payload); + // } + + // resolve(data); + // } catch (err) { + // reject(err); + // } + // }); + // logger.debug("[lambda invoked invocation/payload]", lambdaInvocation, JSON.stringify(newEvent, null, 2)); + // } catch (err) { + // reject(err); + // } + } + // If running local or child process + else { + + return await this.invokeProcess(iid, count, newEvent); + // // Fork process with event + // let worker = require("child_process").fork(process.argv[1], process.argv.slice(2), { + // cwd: process.cwd(), + // env: Object.assign({}, process.env, { + // FANOUT_data: JSON.stringify(newEvent), + // runner_keep_cmd: true + // }), + // execArgv: process.execArgv + // }); + + // // setup communication between processes + // let responseData = {}; + // worker.once("message", (response) => { + // logger.log(`Got Response with instance ${iid + 1}/${count}`); + // responseData = response; + // }); + // worker.once("exit", () => { + // logger.log(`Done with child instance ${iid + 1}/${count}`); + // logger.log("[responseData]", responseData); + // resolve(responseData); + // }); + } + } + + async masterRun(): Promise { + // This is the master, start the needed workers + let timestamp = moment.utc(); + this.container.maxeid = this.container.maxeid || this.inputEvent.maxeid || timestamp.format(FanoutProcessor.eventIdFormat) + timestamp.valueOf(); + this.container.iid = 0; + logger.log("Fanout Master", this.container.iid); + + + // Promise to get bot instances and checkpoints if not provided + //let setupPromise; + if (!this.inputEvent.instances || !this.inputEvent.checkpoints) { + let bot: BotData = await new Promise((resolve, reject) => + this.context.sdk.aws.dynamodb.get( + this.context.sdk.configuration.resources.LeoCron, + this.inputEvent.botId, + {}, + (err, data) => err ? reject(err) : resolve(data))); + + this.inputEvent.instances = (bot || {}).instances || {}; + this.inputEvent.checkpoints = (bot || {}).checkpoints || {}; + if (bot == null) { + await this.context.sdk.bot.createBot(this.inputEvent.botId, {}); + } + } + + // Find number of requested instances + let instances = this.getNeedNeedInstances(this.inputEvent, this.container); + instances = Math.max(1, Math.min(instances, this.hooks.maxInstances || 20)); + this.container.icount = instances; + + + // Add any entries that don't exist + let toAdd = []; + let command = { + TableName: this.context.sdk.configuration.resources.LeoCron, + Key: { + id: this.inputEvent.botId + }, + UpdateExpression: undefined, + ExpressionAttributeNames: { + "#instances": "instances" + }, + ExpressionAttributeValues: {} + }; + for (let i = 0; i < this.container.icount; i++) { + if (!this.inputEvent.instances[i]) { + // Add checkpoint location in the DB for this instance id + this.inputEvent.instances[i] = (this.inputEvent.checkpoints || {}).read || {}; + toAdd.push(`#instances.#i${i} = :i${i}`); + command.ExpressionAttributeNames[`#i${i}`] = `${i}`; + command.ExpressionAttributeValues[`:i${i}`] = this.inputEvent.instances[i]; + } + } + + // Update the cron table with the needed instances + if (toAdd.length > 0) { + command.UpdateExpression = `set ${toAdd.join(",")}`; + logger.log("Adding Extra Worker instances", JSON.stringify(command, null, 2)); + await this.context.sdk.aws.dynamodb.docClient.update(command).promise(); + } + + let workers: Promise>[] = []; + + // Start the other workers + for (let i = 1; i < instances; i++) { + workers.push(this.invokeSelf(this.inputEvent, i, instances)); + } + + // Setup Master Worker + workers.unshift( + (async () => { + logger.log(`Invoking 1/${instances}`); + + // Handle promise or callback response + let data: Result; + let error; + try { + data = await this.processor.run(); + } catch (err) { + error = err; + } + + logger.log(`Done with instance 1 / ${instances} `); + return { + error: error, + data: data, + iid: 0 + }; + + })()); + + + // Wait for all workers to return and figure out what checkpoint to persist + logger.debug(`Waiting on all Fanout workers: count ${workers.length} `); + let responses = await Promise.all(workers); + return this.hooks.reduce ? this.hooks.reduce(responses) : void 0; + } + + async workerRun(): Promise { + logger.log("Fanout Worker", this.iid); + this.container.cploc = "instances"; + + + //let context_getRemainingTimeInMillis = this.context.getRemainingTimeInMillis; + // // save 3 seconds so the response can get back to the parent with time to process and clean up + // this.context.getRemainingTimeInMillis = () => { + // return context_getRemainingTimeInMillis.call(context) - (this.hooks.lambdaTimeoutPaddingMillis || (1000 * 3)); + // }; + + // wrap callback to send back the response to the parent lambda/process + + + + let data: Result; + let error; + try { + data = await this.processor.run(); + } catch (err) { + error = err; + } + let response = { + error: error, + data: data, + iid: this.iid + }; + logger.log("Worker sending data back", this.iid); + logger.debug("Worker sending back response", this.iid, JSON.stringify(response, null, 2)); + + if (process.send) { + // Send response for child process worker to the master + process.send(response); + return; + } else { + // Send response for lambda worker to the master + return response as unknown as Result; + } + } + + public async run(): Promise { + logger.log("Fanout Start"); + logger.log("Fanout Handler", this.iid); + logger.debug("Fanout Handler Event", this.iid, JSON.stringify(this.inputEvent, null, 2)); + logger.debug("Fanout Handler Context", this.iid, this.context); + + try { + if (this.isMaster()) { + return await this.masterRun(); + } else { + return await this.workerRun(); + } + //return await this.processor.run(); + } + finally { + logger.log("Fanout End"); + } + } +} + +export class RStreamsBot { + sdk: RStreamsSdk; + currentContext?: Context; + currentEvent?: InvocationEvent; + isInitialized: boolean = false; + isFanout: boolean = false; + + constructor(private hooks: RStreamsBotHooks) { + this.sdk = new RStreamsSdk(); + this.setupFanout(); + } + setupFanout() { + this.isFanout = this.hooks.eventPartition != null || this.hooks.instances != null; + } + + initialize(event: InvocationEvent, context: Context): Promise { + + this.currentContext = context; + this.currentEvent = event; + + this.setupRegistry(context, event); + + if (!this.isInitialized) { + this.isInitialized = true; + return this.hooks.initialize(this); + } else { + return Promise.resolve(); + } + } + + + /** + * + * @returns handler bound to the class + */ + public export() { + return this.handler.bind(this); + } + + public async handler(inputEvent: InvocationEvent, context: Context) { + let rstreamsContext: RStreamsContext = this.createRStreamsContext(context, inputEvent); + + let processor = this.createProcessor(inputEvent, rstreamsContext); + + logger.debug("Registry", config.registry); + + return await processor.run(); + } + + createProcessor(event: any, context: RStreamsContext): IProcessor { + // + if (this.isFanout) { + FanoutProcessor.fixInstanceForLocal(event); + } + let processor: IProcessor; + let params: ProcessorParameters = { + inputEvent: event, + context: context, + init: this.initialize(event, context), + fn: this.hooks.handler.bind(this.hooks) + }; + + if (CronProcessor.HandlesEvent(event)) { + // verified that InvocationEvent is the correct type + processor = new CronProcessor(params as unknown as ProcessorParameters); + } else if (ApiProcessor.HandlesEvent(event)) { + // verified that InvocationEvent is the correct type + processor = new ApiProcessor(params as unknown as ProcessorParameters, Result, Settings>) as unknown as IProcessor; + } else { + processor = new Processor(params); + } + + if (this.isFanout) { + processor = new FanoutProcessor(processor, event, context, this.hooks); + } + + return processor; + } + + + + createRStreamsContext(context: Context, inputEvent: Record): RStreamsContext { + let rstreamsContext = context as RStreamsContext; + + rstreamsContext.callbackWaitsForEmptyEventLoop = false; + rstreamsContext.resources = rstreamsContext.resources as Settings; + rstreamsContext.botId = typeof inputEvent.botId === "string" ? inputEvent.botId : botId; + + rstreamsContext.sdk = this.sdk; + + // Provided for backwards compatability + // Maybe add this back in the future + (rstreamsContext as any).settings = settings; + + rstreamsContext.getCheckpoint = function (queue, defaultIfNull) { + let queueRef = refUtil.ref(queue); + let c = inputEvent.start || ( + inputEvent.__cron && + inputEvent.__cron.checkpoints && + inputEvent.__cron.checkpoints.read && + ( + (inputEvent.__cron.checkpoints.read[queueRef] && inputEvent.__cron.checkpoints.read[queueRef].checkpoint) || + (inputEvent.__cron.checkpoints.read[queueRef.id] && inputEvent.__cron.checkpoints.read[queueRef.id].checkpoint)) + ) || defaultIfNull; + return c; + }; + return rstreamsContext; + } + + setupRegistry(context: Context, _inputEvent: InvocationEvent) { + + empty(config.registry); + this.sdk.configuration.registry = config.registry; + config.registry.context = context; + + global.cron_run_again = false; + } +} + +function empty(obj: Record) { + for (let k in obj) { + delete obj[k]; + } +} From 0ad5682864489bd9ea0392f19f0687fce65c1f2c Mon Sep 17 00:00:00 2001 From: Clint Zirker Date: Thu, 26 May 2022 12:33:31 -0600 Subject: [PATCH 3/5] Working on smart wrapper and fanout. Still needs tests and comments --- .gitignore | 1 + lib/cron.d.ts | 13 +- lib/cron.js | 75 +- lib/mock-wrapper.ts | 8 +- lib/stream/helper/chunkEventStream.js | 22 +- lib/stream/leo-stream.js | 81 +- lib/types.d.ts | 24 +- test/lib.cron.utest.ts | 23 +- test/lib.mock-wrapper.utest.ts | 14 +- test/lib.stream.leo-stream.utest.ts | 75 +- wrappers/smart.ts | 1471 +++++++++++++------------ 11 files changed, 941 insertions(+), 866 deletions(-) diff --git a/.gitignore b/.gitignore index cdde490c..4c4a4523 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ package-lock.json .scannerwork .idea .idea/ +*.js *.js.map *.utest.js docs/build/ diff --git a/lib/cron.d.ts b/lib/cron.d.ts index 9f549252..9c73ed0e 100644 --- a/lib/cron.d.ts +++ b/lib/cron.d.ts @@ -34,6 +34,12 @@ export interface InstanceData { result?: Buffer; } +export interface ReadFilterGroup { + eid: string; + icount: number; + ts: number; +} + type ExecutionType = "lambda" | "fargate"; /** @@ -54,6 +60,7 @@ export interface BotData { executionType: ExecutionType, instances: Record, requested_kinesis: Record + read_filter_groups?: ReadFilterGroup[] } /** @@ -179,7 +186,7 @@ export interface LeoCron { * @param callback A callback that will be called if something goes wrong * @todo question cron is the first arg but it's an empty type definition, what is it for? */ - checkLock: (cron: Cron, runid: string, remainingTime: Milliseconds, callback: Callback) => void; + checkLock: (cron: Cron & { time?: number }, runid: string, remainingTime: Milliseconds, callback: Callback) => void; /** * Mark a bot done running. @@ -193,7 +200,7 @@ export interface LeoCron { * * @todo question what does forcing a bot to complete mean, remove the lock? */ - reportComplete: (cron: Cron, runid: string, status: string, log: any, opts: ReportCompleteOptions, callback: Callback) => void; + reportComplete: (cron: Cron & { result?: any, message?: any }, runid: string, status: string, log: any, opts: ReportCompleteOptions, callback: Callback) => void; /** * Lock the given bot, marking it as currently running. Only one instance of a bot is meant to be running @@ -246,7 +253,7 @@ export interface LeoCron { * @param bot The bot data to save on the bot * @param callback A callback that will be called if something goes wrong */ - update: (bot: BotData, callback: Callback) => void; + update: (bot: Partial & { id: string }, callback?: Callback) => Promise; /** * @todo unclear diff --git a/lib/cron.js b/lib/cron.js index 89a4fcf8..66bb1baf 100644 --- a/lib/cron.js +++ b/lib/cron.js @@ -8,6 +8,22 @@ var refUtil = require("./reference.js"); const logger = require('leo-logger')('leo-cron'); +function getCheckpointLocationAndType(registry, type = "read") { + + let container = (registry && registry.rstreamsLeoReadFilterContainer) || (registry && registry.__cron) || {}; + let checkpointLocation = container.cploc; + if (checkpointLocation) { + type = container.iid.toString(); + } else { + checkpointLocation = "checkpoints"; + } + + return { + checkpointLocation, + type + }; +} + module.exports = function(configure) { configure = configure || {}; var dynamodb = new dynamo(configure); @@ -341,9 +357,12 @@ module.exports = function(configure) { }, checkpoint: function(id, event, params, callback) { event = refUtil.refId(event); - var type = params.type == "write" ? "write" : "read"; var opts = {}; + // Check for override location for checkpointing + let { checkpointLocation, type } = getCheckpointLocationAndType(configure.registry, params.type == "write" ? "write" : "read"); + + var checkpointData = { checkpoint: params.eid || params.kinesis_number, source_timestamp: params.source_timestamp, @@ -360,7 +379,7 @@ module.exports = function(configure) { }, UpdateExpression: 'set #checkpoints.#type.#event = :value', ExpressionAttributeNames: { - "#checkpoints": "checkpoints", + "#checkpoints": checkpointLocation, "#type": type, "#event": event }, @@ -374,10 +393,10 @@ module.exports = function(configure) { !configure.registry.__cron || !configure.registry.__cron.force) && (params.expected || (configure.registry && configure.registry.__cron))) { var expected = params.expected || (configure.registry && configure.registry.__cron && - configure.registry.__cron.checkpoints && - configure.registry.__cron.checkpoints[type] && - configure.registry.__cron.checkpoints[type][event] && - configure.registry.__cron.checkpoints[type][event].checkpoint) || undefined; + configure.registry.__cron[checkpointLocation] && + configure.registry.__cron[checkpointLocation][type] && + configure.registry.__cron[checkpointLocation][type][event] && + configure.registry.__cron[checkpointLocation][type][event].checkpoint) || undefined; if (expected) { cronCheckpointCommand.ConditionExpression = '#checkpoints.#type.#event.#checkpoint = :expected'; @@ -391,20 +410,20 @@ module.exports = function(configure) { var updateInMemoryCheckpoint = () => { // this path is not guaranteed to be in config so do a safe set - var c = ["registry", "__cron", "checkpoints", type].reduce((o, f) => o[f] = o[f] || {}, configure); + var c = ["registry", "__cron", checkpointLocation, type].reduce((o, f) => o[f] = o[f] || {}, configure); c[event] = checkpointData; }; - let checkpointLocation = (configure.registry && configure.registry.__cron && configure.registry.__cron.cploc); - if (checkpointLocation) { - cronCheckpointCommand.ExpressionAttributeNames[`#checkpoints`] = checkpointLocation; - cronCheckpointCommand.ExpressionAttributeNames["#type"] = configure.registry.__cron.iid.toString(); - delete cronCheckpointCommand.ExpressionAttributeNames["#checkpoint"]; - delete cronCheckpointCommand.ExpressionAttributeValues[":expected"]; - delete cronCheckpointCommand.ConditionExpression; + // if (checkpointLocation) { + // cronCheckpointCommand.ExpressionAttributeNames[`#checkpoints`] = checkpointLocation; + // cronCheckpointCommand.ExpressionAttributeNames["#type"] = configure.registry.__cron.iid.toString(); - } + // delete cronCheckpointCommand.ExpressionAttributeNames["#checkpoint"]; + // delete cronCheckpointCommand.ExpressionAttributeValues[":expected"]; + // delete cronCheckpointCommand.ConditionExpression; + + // } // Safey check to prevent setting checkpoint to undefined if (checkpointData.checkpoint === undefined) { @@ -440,14 +459,17 @@ module.exports = function(configure) { name: id, description: null }; - if (checkpointLocation) { + + // The structure for `checkpoints` is required so it is handled a little different + // Then when it is a different location + if (checkpointLocation != "checkpoints") { entry[checkpointLocation] = { - [configure.registry.__cron.iid]: { + [type]: { [event]: checkpointData } }; } else { - entry.checkpoints[type][event] = checkpointData; + entry[checkpointLocation][type][event] = checkpointData; } dynamodb.put(CRON_TABLE, id, entry, function(err, data) { if (err) { @@ -476,7 +498,11 @@ module.exports = function(configure) { botId = (configure.registry.__cron && configure.registry.__cron.id); } let id = refUtil.refId(queue); - if (!configure.registry.__cron || !configure.registry.__cron.checkpoints) { + + // Check for override location for checkpointing + let { checkpointLocation, type } = getCheckpointLocationAndType(configure.registry); + + if (!configure.registry.__cron || !configure.registry.__cron[checkpointLocation]) { let data = await new Promise((resolve, reject) => { this.get(botId || configure.registry.__cron.id, {}, (err, data) => { if (err) { @@ -487,9 +513,9 @@ module.exports = function(configure) { }); }); configure.registry.__cron = configure.registry.__cron || data.__cron; - configure.registry.__cron.checkpoints = data.__cron.checkpoints; + configure.registry.__cron[checkpointLocation] = data.__cron[checkpointLocation]; } - var checkpointData = ["registry", "__cron", "checkpoints", "read"].reduce((o, f) => o[f] = o[f] || {}, configure); + var checkpointData = ["registry", "__cron", checkpointLocation, type].reduce((o, f) => o[f] = o[f] || {}, configure); return checkpointData[id] && checkpointData[id].checkpoint; }, @@ -577,11 +603,14 @@ module.exports = function(configure) { } }); if (opts.register) { + // Check for override location for checkpointing + let { checkpointLocation, type } = getCheckpointLocationAndType(configure.registry); + process.__config = process.__config || configure; process.__config.registry = process.__config.registry || {}; configure.registry = extend(true, process.__config.registry, configure.registry || {}); - var checkpointData = ["registry", "__cron", "checkpoints", "read"].reduce((o, f) => o[f] = o[f] || {}, configure); - Object.assign(checkpointData, data.checkpoints && data.checkpoints.read || {}); + var checkpointData = ["registry", "__cron", checkpointLocation, type].reduce((o, f) => o[f] = o[f] || {}, configure); + Object.assign(checkpointData, data[checkpointLocation] && data[checkpointLocation][type] || {}); } let single = !opts.instances; this.buildPayloads(data, {}, { diff --git a/lib/mock-wrapper.ts b/lib/mock-wrapper.ts index 4c243fb6..78e2f288 100644 --- a/lib/mock-wrapper.ts +++ b/lib/mock-wrapper.ts @@ -1,10 +1,10 @@ import { StreamUtil } from "./lib"; -import { ReadEvent, Event, ReadOptions, ReadableStream, WritableStream, TransformStream, WriteOptions, BaseWriteOptions } from "./types"; +import { ReadEvent, Event, ReadOptions, ReadableStream, WritableStream, TransformStream, WriteOptions, BaseWriteOptions, Cron } from "./types"; import fs from "fs"; import path from "path"; import util from "./aws-util"; import stream from "stream"; -import { Callback, CronData, Milliseconds, ReportCompleteOptions } from "./cron"; +import { Callback, Milliseconds, ReportCompleteOptions } from "./cron"; import { AWSError } from "aws-sdk"; import uuid from "uuid"; @@ -128,8 +128,8 @@ export default function (leoStream: LeoStream) { }; - leoStream.cron.checkLock = (cron: CronData, runid: string, remainingTime: number, callback: Callback) => callback(null); - leoStream.cron.reportComplete = (cron: CronData, runid: string, status: string, log: any, opts: ReportCompleteOptions, callback: Callback) => callback(null); + leoStream.cron.checkLock = (cron: Cron, runid: string, remainingTime: number, callback: Callback) => callback(null); + leoStream.cron.reportComplete = (cron: Cron, runid: string, status: string, log: any, opts: ReportCompleteOptions, callback: Callback) => callback(null); leoStream.cron.createLock = (id: string, runid: string, maxDuration: Milliseconds, callback: Callback) => callback(null); leoStream.cron.removeLock = (id: string, runid: string, callback: Callback) => callback(null); diff --git a/lib/stream/helper/chunkEventStream.js b/lib/stream/helper/chunkEventStream.js index 6f8a796a..ff29c41a 100644 --- a/lib/stream/helper/chunkEventStream.js +++ b/lib/stream/helper/chunkEventStream.js @@ -198,20 +198,18 @@ module.exports = function(ls, event, opts) { }); } } - }, - function emit(done, data) { - emitChunk(data.isLast, (value) => { - if (value && (value.size || (value.correlations && Object.keys(value.correlations).length))) { - this.push(value); - } - done(); - }); - }, - function end(done) { - logger.debug("done chunking"); - logger.debug("total", totalWrites, totalRecords, totalWrites / totalRecords); + }, function emit(done, data) { + emitChunk(data.isLast, (value) => { + if (value && (value.size || (value.correlations && Object.keys(value.correlations).length))) { + this.push(value); + } done(); }); + }, function end(done) { + logger.debug("done chunking"); + logger.debug("total", totalWrites, totalRecords, totalWrites / totalRecords); + done(); + }); return eventStream; }; diff --git a/lib/stream/leo-stream.js b/lib/stream/leo-stream.js index f456e13c..7c1b7b48 100644 --- a/lib/stream/leo-stream.js +++ b/lib/stream/leo-stream.js @@ -1235,7 +1235,7 @@ module.exports = function(configure) { let filterData = { iid: 0, icount: 1, - instance_groups: [], + read_filter_groups: [], maxeid: null, ...global.rstreamsLeoReadFilterData }; @@ -1258,17 +1258,17 @@ module.exports = function(configure) { // event: queue // }; let igroupIndex = 0; - let instance_groups = filterData.instance_groups || []; + let read_filter_groups = filterData.read_filter_groups || []; const eventPartition = global.rstreamsLeoReadFilterEventPartition; pass = ls.pipeline(pass, ls.through((obj, done) => { // Find current instance group - for (; igroupIndex + 1 < instance_groups.length && obj.eid >= instance_groups[igroupIndex + 1].eid; igroupIndex++) { - console.log("looking for block", igroupIndex); + for (; igroupIndex + 1 < read_filter_groups.length && obj.eid >= read_filter_groups[igroupIndex + 1].eid; igroupIndex++) { + filterLogger.debug(`Past Filter Group, Index: ${igroupIndex}, Range: ${read_filter_groups[igroupIndex].eid} to ${read_filter_groups[igroupIndex + 1].eid}`); } - let icount = (instance_groups[igroupIndex] || {}).icount || filterData.icount; + let icount = (read_filter_groups[igroupIndex] || {}).icount || filterData.icount; let partitionValue = eventPartition(obj); let partition = Math.abs(hashCode(partitionValue != null ? partitionValue : obj.eid)) % icount; filterLogger.debug("[partition]", partition, "[iid]", filterData.iid, "[eid]", obj.eid, "[icount]", icount); @@ -1441,9 +1441,11 @@ module.exports = function(configure) { start = opts.start + " "; //we want it to start after this one } else if (configure.registry && configure.registry.__cron && configure.registry.__cron.starteid && configure.registry.__cron.starteid[queueRef]) { start = configure.registry.__cron.starteid[queueRef]; - } else if (docs.Responses && docs.Responses[CRON_TABLE] && docs.Responses[CRON_TABLE][0]) { //There are no cron jobs, not possible to stream + } else if (leoCron) { //There are no cron jobs, not possible to stream if (leoCron.checkpoint && !leoEvent.v) { start = leoCron.checkpoint; + } else if (filterData.iid && leoCron.instances && leoCron.instances[filterData.iid] && leoCron.instances[filterData.iid][queueRef] && leoCron.instances[filterData.iid][queueRef].checkpoint) { + start = leoCron.instances[filterData.iid][queueRef].checkpoint; } else if (leoCron.checkpoints && leoCron.checkpoints.read && leoCron.checkpoints.read[queueRef]) { start = leoCron.checkpoints.read[queueRef].checkpoint || defaultCheckpointStart; } else { @@ -1454,7 +1456,7 @@ module.exports = function(configure) { } - console.log("Reading event from", start); + console.log("Reading event from", start, "Queue:", queueRef, "Bot:", ID, "IID: ", filterData.iid || 0); //We want to first use a _snapshot if it exists //This means they have to set it to "z/" manually in order to get a snapshot, we won't do a snapshot by default. @@ -1849,23 +1851,21 @@ module.exports = function(configure) { }); gzip.end(item.gzip); } - }, - function(err) { - start = items[items.length - 1].end + " "; - logger.debug("done with this loop", err || ""); - if (err) { - pass.emit("error", err); - } else { - done(); - } - }); - }); - }, - (err) => { - logger.debug("Calling Pass.end"); - if (err) logger.error(err); - pass.end(); + }, function(err) { + start = items[items.length - 1].end + " "; + logger.debug("done with this loop", err || ""); + if (err) { + pass.emit("error", err); + } else { + done(); + } + }); }); + }, (err) => { + logger.debug("Calling Pass.end"); + if (err) logger.error(err); + pass.end(); + }); } else { logger.debug("no events"); pass.end(); @@ -2017,26 +2017,25 @@ module.exports = function(configure) { [table]: myRecords }, "ReturnConsumedCapacity": 'TOTAL' - }, - function(err, data) { - if (err) { - logger.info(`All ${myRecords.length} records failed! Retryable: ${err.retryable}`, err); - logger.error(myRecords); - if (err.retryable) { - retry.backoff(err); - } else { - retry.fail(err); - } - } else if (table in data.UnprocessedItems && Object.keys(data.UnprocessedItems[table]).length !== 0) { - //reset(); - //data.UnprocessedItems[table].map(m => records.push(m.PutRequest.Item)); - myRecords = data.UnprocessedItems[table]; - retry.backoff(); + }, function(err, data) { + if (err) { + logger.info(`All ${myRecords.length} records failed! Retryable: ${err.retryable}`, err); + logger.error(myRecords); + if (err.retryable) { + retry.backoff(err); } else { - logger.info(table, "saved"); - retry.success(); + retry.fail(err); } - }); + } else if (table in data.UnprocessedItems && Object.keys(data.UnprocessedItems[table]).length !== 0) { + //reset(); + //data.UnprocessedItems[table].map(m => records.push(m.PutRequest.Item)); + myRecords = data.UnprocessedItems[table]; + retry.backoff(); + } else { + logger.info(table, "saved"); + retry.success(); + } + }); }); } getExisting((err, existing) => { diff --git a/lib/types.d.ts b/lib/types.d.ts index 4718335d..12678243 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -4,6 +4,7 @@ import { Context } from "aws-lambda"; import { RStreamsSdk } from "../index"; /// import stream from 'stream'; +import { InstanceData } from "./cron"; // export interface LeoStreamOptions extends stream.TransformOptions { // ignoreCommands?: any; // cmd?: any; @@ -426,6 +427,25 @@ export type BotInvocationEventTyped = T & BotInvocationEvent export interface BotInvocationEvent { botId: string, __cron: Cron, + + paused?: boolean, + instances?: Record, + requested_kinesis?: Record, + trigger?: number, + //type?: string, // cron|?? + //token?: number, + name?: string, + scheduledTrigger?: number, + progress?: Record, + //id?: string; + errorCount?: number, + //startTime?: number, + //endTime?: number, + //maxDuration?: number, + //status?: "complete" | "error", + //invokeTime?: number, + goals?: Record, + //lock?: boolean, } @@ -461,13 +481,13 @@ export interface Cron { * * @todo question is this the same as the ID of the bot or can they differ? */ - name: string; + name?: string; /** * The invocation timestamp token which is what is used to lock on to prevent multiple concurrent invocations to the same bot * @todo question Is this runid which I've seen elsewhere? */ - ts: number; + ts?: number; /** For local dev it will ignore the cron locking and force it to run (will run through the handshake and force it to take the lock) */ force?: boolean; diff --git a/test/lib.cron.utest.ts b/test/lib.cron.utest.ts index c3fa10e7..bcb60060 100644 --- a/test/lib.cron.utest.ts +++ b/test/lib.cron.utest.ts @@ -41,7 +41,7 @@ describe("lib/cron.js", function () { await new Promise((resolve, reject) => cron.trigger({ id: "MOCK_CRON_ID" - }, (err) => { err ? reject(err) : resolve(undefined) })); + }, (err) => { err ? reject(err) : resolve(undefined); })); expect(update).called; assert.deepEqual(update.getCall(0).args[0], @@ -84,7 +84,7 @@ describe("lib/cron.js", function () { try { await new Promise((resolve, reject) => cron.trigger({ id: "MOCK_CRON_ID" - }, (err) => { err ? reject(err) : resolve(undefined) })); + }, (err) => { err ? reject(err) : resolve(undefined); })); } catch (err) { error = err; } @@ -698,7 +698,7 @@ describe("lib/cron.js", function () { } ); - assert.isNotNull(error) + assert.isNotNull(error); assert.equal(error.message, "Update Error"); }); }); @@ -1083,7 +1083,6 @@ describe("lib/cron.js", function () { }, runid, status, log, opts, (err) => err ? reject(err) : resolve(undefined))); expect(update).called; - let args = update.getCall(0).args[0]; args.ExpressionAttributeValues[":log"] = JSON.parse(zlib.gunzipSync(args.ExpressionAttributeValues[":log"]).toString()); assert.deepEqual(args, @@ -1534,7 +1533,7 @@ describe("lib/cron.js", function () { } ); assert.isNotNull(error); - assert.equal(error.message, "Some Error") + assert.equal(error.message, "Some Error"); }); }); @@ -1734,10 +1733,10 @@ describe("lib/cron.js", function () { describe("checkpoint", function () { - function assertInMemoryValue(type: string, queue: string, expected: string | undefined) { + function assertInMemoryValue(type: string, queue: string, expected: string | undefined, cpLocation = "checkpoints") { let value; try { - value = process["__config"].registry?.__cron?.checkpoints[type][ref(queue)]?.checkpoint; + value = process["__config"].registry?.__cron[cpLocation][type][ref(queue)]?.checkpoint; } catch (e) { // nothing } @@ -1868,7 +1867,7 @@ describe("lib/cron.js", function () { }, ); - assertInMemoryValue("read", queue, "z/123/456/789") + assertInMemoryValue("read", queue, "z/123/456/789"); }); it("Read without expected", async function () { @@ -1938,7 +1937,7 @@ describe("lib/cron.js", function () { "ConditionExpression": "attribute_not_exists(#checkpoints.#type.#event.#checkpoint)" }, ); - assertInMemoryValue("read", queue, "z/123/456/789") + assertInMemoryValue("read", queue, "z/123/456/789"); }); it("different checkpoint location", async function () { @@ -2000,7 +1999,7 @@ describe("lib/cron.js", function () { "UpdateExpression": "set #checkpoints.#type.#event = :value", }, ); - assertInMemoryValue("read", queue, "z/123/456/789") + assertInMemoryValue("1", queue, "z/123/456/789", "instances"); }); it("Read undefined value", async function () { @@ -2061,7 +2060,7 @@ describe("lib/cron.js", function () { }, ); - assertInMemoryValue("read", queue, "z/123/456/789") + assertInMemoryValue("read", queue, "z/123/456/789"); }); it("Stale Checkpoint", async function () { @@ -2374,7 +2373,7 @@ describe("lib/cron.js", function () { }, ); - assertInMemoryValue("read", queue, "z/987/654/321"); + assertInMemoryValue("1", queue, "z/987/654/321", "instances"); }); it("Update Error and exists", async function () { diff --git a/test/lib.mock-wrapper.utest.ts b/test/lib.mock-wrapper.utest.ts index 13f8be11..d716259b 100644 --- a/test/lib.mock-wrapper.utest.ts +++ b/test/lib.mock-wrapper.utest.ts @@ -86,8 +86,7 @@ describe('lib/mock-wrapper.ts', function () { wrapper(ls); assert(ls["mocked"], "should be mocked"); let data = []; - await - ls.pipeAsync( + await ls.pipeAsync( ls.fromLeo("Mock", "MockQueue"), ls.through((d, done) => { data.push(d); @@ -152,8 +151,7 @@ describe('lib/mock-wrapper.ts', function () { wrapper(ls); assert(ls["mocked"], "should be mocked"); let data = []; - await - ls.pipeAsync( + await ls.pipeAsync( ls.fromLeo("Mock", "MockQueue"), ls.through((d, done) => { data.push(d); @@ -187,8 +185,7 @@ describe('lib/mock-wrapper.ts', function () { let count = 0; // Override the creating of eids in the wrapper to give a constant (ls as any).eventIdFromTimestamp = () => `z/2022/04/15/23/08/1650064081366-000000${count++}`; - await - ls.pipeAsync( + await ls.pipeAsync( ls.eventstream.readArray([ { event: "MockQueue", id: "MockParentBot", payload: { b: 1, c: true } }, { event: "MockQueue", id: "MockParentBot", payload: { b: 2, c: false } } @@ -220,8 +217,7 @@ describe('lib/mock-wrapper.ts', function () { // Override the creating of eids in the wrapper to give a constant (ls as any).eventIdFromTimestamp = () => "z/2022/04/15/23/08/1650064081366-0000000"; - await - ls.pipeAsync( + await ls.pipeAsync( ls.eventstream.readArray([]), ls.toLeo("MOCK") ); @@ -309,7 +305,7 @@ describe('lib/mock-wrapper.ts', function () { wrapper(ls); assert(ls["mocked"], "should be mocked"); - let cron = {}; + let cron = { id: "SomeBotId" }; let runid = "runid-1"; // verifying they don't throw errors diff --git a/test/lib.stream.leo-stream.utest.ts b/test/lib.stream.leo-stream.utest.ts index 1f1d811a..75ad8c73 100644 --- a/test/lib.stream.leo-stream.utest.ts +++ b/test/lib.stream.leo-stream.utest.ts @@ -8,16 +8,9 @@ import { CorrelationId, ReadEvent } from "../lib/types"; chai.use(sinonchai); let ls = utilFn({ onUpdate: () => { }, resources: {}, aws: {} }); -let mockSdkConfig = { - Region: "mock-Region", - LeoStream: "mock-LeoStream", - LeoCron: "mock-LeoCron", - LeoEvent: "mock-LeoEvent", - LeoS3: "mock-leos3", - LeoKinesisStream: "mock-LeoKinesisStream", - LeoFirehoseStream: "mock-LeoFirehoseStream", - LeoSettings: "mock-LeoSettings", -}; +function deepEqualRemoveUndefined>(actual: T, expected: T, message?: string) { + return assert.deepEqual(JSON.parse(JSON.stringify(actual)), JSON.parse(JSON.stringify(expected)), message); +} describe("leo-stream", function () { let sandbox; @@ -153,7 +146,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -282,7 +275,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -364,7 +357,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -456,7 +449,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -560,7 +553,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -754,7 +747,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -864,7 +857,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -974,7 +967,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -1129,7 +1122,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -1258,7 +1251,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -1340,7 +1333,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -1432,7 +1425,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -1586,7 +1579,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -1690,7 +1683,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -1887,7 +1880,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -1997,7 +1990,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -2157,7 +2150,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -2267,7 +2260,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -2421,7 +2414,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -2588,7 +2581,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -2688,7 +2681,7 @@ describe("leo-stream", function () { }) ); - assert.deepEqual(result, [ + deepEqualRemoveUndefined(result, [ { "correlation_id": { "source": "SourceQueue", @@ -2742,10 +2735,10 @@ describe("leo-stream", function () { ]); }); }); - }); - // batch - // flush - // sync + }); + // batch + // flush + // sync describe("createCorrelation", function () { it("single event", function () { @@ -2755,7 +2748,7 @@ describe("leo-stream", function () { eid: "z/1234", payload: {} }; - assert.deepEqual(ls.createCorrelation(event), { + deepEqualRemoveUndefined(ls.createCorrelation(event), { source: "MyQueue", start: "z/1234", end: undefined, @@ -2775,7 +2768,7 @@ describe("leo-stream", function () { eid: "z/12345", payload: {} }; - assert.deepEqual(ls.createCorrelation(event, { partial: true }), { + deepEqualRemoveUndefined(ls.createCorrelation(event, { partial: true }), { source: "MyQueue", partial_start: "z/12345", partial_end: undefined, @@ -2798,7 +2791,7 @@ describe("leo-stream", function () { eid: "z/1234567", payload: {} }; - assert.deepEqual(ls.createCorrelation(startEvent, endEvent, 100), { + deepEqualRemoveUndefined(ls.createCorrelation(startEvent, endEvent, 100), { source: "MyQueue", start: "z/12345", end: "z/1234567", @@ -2819,7 +2812,7 @@ describe("leo-stream", function () { eid: "z/12345678", payload: {} }; - assert.deepEqual(ls.createCorrelation(startEvent, endEvent, 100, { partial: true }), { + deepEqualRemoveUndefined(ls.createCorrelation(startEvent, endEvent, 100, { partial: true }), { source: "MyQueue", partial_start: "z/123456", partial_end: "z/12345678", @@ -2840,7 +2833,7 @@ describe("leo-stream", function () { eid: "z/123456789", payload: {} }]; - assert.deepEqual(ls.createCorrelation(events), { + deepEqualRemoveUndefined(ls.createCorrelation(events), { source: "MyQueue", start: "z/1234567", end: "z/123456789", @@ -2859,7 +2852,7 @@ describe("leo-stream", function () { eid: "z/123456789", payload: {} }]; - assert.deepEqual(ls.createCorrelation(events, { partial: true }), { + deepEqualRemoveUndefined(ls.createCorrelation(events, { partial: true }), { source: "MyQueue", partial_start: "z/1234567", partial_end: "z/123456789", diff --git a/wrappers/smart.ts b/wrappers/smart.ts index c016ab83..38a80b08 100644 --- a/wrappers/smart.ts +++ b/wrappers/smart.ts @@ -1,810 +1,843 @@ import { APIGatewayEventDefaultAuthorizerContext, APIGatewayEventIdentity, APIGatewayEventRequestContext, APIGatewayEventRequestContextWithAuthorizer, APIGatewayProxyEventHeaders, APIGatewayProxyEventMultiValueHeaders, APIGatewayProxyEventMultiValueQueryStringParameters, APIGatewayProxyEventPathParameters, APIGatewayProxyEventQueryStringParameters, APIGatewayProxyEventStageVariables, Context } from "aws-lambda"; -import { BotData, InstanceData, LeoCron, Milliseconds, ReportCompleteOptions } from "../lib/cron"; +//import { BotData, InstanceData, LeoCron, Milliseconds, ReadFilterGroup, ReportCompleteOptions } from "../lib/cron"; import { BotInvocationEvent, Checkpoint, Checkpoints, Cron, RStreamsContext, RStreamsSdk, throughAsync } from "../index"; import refUtil from "../lib/reference"; -import { promisify } from "util"; +//import { promisify } from "util"; import config from "../leoConfigure"; import leoLogger from "leo-logger"; -import moment from "moment"; -import aws, { AWSError } from "aws-sdk"; -import { InvocationResponse } from "aws-sdk/clients/lambda"; +//import moment from "moment"; +//import aws, { AWSError } from "aws-sdk"; +//import { InvocationResponse } from "aws-sdk/clients/lambda"; +import { IProcessor, ProcessorParameters, RStreamsBotHooks } from "./lib/types"; +import { FanoutProcessor } from "./lib/fanout-processor"; +import { CronProcessor } from "./lib/cron-processor"; +import { Processor } from "./lib/processor"; +import { APIGatewayProxyEvent, ApiProcessor } from "./lib/api-processor"; +import "./lib/process"; + +export * from "./lib/types"; +export * from "./lib/fanout-processor"; const logger = leoLogger("smart.wrapper"); -interface RStreamsProcess extends NodeJS.Process { - __config: unknown; - resources: Record; -} +// interface RStreamsProcess extends NodeJS.Process { +// __config: unknown; +// resources: Record; +// } -const rstreamsProcess = process as unknown as RStreamsProcess; +// const rstreamsProcess = process as unknown as RStreamsProcess; -rstreamsProcess.__config = config; -rstreamsProcess.env.TZ = config.timezone; -rstreamsProcess.resources = process.env.Resources && JSON.parse(process.env.Resources) || {}; +// rstreamsProcess.__config = config; +// rstreamsProcess.env.TZ = config.timezone; +// rstreamsProcess.resources = process.env.Resources && JSON.parse(process.env.Resources) || {}; const botId = config.name; const settings = config.cron && config.cron.settings || {}; -enum InstanceStatus { - error = "error", - complete = "complete" -} - -type EventPartitionFn = (event: any) => string | number | string[] | number[]; -type InstancesFn = ((invocationEvent: InvocationEvent, cronData: FanoutEvent) => number); -type InitializeFn = (invoker: RStreamsBot) => Promise; -type HandlerFn = (inputEvent: InvocationEvent, context: RStreamsContext) => Promise; -type ReduceFn = (responses: FanoutInvokeResponse[]) => Result; - -export interface RStreamsBotHooks { - initialize?: InitializeFn; - handler: HandlerFn; - eventPartition?: EventPartitionFn; - instances?: number | InstancesFn }>; - maxInstances?: number; // Defaults to 20 todo: should there be a default - invokeType?: "process" | "lambda"; // run as child process or invoke new lamba - reduce?: ReduceFn -} +// enum InstanceStatus { +// error = "error", +// complete = "complete" +// } + +// type EventPartitionFn = (event: any) => string | number | string[] | number[]; +// type InstancesFn = ((invocationEvent: InvocationEvent, cronData: FanoutEvent) => number); +// type InitializeFn = (invoker: RStreamsBot) => Promise; +// type HandlerFn = (inputEvent: InvocationEvent, context: RStreamsContext) => Promise; +// type ReduceFn = (responses: FanoutInvokeResponse[]) => Result; + +// export interface RStreamsBotHooks { +// initialize?: InitializeFn; +// handler: HandlerFn; +// eventPartition?: EventPartitionFn; +// instances?: number | InstancesFn; +// maxInstances?: number; // Defaults to 20 todo: should there be a default +// invokeType?: "process" | "lambda"; // run as child process or invoke new lamba +// reduce?: ReduceFn +// } + + +// interface ProcessorParameters { +// inputEvent: E; +// context: RStreamsContext; +// init: Promise; +// fn: (event: E, context: RStreamsContext) => Promise; +// } + +// interface IProcessor { +// handlerResponse: { +// error?: Error, +// data?: T +// }; +// run(): Promise; +// } + + +// class Processor implements IProcessor { + +// public handlerResponse: { +// error?: Error, +// data?: T +// } = {}; +// protected inputEvent: E; +// protected context: RStreamsContext; +// private init: Promise; +// private fn: (event: E, context: RStreamsContext) => Promise; +// constructor( +// params: ProcessorParameters +// ) { +// this.inputEvent = params.inputEvent; +// this.context = params.context; +// this.init = params.init; +// this.fn = params.fn; +// } +// initialize() { +// this.setupRegistory(); +// } -// let d: { -// checkpoints: { -// read: { -// "someQueue": { -// checkpoint: "z/234234" -// } -// } -// }, -// instances: { -// "1": { -// "someQueue": { -// checkpoint: "z/234234" -// } -// }, -// "2": { -// "someQueue": { -// checkpoint: "z/234234" -// } +// public async run(): Promise { +// const startTime = Date.now(); +// try { +// await this.init; // Init requester first to setup any dependencies +// this.initialize(); // Init self +// this.handlerResponse.data = await this.fn(this.inputEvent, this.context); // Run function +// return this.handlerResponse.data; +// } catch (err) { +// this.handlerResponse.error = err; +// throw err; +// } finally { +// let duration = Date.now() - startTime; +// logger.debug(`[PROCESSOR]:duration:${duration}`); // } // } -// }; - -/** - * what is the env runner_keep_cmd? - * - * what are instance_groups? - * A range of events and how many instances were running for that range - * Does eid represent the start or end of the block? i think it is the start - * How is this block being set? - * - * instance_group structure: - * [{ - eid: "123", - icount: 3 - }, { - eid: "200", - icount: 4 - }]; - - * - * Fanout Event Structure - * { - * instances?:{ - * [instanceId]: Checkpoint; // This is where we keep positions - * } - * checkpoints?:{ - * - * }, - * __cron: { - * iid?: number // id of this fanout instance, 0=master, 1-n=additional workers - * icount?: number // total number of workers, including the master - * maxeid?: string // Max eid to read from any queue, set to start time of when the master was invoked - * cploc?: "instances"|"checkpoints" // where to save checkpoints - * } - * } - */ - -interface ProcessorParameters { - inputEvent: E, - context: RStreamsContext, - //init: (event: E, context: RStreamsContext) => Promise, - init: Promise, - fn: (event: E, context: RStreamsContext) => Promise -} - -interface IProcessor { - run(): Promise; -} +// protected setupRegistory() { +// if (!config.registry.id) { +// config.registry.id = process.env.AWS_LAMBDA_FUNCTION_NAME; +// } +// } +// } -class Processor implements IProcessor { - protected inputEvent: E; - protected context: RStreamsContext; - private init: Promise; - private fn: (event: E, context: RStreamsContext) => Promise; - constructor( - params: ProcessorParameters - ) { - this.inputEvent = params.inputEvent; - this.context = params.context; - this.init = params.init; - this.fn = params.fn; - } +// class CronProcessor extends Processor { +// public static HandlesEvent(event: any): boolean { +// return event && event.__cron != null; +// } - initialize() { - this.setupRegistory(); - } +// id: string; +// cron: Cron; +// checkLock: (cron: Cron, runid: string, remainingTime: Milliseconds) => Promise; +// reportComplete: (cron: Cron, runid: string, status: string, log: any, opts: ReportCompleteOptions) => Promise; + +// constructor( +// params: ProcessorParameters +// ) { +// super(params); +// this.cron = this.inputEvent.__cron; +// console.log("IID:", this.cron.iid); +// this.id = `${this.cron.id}:${this.cron.iid}:${this.cron.ts}:${this.context.awsRequestId}`; +// this.checkLock = promisify(this.context.sdk.bot.checkLock).bind(this.context.sdk.bot); +// this.reportComplete = promisify(this.context.sdk.bot.reportComplete).bind(this.context.sdk.bot); + +// this.setupUncaughtExceptions(); +// } - public async run(): Promise { - const startTime = Date.now(); - try { - await this.init; // Init requester first to setup any dependencies - this.initialize(); // Init self - return await this.fn(this.inputEvent, this.context); // Run function - } finally { - let duration = Date.now() - startTime; - logger.debug(`[PROCESSOR]:duration:${duration}`); - } - } +// public setupUncaughtExceptions() { +// for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener +// process.removeListener('uncaughtException', x); +// } +// process.on('uncaughtException', async (err) => { +// console.log(`[LEOCRON]:end:${this.id}`); +// logger.error((new Date).toUTCString() + ' uncaughtException:', err.message); +// logger.error(err.stack); +// await this.releaseLock(err); +// }); +// } - protected setupRegistory() { - if (!config.registry.id) { - config.registry.id = process.env.AWS_LAMBDA_FUNCTION_NAME; - } - } -} +// public async obtainLock() { +// try { +// console.log("[LEOCRON]:check:" + this.id); +// await this.checkLock(this.cron, this.id, this.context.getRemainingTimeInMillis()); +// console.log("[LEOCRON]:start:" + this.id); +// } catch (err) { +// if (err.code === "ConditionalCheckFailedException") { +// throw new Error("already running"); +// } else { +// throw new Error("failed getting lock"); +// } +// } +// } -class CronProcessor extends Processor { - public static HandlesEvent(event: any): boolean { - return event && event.__cron != null; - } +// public async releaseLock(error: any) { +// console.log("[LEOCRON]:complete:" + this.id); +// await this.reportComplete(this.cron, this.id, error ? InstanceStatus.error : InstanceStatus.complete, error ? error : "", {}); +// } - id: string; - cron: Cron; - checkLock: (cron: Cron, runid: string, remainingTime: Milliseconds) => Promise; - reportComplete: (cron: Cron, runid: string, status: string, log: any, opts: ReportCompleteOptions) => Promise; - - constructor( - params: ProcessorParameters - ) { - super(params); - this.cron = this.inputEvent.__cron; - console.log("IID:", this.cron.iid); - this.id = `${this.cron.id}:${this.cron.iid}:${this.cron.ts}:${this.context.awsRequestId}`; - this.checkLock = promisify(this.context.sdk.bot.checkLock).bind(this.context.sdk.bot); - this.reportComplete = promisify(this.context.sdk.bot.reportComplete).bind(this.context.sdk.bot); - - this.setupUncaughtExceptions(); - } +// override setupRegistory() { - public setupUncaughtExceptions() { - for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener - process.removeListener('uncaughtException', x); - } - process.on('uncaughtException', async (err) => { - console.log(`[LEOCRON]:end:${this.id}`); - logger.error((new Date).toUTCString() + ' uncaughtException:', err.message); - logger.error(err.stack); - await this.releaseLock(err); - }); - } +// config.registry.__cron = this.cron; +// config.registry.id = this.cron.id; +// } - public async obtainLock() { - try { - console.log("[LEOCRON]:check:" + this.id); - await this.checkLock(this.cron, this.id, this.context.getRemainingTimeInMillis()); - console.log("[LEOCRON]:start:" + this.id); - } catch (err) { - if (err.code === "ConditionalCheckFailedException") { - throw new Error("already running"); - } else { - throw new Error("failed getting lock"); - } - } - } +// public override async run( +// ): Promise { +// await this.obtainLock(); +// let error: Error; +// try { +// return await super.run(); +// } catch (err) { +// error = err; +// } finally { +// await this.releaseLock(error); +// } +// } +// } + +// interface APIGatewayProxyEvent { +// body: T | null; +// headers: APIGatewayProxyEventHeaders; +// multiValueHeaders: APIGatewayProxyEventMultiValueHeaders; +// httpMethod: string; +// isBase64Encoded: boolean; +// path: string; +// pathParameters: APIGatewayProxyEventPathParameters | null; +// queryStringParameters: APIGatewayProxyEventQueryStringParameters | null; +// multiValueQueryStringParameters: APIGatewayProxyEventMultiValueQueryStringParameters | null; +// stageVariables: APIGatewayProxyEventStageVariables | null; +// requestContext: APIGatewayEventRequestContextWithAuthorizer; +// resource: string; +// } + +// interface APIGatewayProxyResult { +// statusCode: number; +// headers?: { +// [header: string]: boolean | number | string; +// } | undefined; +// multiValueHeaders?: { +// [header: string]: Array; +// } | undefined; +// body: string | T; +// isBase64Encoded?: boolean | undefined; +// } + +// type APIGatewayProxyResultOrData = APIGatewayProxyResult | T; + +// class ApiProcessor, T, S> extends Processor, APIGatewayProxyResultOrData, S>{ + +// public static HandlesEvent(event: any): boolean { +// return event && event.httpMethod || event.headers; +// } - public async releaseLock(error: any) { - console.log("[LEOCRON]:complete:" + this.id); - await this.reportComplete(this.cron, this.id, error ? InstanceStatus.error : InstanceStatus.complete, error ? error : "", {}); - } +// constructor( +// params: ProcessorParameters, T, S> +// ) { +// super(params); +// this.setupUncaughtExceptions(); +// } - override setupRegistory() { +// public setupUncaughtExceptions() { +// for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener +// process.removeListener('uncaughtException', x); +// } +// process.on('uncaughtException', (err) => { +// console.error((new Date).toUTCString() + ' uncaughtException:', err.message); +// console.error(err.stack); +// this.context.done(null, { +// statusCode: 500, +// 'Content-Type': 'application/json', +// body: JSON.stringify("Application Error") +// }); +// }); +// } - config.registry.__cron = this.cron; - config.registry.id = this.cron.id; - } +// public override async run(): Promise> { +// try { +// this.inputEvent = this.transformEvent(this.inputEvent); +// let response = await super.run(); + + +// if (response && typeof response === "object" && "statusCode" in response) { +// let data = response as unknown as APIGatewayProxyResult; +// if (config.cors && !("Access-Control-Allow-Origin" in data.headers)) { +// data.headers["Access-Control-Allow-Origin"] = config.cors; +// } +// return data; +// } else { +// let data = response as T; +// return { +// statusCode: 200, +// headers: { +// 'Content-Type': config.ContentType || 'application/json', +// "Access-Control-Allow-Origin": config.cors ? config.cors : undefined +// }, +// body: JSON.stringify(data) +// }; +// } - public override async run( - ): Promise { - await this.obtainLock(); - let error: Error; - try { - //return await super.run(inputEvent, init, fn); - return await super.run(); - } catch (err) { - error = err; - } finally { - await this.releaseLock(error); - } - } -} +// } catch (err) { +// if (err === "Access Denied" || err === "Error: Access Denied") { +// return { +// statusCode: 403, +// headers: { +// 'Content-Type': config.ErrorContentType || 'text/html', +// "Access-Control-Allow-Origin": config.cors ? config.cors : undefined +// }, +// body: err.toString() +// }; +// } else { +// if (typeof err === "object" && "statusCode" in err) { +// if (config.cors && err.headers && !("Access-Control-Allow-Origin" in err.headers)) { +// err.headers["Access-Control-Allow-Origin"] = config.cors; +// } +// return err; +// } else { +// return { +// statusCode: 500, +// headers: { +// 'Content-Type': config.ErrorContentType || 'text/html', +// "Access-Control-Allow-Origin": config.cors ? config.cors : undefined +// }, +// body: err.toString() +// }; +// } +// } +// } +// } -interface APIGatewayProxyEvent { - body: T | null; - headers: APIGatewayProxyEventHeaders; - multiValueHeaders: APIGatewayProxyEventMultiValueHeaders; - httpMethod: string; - isBase64Encoded: boolean; - path: string; - pathParameters: APIGatewayProxyEventPathParameters | null; - queryStringParameters: APIGatewayProxyEventQueryStringParameters | null; - multiValueQueryStringParameters: APIGatewayProxyEventMultiValueQueryStringParameters | null; - stageVariables: APIGatewayProxyEventStageVariables | null; - requestContext: APIGatewayEventRequestContextWithAuthorizer; - resource: string; -} +// transformEvent(inputEvent: unknown): APIGatewayProxyEvent { + +// let outEvent: APIGatewayProxyEvent; + +// if (this.context.identity) { +// // Called Directly not via Api Gateway +// let event = inputEvent as APIGatewayProxyEvent; +// outEvent = { +// body: event.body, +// httpMethod: event.httpMethod, +// queryStringParameters: event.queryStringParameters, +// pathParameters: null, +// multiValueHeaders: null, +// multiValueQueryStringParameters: null, +// isBase64Encoded: false, +// path: "", +// resource: "", +// stageVariables: null, +// headers: { +// Cookie: event.headers && event.headers.Cookie, +// }, +// requestContext: { +// requestId: this.context.awsRequestId, +// identity: this.context.identity as APIGatewayEventIdentity +// } as APIGatewayEventRequestContext +// }; + +// } else { +// outEvent = inputEvent as APIGatewayProxyEvent; +// } -interface APIGatewayProxyResult { - statusCode: number; - headers?: { - [header: string]: boolean | number | string; - } | undefined; - multiValueHeaders?: { - [header: string]: Array; - } | undefined; - body: string | T; - isBase64Encoded?: boolean | undefined; -} -type APIGatewayProxyResultOrData = APIGatewayProxyResult | T; +// if (outEvent.isBase64Encoded) { +// outEvent.body = Buffer.from(outEvent.body as string, 'base64').toString(); +// } +// if (outEvent.body && typeof outEvent.body !== "object") { +// outEvent.body = JSON.parse(outEvent.body); +// } +// Object.keys(outEvent.pathParameters).map((key) => { +// outEvent.pathParameters[key] = decodeURIComponent(outEvent.pathParameters[key]); +// }); +// outEvent.pathParameters = outEvent.pathParameters || {}; +// outEvent.queryStringParameters = outEvent.queryStringParameters || {}; -class ApiProcessor, T, S> extends Processor, APIGatewayProxyResultOrData, S>{ +// return outEvent as APIGatewayProxyEvent; +// } +// } - public static HandlesEvent(event: any): boolean { - return event && event.httpMethod || event.headers; - } - constructor( - params: ProcessorParameters, T, S> - ) { - super(params); - this.setupUncaughtExceptions(); - } +// export interface FanoutEvent { +// iid?: number; - public setupUncaughtExceptions() { - for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener - process.removeListener('uncaughtException', x); - } - process.on('uncaughtException', (err) => { - console.error((new Date).toUTCString() + ' uncaughtException:', err.message); - console.error(err.stack); - this.context.done(null, { - statusCode: 500, - 'Content-Type': 'application/json', - body: JSON.stringify("Application Error") - }); - }); - } +// /** The total number of bot instances */ +// icount?: number; - public override async run(): Promise> { - try { - this.inputEvent = this.transformEvent(this.inputEvent); - let response = await super.run(); - - - if (response && typeof response === "object" && "statusCode" in response) { - let data = response as unknown as APIGatewayProxyResult; - if (config.cors && !("Access-Control-Allow-Origin" in data.headers)) { - data.headers["Access-Control-Allow-Origin"] = config.cors; - } - return data; - } else { - let data = response as T; - return { - statusCode: 200, - headers: { - 'Content-Type': config.ContentType || 'application/json', - "Access-Control-Allow-Origin": config.cors ? config.cors : undefined - }, - body: JSON.stringify(data) - }; - } - - } catch (err) { - if (err === "Access Denied" || err === "Error: Access Denied") { - return { - statusCode: 403, - headers: { - 'Content-Type': config.ErrorContentType || 'text/html', - "Access-Control-Allow-Origin": config.cors ? config.cors : undefined - }, - body: err.toString() - }; - } else { - if (typeof err === "object" && "statusCode" in err) { - if (config.cors && err.headers && !("Access-Control-Allow-Origin" in err.headers)) { - err.headers["Access-Control-Allow-Origin"] = config.cors; - } - return err; - } else { - return { - statusCode: 500, - headers: { - 'Content-Type': config.ErrorContentType || 'text/html', - "Access-Control-Allow-Origin": config.cors ? config.cors : undefined - }, - body: err.toString() - }; - } - } - } - } +// /** Starting positions for a given queue */ +// starteid?: { +// [key: string]: Checkpoint | string // not sure which this is yet +// }, - transformEvent(inputEvent: unknown): APIGatewayProxyEvent { - - let outEvent: APIGatewayProxyEvent; - - if (this.context.identity) { - // Called Directly not via Api Gateway - let event = inputEvent as APIGatewayProxyEvent; - outEvent = { - body: event.body, - httpMethod: event.httpMethod, - queryStringParameters: event.queryStringParameters, - pathParameters: null, - multiValueHeaders: null, - multiValueQueryStringParameters: null, - isBase64Encoded: false, - path: "", - resource: "", - stageVariables: null, - headers: { - Cookie: event.headers && event.headers.Cookie, - }, - requestContext: { - requestId: this.context.awsRequestId, - identity: this.context.identity as APIGatewayEventIdentity - } as APIGatewayEventRequestContext - }; +// /** */ +// read_filter_groups?: ReadFilterGroup[]; - } else { - outEvent = inputEvent as APIGatewayProxyEvent; - } +// ignoreLock?: boolean; - if (outEvent.isBase64Encoded) { - outEvent.body = Buffer.from(outEvent.body as string, 'base64').toString(); - } - if (outEvent.body && typeof outEvent.body !== "object") { - outEvent.body = JSON.parse(outEvent.body); - } - Object.keys(outEvent.pathParameters).map((key) => { - outEvent.pathParameters[key] = decodeURIComponent(outEvent.pathParameters[key]); - }); - outEvent.pathParameters = outEvent.pathParameters || {}; - outEvent.queryStringParameters = outEvent.queryStringParameters || {}; +// /** List of read/write positions of sources and destinations for this bot */ +// checkpoints?: Checkpoints; +// instances?: Record - return outEvent as APIGatewayProxyEvent; - } -} +// maxeid: string +// cploc: "checkpoints" | "instances" +// } -interface FanoutEvent { - iid?: number; +// interface FanoutInvokeResponse { +// error: any; +// data: Result; +// iid: number; +// } - /** The total number of bot instances */ - icount?: number; +// class FanoutProcessor implements IProcessor{ - /** Starting positions for a given queue */ - starteid?: { - [key: string]: Checkpoint | string // not sure which this is yet - }, +// static eventIdFormat = "[z/]YYYY/MM/DD/HH/mm/"; +// static ReadFilterGroupChangeDelayMS: number = 1000 * 60 * 15; // Can only change filter group size every 15 minutes +// static ReadFilterGroupChangeMaxEidDistanceMS: number = 1000 * 60 * 15; // Must be within 15 minutes of the last filter group eid - /** */ - instance_groups?: []; +// public handlerResponse: { error?: Error; data?: Result; } = {}; - ignoreLock?: boolean; +// reduce: ReduceFn; +// getNeedNeedInstances: InstancesFn; +// iid: number; +// icount: number; +// container: FanoutEvent; +// constructor( +// private processor: IProcessor, +// private inputEvent: any, +// private context: RStreamsContext, +// private hooks: RStreamsBotHooks +// ) { +// this.getNeedNeedInstances = typeof this.hooks.instances === "function" ? this.hooks.instances.bind(this.hooks) : (_) => this.hooks.instances as number || 1; - /** List of read/write positions of sources and destinations for this bot */ - checkpoints?: Checkpoints; - instances?: Record +// let { iid, icount, container } = this.getMetadata(inputEvent); - maxeid: string - cploc: "checkpoints" | "instances" - -} - -interface FanoutInvokeResponse { - error: any; - data: Result; - iid: number; -} - -class FanoutProcessor implements IProcessor{ +// config.registry.rstreamsLeoReadFilterContainer = container; +// this.iid = iid; +// this.icount = icount; +// this.container = container; +// // Setup Global RStreamsSdk Filtering +// global.rstreamsLeoReadFilterEventPartition = this.hooks.eventPartition ? this.hooks.eventPartition.bind(this.hooks) : (event => event.eid); +// global.rstreamsLeoReadFilterData = this.container; +// } +// getMetadata(inputEvent) { +// let iid = 0; +// let icount = 1; - static eventIdFormat = "[z/]YYYY/MM/DD/HH/mm/"; +// let container: FanoutEvent = inputEvent.__cron || inputEvent || {}; - reduce: ReduceFn; - // eventPartition: EventPartitionFn; - getNeedNeedInstances: InstancesFn; - iid: number; - icount: number; - container: FanoutEvent; +// if (container && container.iid) { +// //container.iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; +// iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; +// icount = container.icount; +// } - constructor( - private processor: IProcessor, - private inputEvent: any, - private context: RStreamsContext, - private hooks: RStreamsBotHooks - ) { - this.getNeedNeedInstances = typeof this.hooks.instances === "function" ? this.hooks.instances.bind(this.hooks) : (_) => this.hooks.instances as number || 1; +// // Correct missing instances from event to __cron +// if ( +// container != inputEvent && +// container.instances && +// inputEvent.instances && +// Object.keys(container.instances).length != Object.keys(inputEvent.instances).length +// ) { +// container.instances = inputEvent.instances; +// } - //this.fixInstanceForLocal(inputEvent); - let { iid, icount, container } = this.getMetadata(inputEvent); - this.iid = iid; - this.icount = icount; - this.container = container; +// if ( +// container != inputEvent && +// container.checkpoints && +// inputEvent.checkpoints && +// Object.keys(container.checkpoints).length != Object.keys(inputEvent.checkpoints).length +// ) { +// container.checkpoints = inputEvent.checkpoints; +// } - // Setup Global RStreamsSdk Filtering - global.rstreamsLeoReadFilterEventPartition = this.hooks.eventPartition ? this.hooks.eventPartition.bind(this.hooks) : (event => event.eid); - global.rstreamsLeoReadFilterData = this.container; - } +// return { iid, icount, container }; +// } - getMetadata(inputEvent) { - let iid = 0; - let icount = 1; +// static fixInstanceForChildProcess(event: any) { +// event.__FANOUT__ = true; +// // Get fanout data from process env if running locally +// console.log("fixInstanceForChildProcess", process.env.FANOUT_data); +// if (process.env.FANOUT_data) { +// Object.assign(event, JSON.parse(process.env.FANOUT_data)); +// } +// } - let container: FanoutEvent = inputEvent.__cron || inputEvent || {}; +// isMaster(): boolean { +// return this.iid == 0 || this.icount == null; +// } - if (container && container.iid) { - //container.iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; - iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; - icount = container.icount; - } +// isWorker(): boolean { +// return !this.isMaster(); +// } - return { iid, icount, container }; - } +// async invokeLambda(iid: number, count: number, newEvent: any): Promise> { + +// let data: InvocationResponse;//FanoutInvokeResponse; +// let error; +// try { +// let lambdaApi = new aws.Lambda({ +// region: process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION, +// httpOptions: { +// timeout: this.context.getRemainingTimeInMillis() // Default: 120000 // Two minutes +// } +// }); + +// // todo: Should we invoke a new function or just run a new local process +// logger.log("[lambda]", process.env.AWS_LAMBDA_FUNCTION_NAME); +// data = await lambdaApi.invoke({ +// FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME, +// InvocationType: 'RequestResponse', +// Payload: JSON.stringify(newEvent), +// Qualifier: process.env.AWS_LAMBDA_FUNCTION_VERSION +// }).promise(); + +// } catch (err) { +// error = err; +// } - static fixInstanceForLocal(event: any) { - event.__FANOUT__ = true; - // Get fanout data from process env if running locally - console.log("fixInstanceForLocal", process.env.FANOUT_data); - if (process.env.FANOUT_data) { - Object.assign(event, JSON.parse(process.env.FANOUT_data)); - } - } - isMaster(): boolean { - return this.iid == 0 || this.icount == null; - } +// logger.log(`Done with Lambda instance ${iid + 1}/${count}`); +// logger.log("[lambda err]", error); +// logger.log("[lambda data]", data); - isWorker(): boolean { - return !this.isMaster(); - } +// let response: FanoutInvokeResponse; +// if (error) { +// throw error; +// } else if (data.FunctionError) { +// throw data.Payload; +// } else if (data.Payload != undefined && data.Payload != 'null') { +// response = JSON.parse(data.Payload as string); +// } else { +// response = data as any; +// } - async invokeLambda(iid: number, count: number, newEvent: any): Promise> { - - let data: InvocationResponse;//FanoutInvokeResponse; - let error; - try { - let lambdaApi = new aws.Lambda({ - region: process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION, - httpOptions: { - timeout: this.context.getRemainingTimeInMillis() // Default: 120000 // Two minutes - } - }); - - // todo: Should we invoke a new function or just run a new local process - logger.log("[lambda]", process.env.AWS_LAMBDA_FUNCTION_NAME); - data = await lambdaApi.invoke({ - FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME, - InvocationType: 'RequestResponse', - Payload: JSON.stringify(newEvent), - Qualifier: process.env.AWS_LAMBDA_FUNCTION_VERSION - }).promise(); - - } catch (err) { - error = err; - } +// logger.debug("[lambda invoked invocation/payload]", data, JSON.stringify(newEvent, null, 2)); +// return response; +// } +// async invokeProcess(iid: number, count: number, newEvent: any): Promise> { +// // Fork process with event +// let worker = require("child_process").fork(process.argv[1], process.argv.slice(2), { +// cwd: process.cwd(), +// env: Object.assign({}, process.env, { +// FANOUT_data: JSON.stringify(newEvent), +// runner_keep_cmd: true +// }), +// execArgv: process.execArgv +// }); + +// // setup communication between processes +// let responseData: FanoutInvokeResponse; +// worker.once("message", (response: FanoutInvokeResponse) => { +// logger.log(`Got Response with instance ${iid + 1}/${count}`); +// responseData = response; +// }); + +// return new Promise(resolve => { +// worker.once("exit", () => { +// logger.log(`Done with child instance ${iid + 1}/${count}`); +// logger.log("[responseData]", responseData); +// resolve(responseData); +// }); +// }); +// } - logger.log(`Done with Lambda instance ${iid + 1}/${count}`); - logger.log("[lambda err]", error); - logger.log("[lambda data]", data); +// async invokeSelf(event: any, iid: number, count: number): Promise> { +// logger.log(`Invoking ${iid + 1}/${count}`); + +// // Deep copy the invocation event and set instances variables +// let newEvent = JSON.parse(JSON.stringify(event)); +// let { container } = this.getMetadata(newEvent); +// container.iid = iid; +// container.icount = count; +// container.ignoreLock = true; +// // delete (container as any).force; + +// // Add starting points for all queues. +// // leo-sdk will look to this before the default checkpoints.read[queue] +// let myInstance = (event.instances || {})[iid] || {}; +// container.starteid = Object.keys(myInstance).reduce((all, key) => { +// if (key.match(/^queue:/) && myInstance[key] && myInstance[key].checkpoint) { +// all[key] = myInstance[key].checkpoint; +// } +// return all; +// }, {}); - let response: FanoutInvokeResponse; - if (error) { - throw error; - } else if (data.FunctionError) { - throw data.Payload; - } else if (data.Payload != undefined && data.Payload != 'null') { - response = JSON.parse(data.Payload as string); - } else { - response = data as any; - } +// // If running in lamba +// if (process.env.AWS_LAMBDA_FUNCTION_NAME && !process.env.IS_LOCAL && this.hooks.invokeType !== "process") { +// return await this.invokeLambda(iid, count, newEvent); +// } +// // If running local or child process +// else { +// return await this.invokeProcess(iid, count, newEvent); +// } +// } - logger.debug("[lambda invoked invocation/payload]", data, JSON.stringify(newEvent, null, 2)); - return response; - } +// async masterRun(): Promise { +// // This is the master, start the needed workers +// let timestamp = moment.utc(); +// this.container.maxeid = this.container.maxeid || this.inputEvent.maxeid || timestamp.format(FanoutProcessor.eventIdFormat) + timestamp.valueOf(); +// this.container.iid = 0; +// logger.log("Fanout Master", this.container.iid); + + +// let readFilterGroups: ReadFilterGroup[] = this.inputEvent.read_filter_groups || []; +// // Get Bot data if not provided +// if ( +// !this.container.instances || +// !this.container.checkpoints +// ) { +// let bot: BotData = await new Promise((resolve, reject) => +// this.context.sdk.aws.dynamodb.get( +// this.context.sdk.configuration.resources.LeoCron, +// this.inputEvent.botId, +// {}, +// (err, data) => err ? reject(err) : resolve(data))); + +// this.container.instances = (bot || {}).instances || {}; +// this.container.checkpoints = (bot || {}).checkpoints || { read: {}, write: {} }; +// this.inputEvent.requested_kinesis = (bot || {}).requested_kinesis || {}; +// readFilterGroups = (bot || {}).read_filter_groups || []; +// if (bot == null) { +// await this.context.sdk.bot.createBot(this.inputEvent.botId, {}); +// } +// } - async invokeProcess(iid: number, count: number, newEvent: any): Promise> { - // Fork process with event - let worker = require("child_process").fork(process.argv[1], process.argv.slice(2), { - cwd: process.cwd(), - env: Object.assign({}, process.env, { - FANOUT_data: JSON.stringify(newEvent), - runner_keep_cmd: true - }), - execArgv: process.execArgv - }); - - // setup communication between processes - let responseData: FanoutInvokeResponse; - worker.once("message", (response: FanoutInvokeResponse) => { - logger.log(`Got Response with instance ${iid + 1}/${count}`); - responseData = response; - }); - - return new Promise(resolve => { - worker.once("exit", () => { - logger.log(`Done with child instance ${iid + 1}/${count}`); - logger.log("[responseData]", responseData); - resolve(responseData); - }); - }); - } +// // Find number of requested instances +// let instances = Math.ceil(this.getNeedNeedInstances(this.inputEvent, this.container)); +// instances = Math.max(1, Math.min(instances, this.hooks.maxInstances || 20)); + +// // Add any entries that don't exist +// let command = { +// TableName: this.context.sdk.configuration.resources.LeoCron, +// Key: { +// id: this.inputEvent.botId +// }, +// UpdateExpression: undefined, +// ExpressionAttributeNames: {}, +// ExpressionAttributeValues: {} +// }; +// let toAdd = []; + +// // Filter out any dead groups +// let minEid: string = null; +// let maxEid: string = "z/"; +// let activeInstancesFromFilterGroups = new Set(); +// let maxInstancesFromGroupsPreFilter = readFilterGroups.reduce((a, b) => Math.max(a, b.icount), instances); +// for (let i = 0; i < maxInstancesFromGroupsPreFilter; i++) { +// activeInstancesFromFilterGroups.add(i.toString()); +// } +// Object.entries(this.container.instances) +// .filter(([key]) => activeInstancesFromFilterGroups.has(key)) +// .map(([_key, value]) => value) +// .concat(this.container.checkpoints.read).forEach(inst => { +// Object.entries(inst).forEach(([key, cp]) => { +// if (key.match(/^queue:/) && cp?.checkpoint) { +// minEid = (minEid && minEid.localeCompare(cp.checkpoint) <= 0) ? minEid : cp.checkpoint; +// maxEid = (maxEid && maxEid.localeCompare(cp.checkpoint) >= 0) ? maxEid : cp.checkpoint; +// } +// }); +// }); + +// // Only filter if we have a minEid and there are more than 1 group +// // Otherwise it just removed and adds the group +// if (minEid && readFilterGroups.length > 1) { +// readFilterGroups = readFilterGroups.filter(g => g.eid >= minEid); +// } +// let latest: ReadFilterGroup = readFilterGroups[readFilterGroups.length - 1]; +// if ( +// latest == null || +// ( +// latest.icount != instances && // Have a different icount +// // latest.eid < maxEid && +// (Date.now() - latest.ts) >= FanoutProcessor.ReadFilterGroupChangeDelayMS && // outside the change delay window +// true // (Date.now() - this.context.sdk.streams.eventIdToTimestamp(latest.eid)) <= FanoutProcessor.ReadFilterGroupChangeMaxEidDistanceMS // last group eid is close +// ) +// ) { +// readFilterGroups.push({ +// icount: instances, +// eid: maxEid, //this.container.maxeid, +// ts: Date.now() +// }); +// toAdd.push(`#rfg = :rfg`); +// command.ExpressionAttributeNames["#rfg"] = "read_filter_groups"; +// command.ExpressionAttributeValues[":rfg"] = readFilterGroups; +// } else { +// // Ignore the new count because they aren't allowed to change yet +// // instances = latest.icount; +// } - async invokeSelf(event: any, iid: number, count: number): Promise> { - logger.log(`Invoking ${iid + 1}/${count}`); - - // Deep copy the invocation event and set instances variables - let newEvent = JSON.parse(JSON.stringify(event)); - let { container } = this.getMetadata(newEvent); - container.iid = iid; - container.icount = count; - container.ignoreLock = true; - - // Add starting points for all queues. - // leo-sdk will look to this before the default checkpoints.read[queue] - let myInstance = (event.instances || {})[iid] || {}; - container.starteid = Object.keys(myInstance).reduce((all, key) => { - if (key.match(/^queue:/) && myInstance[key] && myInstance[key].checkpoint) { - all[key] = myInstance[key].checkpoint; - } - return all; - }, {}); - - // If running in lamba - if (process.env.AWS_LAMBDA_FUNCTION_NAME && !process.env.IS_LOCAL && this.hooks.invokeType !== "process") { - return await this.invokeLambda(iid, count, newEvent); - // try { - // let lambdaApi = new aws.Lambda({ - // region: process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION, - // httpOptions: { - // timeout: context.getRemainingTimeInMillis() // Default: 120000 // Two minutes - // } - // }); - - - // // todo: Should we invoke a new function or just run a new local process - // logger.log("[lambda]", process.env.AWS_LAMBDA_FUNCTION_NAME); - // const lambdaInvocation = lambdaApi.invoke({ - // FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME, - // InvocationType: 'RequestResponse', - // Payload: JSON.stringify(newEvent), - // Qualifier: process.env.AWS_LAMBDA_FUNCTION_VERSION - // }, (err: any, data: any) => { - // logger.log(`Done with Lambda instance ${iid + 1}/${count}`); - // try { - // logger.log("[lambda err]", err); - // logger.log("[lambda data]", data); - // if (err) { - // return reject(err); - // } else if (!err && data.FunctionError) { - // err = data.Payload; - // return reject(err); - // } else if (!err && data.Payload != undefined && data.Payload != 'null') { - // data = JSON.parse(data.Payload); - // } - - // resolve(data); - // } catch (err) { - // reject(err); - // } - // }); - // logger.debug("[lambda invoked invocation/payload]", lambdaInvocation, JSON.stringify(newEvent, null, 2)); - // } catch (err) { - // reject(err); - // } - } - // If running local or child process - else { - - return await this.invokeProcess(iid, count, newEvent); - // // Fork process with event - // let worker = require("child_process").fork(process.argv[1], process.argv.slice(2), { - // cwd: process.cwd(), - // env: Object.assign({}, process.env, { - // FANOUT_data: JSON.stringify(newEvent), - // runner_keep_cmd: true - // }), - // execArgv: process.execArgv - // }); - - // // setup communication between processes - // let responseData = {}; - // worker.once("message", (response) => { - // logger.log(`Got Response with instance ${iid + 1}/${count}`); - // responseData = response; - // }); - // worker.once("exit", () => { - // logger.log(`Done with child instance ${iid + 1}/${count}`); - // logger.log("[responseData]", responseData); - // resolve(responseData); - // }); - } - } +// this.container.icount = instances; +// this.container.read_filter_groups = readFilterGroups; - async masterRun(): Promise { - // This is the master, start the needed workers - let timestamp = moment.utc(); - this.container.maxeid = this.container.maxeid || this.inputEvent.maxeid || timestamp.format(FanoutProcessor.eventIdFormat) + timestamp.valueOf(); - this.container.iid = 0; - logger.log("Fanout Master", this.container.iid); - - - // Promise to get bot instances and checkpoints if not provided - //let setupPromise; - if (!this.inputEvent.instances || !this.inputEvent.checkpoints) { - let bot: BotData = await new Promise((resolve, reject) => - this.context.sdk.aws.dynamodb.get( - this.context.sdk.configuration.resources.LeoCron, - this.inputEvent.botId, - {}, - (err, data) => err ? reject(err) : resolve(data))); - - this.inputEvent.instances = (bot || {}).instances || {}; - this.inputEvent.checkpoints = (bot || {}).checkpoints || {}; - if (bot == null) { - await this.context.sdk.bot.createBot(this.inputEvent.botId, {}); - } - } +// let maxInstancesFromGroups = readFilterGroups.reduce((a, b) => Math.max(a, b.icount), instances); - // Find number of requested instances - let instances = this.getNeedNeedInstances(this.inputEvent, this.container); - instances = Math.max(1, Math.min(instances, this.hooks.maxInstances || 20)); - this.container.icount = instances; - - - // Add any entries that don't exist - let toAdd = []; - let command = { - TableName: this.context.sdk.configuration.resources.LeoCron, - Key: { - id: this.inputEvent.botId - }, - UpdateExpression: undefined, - ExpressionAttributeNames: { - "#instances": "instances" - }, - ExpressionAttributeValues: {} - }; - for (let i = 0; i < this.container.icount; i++) { - if (!this.inputEvent.instances[i]) { - // Add checkpoint location in the DB for this instance id - this.inputEvent.instances[i] = (this.inputEvent.checkpoints || {}).read || {}; - toAdd.push(`#instances.#i${i} = :i${i}`); - command.ExpressionAttributeNames[`#i${i}`] = `${i}`; - command.ExpressionAttributeValues[`:i${i}`] = this.inputEvent.instances[i]; - } - } +// // Add the starting point for the instances to be the new max eid +// let startingCheckpoints = Object.keys((this.container.checkpoints || {}).read || {}).reduce((out, queue) => { +// if (queue.match(/^queue:/)) { +// out[queue] = { +// checkpoint: maxEid +// }; +// } +// return out; +// }, {}); + +// for (let i = 0; i < maxInstancesFromGroups; i++) { +// // Instance doesn't exist yet +// if (!this.container.instances[i]) { +// // Add checkpoint location in the DB for this instance id +// this.container.instances[i] = startingCheckpoints; + +// toAdd.push(`#instances.#i${i} = :i${i}`); +// command.ExpressionAttributeNames[`#i${i}`] = `${i}`; +// command.ExpressionAttributeValues[`:i${i}`] = this.container.instances[i]; +// command.ExpressionAttributeNames["#instances"] = "instances"; +// this.container.instances[i].__last_activated = Date.now(); +// } else if (i != 0) { +// let changed = false; +// this.container.instances[i] = { +// ...startingCheckpoints, +// ...this.container.instances[i] +// }; + +// // Reset the starting point for the instances to be the new max eid +// Object.entries(this.container.instances[i]).forEach(([queue, cp]: [string, Checkpoint]) => { +// if (queue.match(/^queue:/) && cp && cp.checkpoint && cp.checkpoint < minEid) { +// changed = true; +// this.container.instances[i][queue] = { +// checkpoint: maxEid +// }; +// } +// }); + +// // Only update if we needed to update position +// if (changed) { +// toAdd.push(`#instances.#i${i} = :i${i}`); +// command.ExpressionAttributeNames[`#i${i}`] = `${i}`; +// command.ExpressionAttributeValues[`:i${i}`] = this.container.instances[i]; +// this.container.instances[i].__last_activated = Date.now(); +// command.ExpressionAttributeNames["#instances"] = "instances"; +// } +// } +// } - // Update the cron table with the needed instances - if (toAdd.length > 0) { - command.UpdateExpression = `set ${toAdd.join(",")}`; - logger.log("Adding Extra Worker instances", JSON.stringify(command, null, 2)); - await this.context.sdk.aws.dynamodb.docClient.update(command).promise(); - } +// // Update the cron table with the needed instances +// if (toAdd.length > 0) { +// command.UpdateExpression = `set ${toAdd.join(",")}`; +// logger.log("Updating Worker instances data", JSON.stringify(command, null, 2)); +// await this.context.sdk.aws.dynamodb.docClient.update(command).promise(); +// } - let workers: Promise>[] = []; +// let workers: Promise>[] = []; - // Start the other workers - for (let i = 1; i < instances; i++) { - workers.push(this.invokeSelf(this.inputEvent, i, instances)); - } +// // Start the other workers +// for (let i = 1; i < maxInstancesFromGroups; i++) { +// workers.push(this.invokeSelf(this.inputEvent, i, instances)); +// } - // Setup Master Worker - workers.unshift( - (async () => { - logger.log(`Invoking 1/${instances}`); - - // Handle promise or callback response - let data: Result; - let error; - try { - data = await this.processor.run(); - } catch (err) { - error = err; - } - - logger.log(`Done with instance 1 / ${instances} `); - return { - error: error, - data: data, - iid: 0 - }; - - })()); - - - // Wait for all workers to return and figure out what checkpoint to persist - logger.debug(`Waiting on all Fanout workers: count ${workers.length} `); - let responses = await Promise.all(workers); - return this.hooks.reduce ? this.hooks.reduce(responses) : void 0; - } +// // Setup Master Worker +// workers.unshift( +// (async () => { +// logger.log(`Invoking 1/${instances}`); + +// // Handle promise or callback response +// let data: Result; +// let error; +// try { +// data = await this.processor.run(); +// } catch (err) { +// error = err; +// } + +// let response = { +// error: error || this.processor.handlerResponse.error, +// data: data || this.processor.handlerResponse.data, +// iid: this.iid +// }; +// if (response.error && response.error instanceof Error) { +// response.error = { +// name: response.error.name, +// message: response.error.message, +// stack: response.error.stack +// }; +// } + +// logger.log(`Done with instance 1 / ${instances} `); +// return response; + +// })()); + + +// // Wait for all workers to return and figure out what checkpoint to persist +// logger.debug(`Waiting on all Fanout workers: count ${workers.length} `); +// let responses = await Promise.all(workers); +// return this.hooks.reduce ? this.hooks.reduce(responses) : void 0; +// } - async workerRun(): Promise { - logger.log("Fanout Worker", this.iid); - this.container.cploc = "instances"; +// async workerRun(): Promise { +// logger.log("Fanout Worker", this.iid); +// this.container.cploc = "instances"; - //let context_getRemainingTimeInMillis = this.context.getRemainingTimeInMillis; - // // save 3 seconds so the response can get back to the parent with time to process and clean up - // this.context.getRemainingTimeInMillis = () => { - // return context_getRemainingTimeInMillis.call(context) - (this.hooks.lambdaTimeoutPaddingMillis || (1000 * 3)); - // }; +// //let context_getRemainingTimeInMillis = this.context.getRemainingTimeInMillis; +// // // save 3 seconds so the response can get back to the parent with time to process and clean up +// // this.context.getRemainingTimeInMillis = () => { +// // return context_getRemainingTimeInMillis.call(context) - (this.hooks.lambdaTimeoutPaddingMillis || (1000 * 3)); +// // }; - // wrap callback to send back the response to the parent lambda/process +// // wrap callback to send back the response to the parent lambda/process - let data: Result; - let error; - try { - data = await this.processor.run(); - } catch (err) { - error = err; - } - let response = { - error: error, - data: data, - iid: this.iid - }; - logger.log("Worker sending data back", this.iid); - logger.debug("Worker sending back response", this.iid, JSON.stringify(response, null, 2)); +// let data: Result; +// let error; +// try { +// data = await this.processor.run(); +// } catch (err) { +// error = err; +// } +// let response = { +// error: error || this.processor.handlerResponse.error, +// data: data || this.processor.handlerResponse.data, +// iid: this.iid +// }; +// if (response.error && response.error instanceof Error) { +// response.error = { +// name: response.error.name, +// message: response.error.message, +// stack: response.error.stack +// }; +// } +// this.handlerResponse = this.processor.handlerResponse; +// logger.log("Worker sending data back", this.iid); +// logger.debug("Worker sending back response", this.iid, JSON.stringify(response, null, 2)); + +// if (process.send) { +// // Send response for child process worker to the master +// process.send(response); +// return; +// } else { +// // Send response for lambda worker to the master +// return response as unknown as Result; +// } +// } - if (process.send) { - // Send response for child process worker to the master - process.send(response); - return; - } else { - // Send response for lambda worker to the master - return response as unknown as Result; - } - } +// public async run(): Promise { +// logger.log("Fanout Start"); +// logger.log("Fanout Handler", this.iid); +// logger.debug("Fanout Handler Event", this.iid, JSON.stringify(this.inputEvent, null, 2)); - public async run(): Promise { - logger.log("Fanout Start"); - logger.log("Fanout Handler", this.iid); - logger.debug("Fanout Handler Event", this.iid, JSON.stringify(this.inputEvent, null, 2)); - logger.debug("Fanout Handler Context", this.iid, this.context); - - try { - if (this.isMaster()) { - return await this.masterRun(); - } else { - return await this.workerRun(); - } - //return await this.processor.run(); - } - finally { - logger.log("Fanout End"); - } - } -} +// try { +// if (this.isMaster()) { +// return await this.masterRun(); +// } else { +// return await this.workerRun(); +// } +// } +// finally { +// logger.log("Fanout End"); +// } +// } +// } export class RStreamsBot { sdk: RStreamsSdk; @@ -858,7 +891,7 @@ export class RStreamsBot { createProcessor(event: any, context: RStreamsContext): IProcessor { // if (this.isFanout) { - FanoutProcessor.fixInstanceForLocal(event); + FanoutProcessor.fixInstanceForChildProcess(event); } let processor: IProcessor; let params: ProcessorParameters = { From d5bdb387797b79d6ebeb9ae9a2f906564fbf87cd Mon Sep 17 00:00:00 2001 From: Clint Zirker Date: Thu, 26 May 2022 12:37:00 -0600 Subject: [PATCH 4/5] Removed dead code and added files I missed --- wrappers/lib/api-processor.ts | 163 ++++++ wrappers/lib/cron-processor.ts | 83 ++++ wrappers/lib/fanout-processor.ts | 497 +++++++++++++++++++ wrappers/lib/process.ts | 19 + wrappers/lib/processor.ts | 53 ++ wrappers/lib/types.ts | 40 ++ wrappers/smart.ts | 825 +------------------------------ 7 files changed, 857 insertions(+), 823 deletions(-) create mode 100644 wrappers/lib/api-processor.ts create mode 100644 wrappers/lib/cron-processor.ts create mode 100644 wrappers/lib/fanout-processor.ts create mode 100644 wrappers/lib/process.ts create mode 100644 wrappers/lib/processor.ts create mode 100644 wrappers/lib/types.ts diff --git a/wrappers/lib/api-processor.ts b/wrappers/lib/api-processor.ts new file mode 100644 index 00000000..11c04210 --- /dev/null +++ b/wrappers/lib/api-processor.ts @@ -0,0 +1,163 @@ +import { APIGatewayEventDefaultAuthorizerContext, APIGatewayEventIdentity, APIGatewayEventRequestContext, APIGatewayEventRequestContextWithAuthorizer, APIGatewayProxyEventHeaders, APIGatewayProxyEventMultiValueHeaders, APIGatewayProxyEventMultiValueQueryStringParameters, APIGatewayProxyEventPathParameters, APIGatewayProxyEventQueryStringParameters, APIGatewayProxyEventStageVariables } from "aws-lambda"; +import { Processor } from "./processor"; +import { ProcessorParameters } from "./types"; +import { config } from "./process"; + +export interface APIGatewayProxyEvent { + body: T | null; + headers: APIGatewayProxyEventHeaders; + multiValueHeaders: APIGatewayProxyEventMultiValueHeaders; + httpMethod: string; + isBase64Encoded: boolean; + path: string; + pathParameters: APIGatewayProxyEventPathParameters | null; + queryStringParameters: APIGatewayProxyEventQueryStringParameters | null; + multiValueQueryStringParameters: APIGatewayProxyEventMultiValueQueryStringParameters | null; + stageVariables: APIGatewayProxyEventStageVariables | null; + requestContext: APIGatewayEventRequestContextWithAuthorizer; + resource: string; +} + +interface APIGatewayProxyResult { + statusCode: number; + headers?: { + [header: string]: boolean | number | string; + } | undefined; + multiValueHeaders?: { + [header: string]: Array; + } | undefined; + body: string | T; + isBase64Encoded?: boolean | undefined; +} + +type APIGatewayProxyResultOrData = APIGatewayProxyResult | T; + +export class ApiProcessor, T, S> extends Processor, APIGatewayProxyResultOrData, S>{ + + public static HandlesEvent(event: any): boolean { + return event && event.httpMethod || event.headers; + } + + constructor( + params: ProcessorParameters, T, S> + ) { + super(params); + this.setupUncaughtExceptions(); + } + + public setupUncaughtExceptions() { + for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener + process.removeListener('uncaughtException', x); + } + process.on('uncaughtException', (err) => { + console.error((new Date).toUTCString() + ' uncaughtException:', err.message); + console.error(err.stack); + this.context.done(null, { + statusCode: 500, + 'Content-Type': 'application/json', + body: JSON.stringify("Application Error") + }); + }); + } + + public override async run(): Promise> { + try { + this.inputEvent = this.transformEvent(this.inputEvent); + let response = await super.run(); + + + if (response && typeof response === "object" && "statusCode" in response) { + let data = response as unknown as APIGatewayProxyResult; + if (config.cors && !("Access-Control-Allow-Origin" in data.headers)) { + data.headers["Access-Control-Allow-Origin"] = config.cors; + } + return data; + } else { + let data = response as T; + return { + statusCode: 200, + headers: { + 'Content-Type': config.ContentType || 'application/json', + "Access-Control-Allow-Origin": config.cors ? config.cors : undefined + }, + body: JSON.stringify(data) + }; + } + + } catch (err) { + if (err === "Access Denied" || err === "Error: Access Denied") { + return { + statusCode: 403, + headers: { + 'Content-Type': config.ErrorContentType || 'text/html', + "Access-Control-Allow-Origin": config.cors ? config.cors : undefined + }, + body: err.toString() + }; + } else { + if (typeof err === "object" && "statusCode" in err) { + if (config.cors && err.headers && !("Access-Control-Allow-Origin" in err.headers)) { + err.headers["Access-Control-Allow-Origin"] = config.cors; + } + return err; + } else { + return { + statusCode: 500, + headers: { + 'Content-Type': config.ErrorContentType || 'text/html', + "Access-Control-Allow-Origin": config.cors ? config.cors : undefined + }, + body: err.toString() + }; + } + } + } + } + + transformEvent(inputEvent: unknown): APIGatewayProxyEvent { + + let outEvent: APIGatewayProxyEvent; + + if (this.context.identity) { + // Called Directly not via Api Gateway + let event = inputEvent as APIGatewayProxyEvent; + outEvent = { + body: event.body, + httpMethod: event.httpMethod, + queryStringParameters: event.queryStringParameters, + pathParameters: null, + multiValueHeaders: null, + multiValueQueryStringParameters: null, + isBase64Encoded: false, + path: "", + resource: "", + stageVariables: null, + headers: { + Cookie: event.headers && event.headers.Cookie, + }, + requestContext: { + requestId: this.context.awsRequestId, + identity: this.context.identity as APIGatewayEventIdentity + } as APIGatewayEventRequestContext + }; + + } else { + outEvent = inputEvent as APIGatewayProxyEvent; + } + + + if (outEvent.isBase64Encoded) { + outEvent.body = Buffer.from(outEvent.body as string, 'base64').toString(); + } + if (outEvent.body && typeof outEvent.body !== "object") { + outEvent.body = JSON.parse(outEvent.body); + } + Object.keys(outEvent.pathParameters).map((key) => { + outEvent.pathParameters[key] = decodeURIComponent(outEvent.pathParameters[key]); + }); + outEvent.pathParameters = outEvent.pathParameters || {}; + outEvent.queryStringParameters = outEvent.queryStringParameters || {}; + + return outEvent as APIGatewayProxyEvent; + } +} diff --git a/wrappers/lib/cron-processor.ts b/wrappers/lib/cron-processor.ts new file mode 100644 index 00000000..e7b691cd --- /dev/null +++ b/wrappers/lib/cron-processor.ts @@ -0,0 +1,83 @@ +import { Milliseconds, ReportCompleteOptions } from "../../lib/cron"; +import { BotInvocationEvent, Cron } from "../../index"; +import { InstanceStatus, ProcessorParameters } from "./types"; +import { promisify } from "util"; +import { Processor } from "./processor"; +import { config } from "./process"; +import leoLogger from "leo-logger"; + +const logger = leoLogger("wrapper.cron-processor"); + +export class CronProcessor extends Processor { + public static HandlesEvent(event: any): boolean { + return event && event.__cron != null; + } + + id: string; + cron: Cron; + checkLock: (cron: Cron, runid: string, remainingTime: Milliseconds) => Promise; + reportComplete: (cron: Cron, runid: string, status: string, log: any, opts: ReportCompleteOptions) => Promise; + + constructor( + params: ProcessorParameters + ) { + super(params); + this.cron = this.inputEvent.__cron; + console.log("IID:", this.cron.iid); + this.id = `${this.cron.id}:${this.cron.iid}:${this.cron.ts}:${this.context.awsRequestId}`; + this.checkLock = promisify(this.context.sdk.bot.checkLock).bind(this.context.sdk.bot); + this.reportComplete = promisify(this.context.sdk.bot.reportComplete).bind(this.context.sdk.bot); + + this.setupUncaughtExceptions(); + } + + public setupUncaughtExceptions() { + for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener + process.removeListener('uncaughtException', x); + } + process.on('uncaughtException', async (err) => { + console.log(`[LEOCRON]:end:${this.id}`); + logger.error((new Date).toUTCString() + ' uncaughtException:', err.message); + logger.error(err.stack); + await this.releaseLock(err); + }); + } + + public async obtainLock() { + try { + console.log("[LEOCRON]:check:" + this.id); + await this.checkLock(this.cron, this.id, this.context.getRemainingTimeInMillis()); + console.log("[LEOCRON]:start:" + this.id); + } catch (err) { + if (err.code === "ConditionalCheckFailedException") { + throw new Error("already running"); + } else { + throw new Error("failed getting lock"); + } + } + } + + public async releaseLock(error: any) { + console.log("[LEOCRON]:complete:" + this.id); + await this.reportComplete(this.cron, this.id, error ? InstanceStatus.error : InstanceStatus.complete, error ? error : "", {}); + } + + override setupRegistory() { + + config.registry.__cron = this.cron; + config.registry.id = this.cron.id; + } + + public override async run( + ): Promise { + await this.obtainLock(); + let error: Error; + try { + return await super.run(); + } catch (err) { + error = err; + } finally { + await this.releaseLock(error); + } + } +} diff --git a/wrappers/lib/fanout-processor.ts b/wrappers/lib/fanout-processor.ts new file mode 100644 index 00000000..9886b294 --- /dev/null +++ b/wrappers/lib/fanout-processor.ts @@ -0,0 +1,497 @@ +import { InvocationResponse } from "aws-sdk/clients/lambda"; +import { Checkpoints, Checkpoint, RStreamsContext, } from "../../index"; +import { BotData, InstanceData, ReadFilterGroup } from "../../lib/cron"; +import { InstancesFn, IProcessor, ReduceFn, RStreamsBotHooks } from "./types"; +import aws from "aws-sdk"; +import moment from "moment"; +import leoLogger from "leo-logger"; +import { config } from "./process"; + +const logger = leoLogger("wrapper.fanout-processor"); + + +export interface FanoutEvent { + iid?: number; + + /** The total number of bot instances */ + icount?: number; + + /** Starting positions for a given queue */ + starteid?: { + [key: string]: Checkpoint | string // not sure which this is yet + }, + + /** */ + read_filter_groups?: ReadFilterGroup[]; + + ignoreLock?: boolean; + + + /** List of read/write positions of sources and destinations for this bot */ + checkpoints?: Checkpoints; + instances?: Record + + maxeid: string + cploc: "checkpoints" | "instances" + +} + +export interface FanoutInvokeResponse { + error: any; + data: Result; + iid: number; +} + +export class FanoutProcessor implements IProcessor{ + + static eventIdFormat = "[z/]YYYY/MM/DD/HH/mm/"; + static ReadFilterGroupChangeDelayMS: number = 1000 * 60 * 15; // Can only change filter group size every 15 minutes + static ReadFilterGroupChangeMaxEidDistanceMS: number = 1000 * 60 * 15; // Must be within 15 minutes of the last filter group eid + + public handlerResponse: { error?: Error; data?: Result; } = {}; + + reduce: ReduceFn; + getNeedNeedInstances: InstancesFn; + iid: number; + icount: number; + container: FanoutEvent; + + constructor( + private processor: IProcessor, + private inputEvent: any, + private context: RStreamsContext, + private hooks: RStreamsBotHooks + ) { + this.getNeedNeedInstances = typeof this.hooks.instances === "function" ? this.hooks.instances.bind(this.hooks) : (_) => this.hooks.instances as number || 1; + + let { iid, icount, container } = this.getMetadata(inputEvent); + + config.registry.rstreamsLeoReadFilterContainer = container; + this.iid = iid; + this.icount = icount; + this.container = container; + + // Setup Global RStreamsSdk Filtering + global.rstreamsLeoReadFilterEventPartition = this.hooks.eventPartition ? this.hooks.eventPartition.bind(this.hooks) : (event => event.eid); + global.rstreamsLeoReadFilterData = this.container; + } + + getMetadata(inputEvent) { + let iid = 0; + let icount = 1; + + let container: FanoutEvent = inputEvent.__cron || inputEvent || {}; + + if (container && container.iid) { + //container.iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; + iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; + icount = container.icount; + } + + // Correct missing instances from event to __cron + if ( + container != inputEvent && + container.instances && + inputEvent.instances && + Object.keys(container.instances).length != Object.keys(inputEvent.instances).length + ) { + container.instances = inputEvent.instances; + } + + if ( + container != inputEvent && + container.checkpoints && + inputEvent.checkpoints && + Object.keys(container.checkpoints).length != Object.keys(inputEvent.checkpoints).length + ) { + container.checkpoints = inputEvent.checkpoints; + } + + return { iid, icount, container }; + } + + static fixInstanceForChildProcess(event: any) { + event.__FANOUT__ = true; + // Get fanout data from process env if running locally + if (process.env.FANOUT_data) { + Object.assign(event, JSON.parse(process.env.FANOUT_data)); + } + } + + isMaster(): boolean { + return this.iid == 0 || this.icount == null || this.icount == 1; + } + + isWorker(): boolean { + return !this.isMaster(); + } + + async invokeLambda(iid: number, count: number, newEvent: any): Promise> { + + let data: InvocationResponse;//FanoutInvokeResponse; + let error; + try { + let lambdaApi = new aws.Lambda({ + region: process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION, + httpOptions: { + timeout: this.context.getRemainingTimeInMillis() // Default: 120000 // Two minutes + } + }); + + // todo: Should we invoke a new function or just run a new local process + logger.log("[lambda]", process.env.AWS_LAMBDA_FUNCTION_NAME); + data = await lambdaApi.invoke({ + FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME, + InvocationType: 'RequestResponse', + Payload: JSON.stringify(newEvent), + Qualifier: process.env.AWS_LAMBDA_FUNCTION_VERSION + }).promise(); + + } catch (err) { + error = err; + } + + + logger.log(`Done with Lambda instance ${iid + 1}/${count}`); + logger.log("[lambda err]", error); + logger.log("[lambda data]", data); + + let response: FanoutInvokeResponse; + if (error) { + throw error; + } else if (data.FunctionError) { + throw data.Payload; + } else if (data.Payload != undefined && data.Payload != 'null') { + response = JSON.parse(data.Payload as string); + } else { + response = data as any; + } + + logger.debug("[lambda invoked invocation/payload]", data, JSON.stringify(newEvent, null, 2)); + return response; + } + + async invokeProcess(iid: number, count: number, newEvent: any): Promise> { + // Fork process with event + let worker = require("child_process").fork(process.argv[1], process.argv.slice(2), { + cwd: process.cwd(), + env: Object.assign({}, process.env, { + FANOUT_data: JSON.stringify(newEvent), + runner_keep_cmd: true + }), + execArgv: process.execArgv + }); + + // setup communication between processes + let responseData: FanoutInvokeResponse; + worker.once("message", (response: FanoutInvokeResponse) => { + logger.log(`Got Response with instance ${iid + 1}/${count}`); + responseData = response; + }); + + return new Promise(resolve => { + worker.once("exit", () => { + logger.log(`Done with child instance ${iid + 1}/${count}`); + logger.log("[responseData]", responseData); + resolve(responseData); + }); + }); + } + + async invokeSelf(event: any, iid: number, count: number): Promise> { + logger.log(`Invoking ${iid + 1}/${count}`); + + // Deep copy the invocation event and set instances variables + let newEvent = JSON.parse(JSON.stringify(event)); + let { container } = this.getMetadata(newEvent); + container.iid = iid; + container.icount = count; + container.ignoreLock = true; + // delete (container as any).force; + + // Add starting points for all queues. + // leo-sdk will look to this before the default checkpoints.read[queue] + let myInstance = (event.instances || {})[iid] || {}; + container.starteid = Object.keys(myInstance).reduce((all, key) => { + if (key.match(/^queue:/) && myInstance[key] && myInstance[key].checkpoint) { + all[key] = myInstance[key].checkpoint; + } + return all; + }, {}); + + // If running in lamba + if (process.env.AWS_LAMBDA_FUNCTION_NAME && !process.env.IS_LOCAL && this.hooks.invokeType !== "process") { + return await this.invokeLambda(iid, count, newEvent); + } + // If running local or child process + else { + return await this.invokeProcess(iid, count, newEvent); + } + } + + async masterRun(): Promise { + // This is the master, start the needed workers + let timestamp = moment.utc(); + this.container.maxeid = this.container.maxeid || this.inputEvent.maxeid || timestamp.format(FanoutProcessor.eventIdFormat) + timestamp.valueOf(); + this.container.iid = 0; + logger.log("Fanout Master", this.container.iid); + + + let readFilterGroups: ReadFilterGroup[] = this.inputEvent.read_filter_groups || []; + // Get Bot data if not provided + if ( + !this.container.instances || + !this.container.checkpoints + ) { + let bot: BotData = await new Promise((resolve, reject) => + this.context.sdk.aws.dynamodb.get( + this.context.sdk.configuration.resources.LeoCron, + this.inputEvent.botId, + {}, + (err, data) => err ? reject(err) : resolve(data))); + + this.container.instances = (bot || {}).instances || {}; + this.container.checkpoints = (bot || {}).checkpoints || { read: {}, write: {} }; + this.inputEvent.requested_kinesis = (bot || {}).requested_kinesis || {}; + readFilterGroups = (bot || {}).read_filter_groups || []; + if (bot == null) { + await this.context.sdk.bot.createBot(this.inputEvent.botId, {}); + } + } + + // Find number of requested instances + let instances = Math.ceil(this.getNeedNeedInstances(this.inputEvent, this.container)); + instances = Math.max(1, Math.min(instances, this.hooks.maxInstances || 20)); + + // Add any entries that don't exist + let command = { + TableName: this.context.sdk.configuration.resources.LeoCron, + Key: { + id: this.inputEvent.botId + }, + UpdateExpression: undefined, + ExpressionAttributeNames: {}, + ExpressionAttributeValues: {} + }; + let toAdd = []; + + // Filter out any dead groups + let minEid: string = null; + let maxEid: string = "z/"; + let activeInstancesFromFilterGroups = new Set(); + let maxInstancesFromGroupsPreFilter = readFilterGroups.reduce((a, b) => Math.max(a, b.icount), instances); + for (let i = 0; i < maxInstancesFromGroupsPreFilter; i++) { + activeInstancesFromFilterGroups.add(i.toString()); + } + Object.entries(this.container.instances) + .filter(([key]) => activeInstancesFromFilterGroups.has(key)) + .map(([_key, value]) => value) + .concat(this.container.checkpoints.read).forEach(inst => { + Object.entries(inst).forEach(([key, cp]) => { + if (key.match(/^queue:/) && cp?.checkpoint) { + minEid = (minEid && minEid.localeCompare(cp.checkpoint) <= 0) ? minEid : cp.checkpoint; + maxEid = (maxEid && maxEid.localeCompare(cp.checkpoint) >= 0) ? maxEid : cp.checkpoint; + } + }); + }); + + // Only filter if we have a minEid and there are more than 1 group + // Otherwise it just removed and adds the group + if (minEid && readFilterGroups.length > 1) { + readFilterGroups = readFilterGroups.filter(g => g.eid >= minEid); + } + let latest: ReadFilterGroup = readFilterGroups[readFilterGroups.length - 1]; + if ( + latest == null || + ( + latest.icount != instances && // Have a different icount + // latest.eid < maxEid && + (Date.now() - latest.ts) >= FanoutProcessor.ReadFilterGroupChangeDelayMS && // outside the change delay window + true // (Date.now() - this.context.sdk.streams.eventIdToTimestamp(latest.eid)) <= FanoutProcessor.ReadFilterGroupChangeMaxEidDistanceMS // last group eid is close + ) + ) { + readFilterGroups.push({ + icount: instances, + eid: maxEid, //this.container.maxeid, + ts: Date.now() + }); + toAdd.push(`#rfg = :rfg`); + command.ExpressionAttributeNames["#rfg"] = "read_filter_groups"; + command.ExpressionAttributeValues[":rfg"] = readFilterGroups; + } else { + // Ignore the new count because they aren't allowed to change yet + // instances = latest.icount; + } + + this.container.icount = instances; + this.container.read_filter_groups = readFilterGroups; + + let maxInstancesFromGroups = readFilterGroups.reduce((a, b) => Math.max(a, b.icount), instances); + + // Add the starting point for the instances to be the new max eid + let startingCheckpoints = Object.keys((this.container.checkpoints || {}).read || {}).reduce((out, queue) => { + if (queue.match(/^queue:/)) { + out[queue] = { + checkpoint: maxEid + }; + } + return out; + }, {}); + + for (let i = 0; i < maxInstancesFromGroups; i++) { + // Instance doesn't exist yet + if (!this.container.instances[i]) { + // Add checkpoint location in the DB for this instance id + this.container.instances[i] = startingCheckpoints; + + toAdd.push(`#instances.#i${i} = :i${i}`); + command.ExpressionAttributeNames[`#i${i}`] = `${i}`; + command.ExpressionAttributeValues[`:i${i}`] = this.container.instances[i]; + command.ExpressionAttributeNames["#instances"] = "instances"; + this.container.instances[i].__last_activated = Date.now(); + } else if (i != 0) { + let changed = false; + this.container.instances[i] = { + ...startingCheckpoints, + ...this.container.instances[i] + }; + + // Reset the starting point for the instances to be the new max eid + Object.entries(this.container.instances[i]).forEach(([queue, cp]: [string, Checkpoint]) => { + if (queue.match(/^queue:/) && cp && cp.checkpoint && cp.checkpoint < minEid) { + changed = true; + this.container.instances[i][queue] = { + checkpoint: maxEid + }; + } + }); + + // Only update if we needed to update position + if (changed) { + toAdd.push(`#instances.#i${i} = :i${i}`); + command.ExpressionAttributeNames[`#i${i}`] = `${i}`; + command.ExpressionAttributeValues[`:i${i}`] = this.container.instances[i]; + this.container.instances[i].__last_activated = Date.now(); + command.ExpressionAttributeNames["#instances"] = "instances"; + } + } + } + + // Update the cron table with the needed instances + if (toAdd.length > 0) { + command.UpdateExpression = `set ${toAdd.join(",")}`; + logger.log("Updating Worker instances data", JSON.stringify(command, null, 2)); + await this.context.sdk.aws.dynamodb.docClient.update(command).promise(); + } + + let workers: Promise>[] = []; + + // Start the other workers + for (let i = 1; i < maxInstancesFromGroups; i++) { + workers.push(this.invokeSelf(this.inputEvent, i, instances)); + } + + // Setup Master Worker + workers.unshift( + (async () => { + logger.log(`Invoking 1/${instances}`); + + // Handle promise or callback response + let data: Result; + let error; + try { + data = await this.processor.run(); + } catch (err) { + error = err; + } + + let response = { + error: error || this.processor.handlerResponse.error, + data: data || this.processor.handlerResponse.data, + iid: this.iid + }; + if (response.error && response.error instanceof Error) { + response.error = { + name: response.error.name, + message: response.error.message, + stack: response.error.stack + }; + } + + logger.log(`Done with instance 1 / ${instances} `); + return response; + + })()); + + + // Wait for all workers to return and figure out what checkpoint to persist + logger.debug(`Waiting on all Fanout workers: count ${workers.length} `); + let responses = await Promise.all(workers); + return this.hooks.reduce ? this.hooks.reduce(responses) : void 0; + } + + async workerRun(): Promise { + logger.log("Fanout Worker", this.iid); + this.container.cploc = "instances"; + + + //let context_getRemainingTimeInMillis = this.context.getRemainingTimeInMillis; + // // save 3 seconds so the response can get back to the parent with time to process and clean up + // this.context.getRemainingTimeInMillis = () => { + // return context_getRemainingTimeInMillis.call(context) - (this.hooks.lambdaTimeoutPaddingMillis || (1000 * 3)); + // }; + + // wrap callback to send back the response to the parent lambda/process + + + + let data: Result; + let error; + try { + data = await this.processor.run(); + } catch (err) { + error = err; + } + let response = { + error: error || this.processor.handlerResponse.error, + data: data || this.processor.handlerResponse.data, + iid: this.iid + }; + if (response.error && response.error instanceof Error) { + response.error = { + name: response.error.name, + message: response.error.message, + stack: response.error.stack + }; + } + this.handlerResponse = this.processor.handlerResponse; + logger.log("Worker sending data back", this.iid); + logger.debug("Worker sending back response", this.iid, JSON.stringify(response, null, 2)); + + if (process.send) { + // Send response for child process worker to the master + process.send(response); + return; + } else { + // Send response for lambda worker to the master + return response as unknown as Result; + } + } + + public async run(): Promise { + logger.log("Fanout Start"); + logger.log("Fanout Handler", this.iid); + logger.debug("Fanout Handler Event", this.iid, JSON.stringify(this.inputEvent, null, 2)); + + try { + if (this.isMaster()) { + return await this.masterRun(); + } else { + return await this.workerRun(); + } + } + finally { + logger.log("Fanout End"); + } + } +} diff --git a/wrappers/lib/process.ts b/wrappers/lib/process.ts new file mode 100644 index 00000000..21a7b7c5 --- /dev/null +++ b/wrappers/lib/process.ts @@ -0,0 +1,19 @@ +import leoConfig from "../../leoConfigure"; + +export interface RStreamsProcess extends NodeJS.Process { + __config: unknown; + resources: Record; +} + +const rstreamsProcess = process as unknown as RStreamsProcess; + +if (rstreamsProcess.__config == null) { + rstreamsProcess.__config = leoConfig; + rstreamsProcess.env.TZ = leoConfig.timezone; + rstreamsProcess.resources = process.env.Resources && JSON.parse(process.env.Resources) || {}; +} + +export const config = leoConfig; +export const registry = leoConfig.registry; +export const resources = rstreamsProcess.resources; +export default rstreamsProcess; diff --git a/wrappers/lib/processor.ts b/wrappers/lib/processor.ts new file mode 100644 index 00000000..60b8bf64 --- /dev/null +++ b/wrappers/lib/processor.ts @@ -0,0 +1,53 @@ +import { RStreamsContext } from "../../lib/types"; +import { IProcessor, ProcessorParameters } from "./types"; +import { config } from "./process"; + +import leoLogger from "leo-logger"; + +const logger = leoLogger("wrapper.base-processor"); + +export class Processor implements IProcessor { + + public handlerResponse: { + error?: Error, + data?: T + } = {}; + protected inputEvent: E; + protected context: RStreamsContext; + private init: Promise; + private fn: (event: E, context: RStreamsContext) => Promise; + constructor( + params: ProcessorParameters + ) { + this.inputEvent = params.inputEvent; + this.context = params.context; + this.init = params.init; + this.fn = params.fn; + } + + initialize() { + this.setupRegistory(); + } + + public async run(): Promise { + const startTime = Date.now(); + try { + await this.init; // Init requester first to setup any dependencies + this.initialize(); // Init self + this.handlerResponse.data = await this.fn(this.inputEvent, this.context); // Run function + return this.handlerResponse.data; + } catch (err) { + this.handlerResponse.error = err; + throw err; + } finally { + let duration = Date.now() - startTime; + logger.debug(`[PROCESSOR]:duration:${duration}`); + } + } + + protected setupRegistory() { + if (!config.registry.id) { + config.registry.id = process.env.AWS_LAMBDA_FUNCTION_NAME; + } + } +} diff --git a/wrappers/lib/types.ts b/wrappers/lib/types.ts new file mode 100644 index 00000000..43c2725c --- /dev/null +++ b/wrappers/lib/types.ts @@ -0,0 +1,40 @@ +import { RStreamsContext } from "../../index"; +import { RStreamsBot } from "../smart"; +import { FanoutEvent, FanoutInvokeResponse } from "./fanout-processor"; + + +export type EventPartitionFn = (event: any) => string | number | string[] | number[]; +export type InstancesFn = ((invocationEvent: InvocationEvent, cronData: FanoutEvent) => number); +export type InitializeFn = (invoker: RStreamsBot) => Promise; +export type HandlerFn = (inputEvent: InvocationEvent, context: RStreamsContext) => Promise; +export type ReduceFn = (responses: FanoutInvokeResponse[]) => Result; + +export interface RStreamsBotHooks { + initialize?: InitializeFn; + handler: HandlerFn; + eventPartition?: EventPartitionFn; + instances?: number | InstancesFn; + maxInstances?: number; // Defaults to 20 todo: should there be a default + invokeType?: "process" | "lambda"; // run as child process or invoke new lamba + reduce?: ReduceFn +} + +export interface ProcessorParameters { + inputEvent: E; + context: RStreamsContext; + init: Promise; + fn: (event: E, context: RStreamsContext) => Promise; +} + +export interface IProcessor { + handlerResponse: { + error?: Error, + data?: T + }; + run(): Promise; +} + +export enum InstanceStatus { + error = "error", + complete = "complete" +} diff --git a/wrappers/smart.ts b/wrappers/smart.ts index 38a80b08..5aa4cdcc 100644 --- a/wrappers/smart.ts +++ b/wrappers/smart.ts @@ -1,13 +1,8 @@ -import { APIGatewayEventDefaultAuthorizerContext, APIGatewayEventIdentity, APIGatewayEventRequestContext, APIGatewayEventRequestContextWithAuthorizer, APIGatewayProxyEventHeaders, APIGatewayProxyEventMultiValueHeaders, APIGatewayProxyEventMultiValueQueryStringParameters, APIGatewayProxyEventPathParameters, APIGatewayProxyEventQueryStringParameters, APIGatewayProxyEventStageVariables, Context } from "aws-lambda"; -//import { BotData, InstanceData, LeoCron, Milliseconds, ReadFilterGroup, ReportCompleteOptions } from "../lib/cron"; -import { BotInvocationEvent, Checkpoint, Checkpoints, Cron, RStreamsContext, RStreamsSdk, throughAsync } from "../index"; +import { Context } from "aws-lambda"; +import { BotInvocationEvent, RStreamsContext, RStreamsSdk } from "../index"; import refUtil from "../lib/reference"; -//import { promisify } from "util"; import config from "../leoConfigure"; import leoLogger from "leo-logger"; -//import moment from "moment"; -//import aws, { AWSError } from "aws-sdk"; -//import { InvocationResponse } from "aws-sdk/clients/lambda"; import { IProcessor, ProcessorParameters, RStreamsBotHooks } from "./lib/types"; import { FanoutProcessor } from "./lib/fanout-processor"; import { CronProcessor } from "./lib/cron-processor"; @@ -20,825 +15,9 @@ export * from "./lib/fanout-processor"; const logger = leoLogger("smart.wrapper"); -// interface RStreamsProcess extends NodeJS.Process { -// __config: unknown; -// resources: Record; -// } - -// const rstreamsProcess = process as unknown as RStreamsProcess; - -// rstreamsProcess.__config = config; -// rstreamsProcess.env.TZ = config.timezone; -// rstreamsProcess.resources = process.env.Resources && JSON.parse(process.env.Resources) || {}; - const botId = config.name; const settings = config.cron && config.cron.settings || {}; -// enum InstanceStatus { -// error = "error", -// complete = "complete" -// } - -// type EventPartitionFn = (event: any) => string | number | string[] | number[]; -// type InstancesFn = ((invocationEvent: InvocationEvent, cronData: FanoutEvent) => number); -// type InitializeFn = (invoker: RStreamsBot) => Promise; -// type HandlerFn = (inputEvent: InvocationEvent, context: RStreamsContext) => Promise; -// type ReduceFn = (responses: FanoutInvokeResponse[]) => Result; - -// export interface RStreamsBotHooks { -// initialize?: InitializeFn; -// handler: HandlerFn; -// eventPartition?: EventPartitionFn; -// instances?: number | InstancesFn; -// maxInstances?: number; // Defaults to 20 todo: should there be a default -// invokeType?: "process" | "lambda"; // run as child process or invoke new lamba -// reduce?: ReduceFn -// } - - -// interface ProcessorParameters { -// inputEvent: E; -// context: RStreamsContext; -// init: Promise; -// fn: (event: E, context: RStreamsContext) => Promise; -// } - -// interface IProcessor { -// handlerResponse: { -// error?: Error, -// data?: T -// }; -// run(): Promise; -// } - - -// class Processor implements IProcessor { - -// public handlerResponse: { -// error?: Error, -// data?: T -// } = {}; -// protected inputEvent: E; -// protected context: RStreamsContext; -// private init: Promise; -// private fn: (event: E, context: RStreamsContext) => Promise; -// constructor( -// params: ProcessorParameters -// ) { -// this.inputEvent = params.inputEvent; -// this.context = params.context; -// this.init = params.init; -// this.fn = params.fn; -// } - -// initialize() { -// this.setupRegistory(); -// } - -// public async run(): Promise { -// const startTime = Date.now(); -// try { -// await this.init; // Init requester first to setup any dependencies -// this.initialize(); // Init self -// this.handlerResponse.data = await this.fn(this.inputEvent, this.context); // Run function -// return this.handlerResponse.data; -// } catch (err) { -// this.handlerResponse.error = err; -// throw err; -// } finally { -// let duration = Date.now() - startTime; -// logger.debug(`[PROCESSOR]:duration:${duration}`); -// } -// } - -// protected setupRegistory() { -// if (!config.registry.id) { -// config.registry.id = process.env.AWS_LAMBDA_FUNCTION_NAME; -// } -// } -// } - -// class CronProcessor extends Processor { -// public static HandlesEvent(event: any): boolean { -// return event && event.__cron != null; -// } - -// id: string; -// cron: Cron; -// checkLock: (cron: Cron, runid: string, remainingTime: Milliseconds) => Promise; -// reportComplete: (cron: Cron, runid: string, status: string, log: any, opts: ReportCompleteOptions) => Promise; - -// constructor( -// params: ProcessorParameters -// ) { -// super(params); -// this.cron = this.inputEvent.__cron; -// console.log("IID:", this.cron.iid); -// this.id = `${this.cron.id}:${this.cron.iid}:${this.cron.ts}:${this.context.awsRequestId}`; -// this.checkLock = promisify(this.context.sdk.bot.checkLock).bind(this.context.sdk.bot); -// this.reportComplete = promisify(this.context.sdk.bot.reportComplete).bind(this.context.sdk.bot); - -// this.setupUncaughtExceptions(); -// } - -// public setupUncaughtExceptions() { -// for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener -// process.removeListener('uncaughtException', x); -// } -// process.on('uncaughtException', async (err) => { -// console.log(`[LEOCRON]:end:${this.id}`); -// logger.error((new Date).toUTCString() + ' uncaughtException:', err.message); -// logger.error(err.stack); -// await this.releaseLock(err); -// }); -// } - -// public async obtainLock() { -// try { -// console.log("[LEOCRON]:check:" + this.id); -// await this.checkLock(this.cron, this.id, this.context.getRemainingTimeInMillis()); -// console.log("[LEOCRON]:start:" + this.id); -// } catch (err) { -// if (err.code === "ConditionalCheckFailedException") { -// throw new Error("already running"); -// } else { -// throw new Error("failed getting lock"); -// } -// } -// } - -// public async releaseLock(error: any) { -// console.log("[LEOCRON]:complete:" + this.id); -// await this.reportComplete(this.cron, this.id, error ? InstanceStatus.error : InstanceStatus.complete, error ? error : "", {}); -// } - -// override setupRegistory() { - -// config.registry.__cron = this.cron; -// config.registry.id = this.cron.id; -// } - -// public override async run( -// ): Promise { -// await this.obtainLock(); -// let error: Error; -// try { -// return await super.run(); -// } catch (err) { -// error = err; -// } finally { -// await this.releaseLock(error); -// } -// } -// } - -// interface APIGatewayProxyEvent { -// body: T | null; -// headers: APIGatewayProxyEventHeaders; -// multiValueHeaders: APIGatewayProxyEventMultiValueHeaders; -// httpMethod: string; -// isBase64Encoded: boolean; -// path: string; -// pathParameters: APIGatewayProxyEventPathParameters | null; -// queryStringParameters: APIGatewayProxyEventQueryStringParameters | null; -// multiValueQueryStringParameters: APIGatewayProxyEventMultiValueQueryStringParameters | null; -// stageVariables: APIGatewayProxyEventStageVariables | null; -// requestContext: APIGatewayEventRequestContextWithAuthorizer; -// resource: string; -// } - -// interface APIGatewayProxyResult { -// statusCode: number; -// headers?: { -// [header: string]: boolean | number | string; -// } | undefined; -// multiValueHeaders?: { -// [header: string]: Array; -// } | undefined; -// body: string | T; -// isBase64Encoded?: boolean | undefined; -// } - -// type APIGatewayProxyResultOrData = APIGatewayProxyResult | T; - -// class ApiProcessor, T, S> extends Processor, APIGatewayProxyResultOrData, S>{ - -// public static HandlesEvent(event: any): boolean { -// return event && event.httpMethod || event.headers; -// } - -// constructor( -// params: ProcessorParameters, T, S> -// ) { -// super(params); -// this.setupUncaughtExceptions(); -// } - -// public setupUncaughtExceptions() { -// for (let x of process.listeners('uncaughtException')) { //remove lambdas default listener -// process.removeListener('uncaughtException', x); -// } -// process.on('uncaughtException', (err) => { -// console.error((new Date).toUTCString() + ' uncaughtException:', err.message); -// console.error(err.stack); -// this.context.done(null, { -// statusCode: 500, -// 'Content-Type': 'application/json', -// body: JSON.stringify("Application Error") -// }); -// }); -// } - -// public override async run(): Promise> { -// try { -// this.inputEvent = this.transformEvent(this.inputEvent); -// let response = await super.run(); - - -// if (response && typeof response === "object" && "statusCode" in response) { -// let data = response as unknown as APIGatewayProxyResult; -// if (config.cors && !("Access-Control-Allow-Origin" in data.headers)) { -// data.headers["Access-Control-Allow-Origin"] = config.cors; -// } -// return data; -// } else { -// let data = response as T; -// return { -// statusCode: 200, -// headers: { -// 'Content-Type': config.ContentType || 'application/json', -// "Access-Control-Allow-Origin": config.cors ? config.cors : undefined -// }, -// body: JSON.stringify(data) -// }; -// } - -// } catch (err) { -// if (err === "Access Denied" || err === "Error: Access Denied") { -// return { -// statusCode: 403, -// headers: { -// 'Content-Type': config.ErrorContentType || 'text/html', -// "Access-Control-Allow-Origin": config.cors ? config.cors : undefined -// }, -// body: err.toString() -// }; -// } else { -// if (typeof err === "object" && "statusCode" in err) { -// if (config.cors && err.headers && !("Access-Control-Allow-Origin" in err.headers)) { -// err.headers["Access-Control-Allow-Origin"] = config.cors; -// } -// return err; -// } else { -// return { -// statusCode: 500, -// headers: { -// 'Content-Type': config.ErrorContentType || 'text/html', -// "Access-Control-Allow-Origin": config.cors ? config.cors : undefined -// }, -// body: err.toString() -// }; -// } -// } -// } -// } - -// transformEvent(inputEvent: unknown): APIGatewayProxyEvent { - -// let outEvent: APIGatewayProxyEvent; - -// if (this.context.identity) { -// // Called Directly not via Api Gateway -// let event = inputEvent as APIGatewayProxyEvent; -// outEvent = { -// body: event.body, -// httpMethod: event.httpMethod, -// queryStringParameters: event.queryStringParameters, -// pathParameters: null, -// multiValueHeaders: null, -// multiValueQueryStringParameters: null, -// isBase64Encoded: false, -// path: "", -// resource: "", -// stageVariables: null, -// headers: { -// Cookie: event.headers && event.headers.Cookie, -// }, -// requestContext: { -// requestId: this.context.awsRequestId, -// identity: this.context.identity as APIGatewayEventIdentity -// } as APIGatewayEventRequestContext -// }; - -// } else { -// outEvent = inputEvent as APIGatewayProxyEvent; -// } - - -// if (outEvent.isBase64Encoded) { -// outEvent.body = Buffer.from(outEvent.body as string, 'base64').toString(); -// } -// if (outEvent.body && typeof outEvent.body !== "object") { -// outEvent.body = JSON.parse(outEvent.body); -// } -// Object.keys(outEvent.pathParameters).map((key) => { -// outEvent.pathParameters[key] = decodeURIComponent(outEvent.pathParameters[key]); -// }); -// outEvent.pathParameters = outEvent.pathParameters || {}; -// outEvent.queryStringParameters = outEvent.queryStringParameters || {}; - -// return outEvent as APIGatewayProxyEvent; -// } -// } - - -// export interface FanoutEvent { -// iid?: number; - -// /** The total number of bot instances */ -// icount?: number; - -// /** Starting positions for a given queue */ -// starteid?: { -// [key: string]: Checkpoint | string // not sure which this is yet -// }, - -// /** */ -// read_filter_groups?: ReadFilterGroup[]; - -// ignoreLock?: boolean; - - -// /** List of read/write positions of sources and destinations for this bot */ -// checkpoints?: Checkpoints; -// instances?: Record - -// maxeid: string -// cploc: "checkpoints" | "instances" - -// } - -// interface FanoutInvokeResponse { -// error: any; -// data: Result; -// iid: number; -// } - -// class FanoutProcessor implements IProcessor{ - -// static eventIdFormat = "[z/]YYYY/MM/DD/HH/mm/"; -// static ReadFilterGroupChangeDelayMS: number = 1000 * 60 * 15; // Can only change filter group size every 15 minutes -// static ReadFilterGroupChangeMaxEidDistanceMS: number = 1000 * 60 * 15; // Must be within 15 minutes of the last filter group eid - -// public handlerResponse: { error?: Error; data?: Result; } = {}; - -// reduce: ReduceFn; -// getNeedNeedInstances: InstancesFn; -// iid: number; -// icount: number; -// container: FanoutEvent; - -// constructor( -// private processor: IProcessor, -// private inputEvent: any, -// private context: RStreamsContext, -// private hooks: RStreamsBotHooks -// ) { -// this.getNeedNeedInstances = typeof this.hooks.instances === "function" ? this.hooks.instances.bind(this.hooks) : (_) => this.hooks.instances as number || 1; - -// let { iid, icount, container } = this.getMetadata(inputEvent); - -// config.registry.rstreamsLeoReadFilterContainer = container; -// this.iid = iid; -// this.icount = icount; -// this.container = container; - -// // Setup Global RStreamsSdk Filtering -// global.rstreamsLeoReadFilterEventPartition = this.hooks.eventPartition ? this.hooks.eventPartition.bind(this.hooks) : (event => event.eid); -// global.rstreamsLeoReadFilterData = this.container; -// } - -// getMetadata(inputEvent) { -// let iid = 0; -// let icount = 1; - -// let container: FanoutEvent = inputEvent.__cron || inputEvent || {}; - -// if (container && container.iid) { -// //container.iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; -// iid = typeof container.iid === "number" ? container.iid : parseInt(container.iid, 10) || 0; -// icount = container.icount; -// } - -// // Correct missing instances from event to __cron -// if ( -// container != inputEvent && -// container.instances && -// inputEvent.instances && -// Object.keys(container.instances).length != Object.keys(inputEvent.instances).length -// ) { -// container.instances = inputEvent.instances; -// } - -// if ( -// container != inputEvent && -// container.checkpoints && -// inputEvent.checkpoints && -// Object.keys(container.checkpoints).length != Object.keys(inputEvent.checkpoints).length -// ) { -// container.checkpoints = inputEvent.checkpoints; -// } - -// return { iid, icount, container }; -// } - -// static fixInstanceForChildProcess(event: any) { -// event.__FANOUT__ = true; -// // Get fanout data from process env if running locally -// console.log("fixInstanceForChildProcess", process.env.FANOUT_data); -// if (process.env.FANOUT_data) { -// Object.assign(event, JSON.parse(process.env.FANOUT_data)); -// } -// } - -// isMaster(): boolean { -// return this.iid == 0 || this.icount == null; -// } - -// isWorker(): boolean { -// return !this.isMaster(); -// } - -// async invokeLambda(iid: number, count: number, newEvent: any): Promise> { - -// let data: InvocationResponse;//FanoutInvokeResponse; -// let error; -// try { -// let lambdaApi = new aws.Lambda({ -// region: process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION, -// httpOptions: { -// timeout: this.context.getRemainingTimeInMillis() // Default: 120000 // Two minutes -// } -// }); - -// // todo: Should we invoke a new function or just run a new local process -// logger.log("[lambda]", process.env.AWS_LAMBDA_FUNCTION_NAME); -// data = await lambdaApi.invoke({ -// FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME, -// InvocationType: 'RequestResponse', -// Payload: JSON.stringify(newEvent), -// Qualifier: process.env.AWS_LAMBDA_FUNCTION_VERSION -// }).promise(); - -// } catch (err) { -// error = err; -// } - - -// logger.log(`Done with Lambda instance ${iid + 1}/${count}`); -// logger.log("[lambda err]", error); -// logger.log("[lambda data]", data); - -// let response: FanoutInvokeResponse; -// if (error) { -// throw error; -// } else if (data.FunctionError) { -// throw data.Payload; -// } else if (data.Payload != undefined && data.Payload != 'null') { -// response = JSON.parse(data.Payload as string); -// } else { -// response = data as any; -// } - -// logger.debug("[lambda invoked invocation/payload]", data, JSON.stringify(newEvent, null, 2)); -// return response; -// } - -// async invokeProcess(iid: number, count: number, newEvent: any): Promise> { -// // Fork process with event -// let worker = require("child_process").fork(process.argv[1], process.argv.slice(2), { -// cwd: process.cwd(), -// env: Object.assign({}, process.env, { -// FANOUT_data: JSON.stringify(newEvent), -// runner_keep_cmd: true -// }), -// execArgv: process.execArgv -// }); - -// // setup communication between processes -// let responseData: FanoutInvokeResponse; -// worker.once("message", (response: FanoutInvokeResponse) => { -// logger.log(`Got Response with instance ${iid + 1}/${count}`); -// responseData = response; -// }); - -// return new Promise(resolve => { -// worker.once("exit", () => { -// logger.log(`Done with child instance ${iid + 1}/${count}`); -// logger.log("[responseData]", responseData); -// resolve(responseData); -// }); -// }); -// } - -// async invokeSelf(event: any, iid: number, count: number): Promise> { -// logger.log(`Invoking ${iid + 1}/${count}`); - -// // Deep copy the invocation event and set instances variables -// let newEvent = JSON.parse(JSON.stringify(event)); -// let { container } = this.getMetadata(newEvent); -// container.iid = iid; -// container.icount = count; -// container.ignoreLock = true; -// // delete (container as any).force; - -// // Add starting points for all queues. -// // leo-sdk will look to this before the default checkpoints.read[queue] -// let myInstance = (event.instances || {})[iid] || {}; -// container.starteid = Object.keys(myInstance).reduce((all, key) => { -// if (key.match(/^queue:/) && myInstance[key] && myInstance[key].checkpoint) { -// all[key] = myInstance[key].checkpoint; -// } -// return all; -// }, {}); - -// // If running in lamba -// if (process.env.AWS_LAMBDA_FUNCTION_NAME && !process.env.IS_LOCAL && this.hooks.invokeType !== "process") { -// return await this.invokeLambda(iid, count, newEvent); -// } -// // If running local or child process -// else { -// return await this.invokeProcess(iid, count, newEvent); -// } -// } - -// async masterRun(): Promise { -// // This is the master, start the needed workers -// let timestamp = moment.utc(); -// this.container.maxeid = this.container.maxeid || this.inputEvent.maxeid || timestamp.format(FanoutProcessor.eventIdFormat) + timestamp.valueOf(); -// this.container.iid = 0; -// logger.log("Fanout Master", this.container.iid); - - -// let readFilterGroups: ReadFilterGroup[] = this.inputEvent.read_filter_groups || []; -// // Get Bot data if not provided -// if ( -// !this.container.instances || -// !this.container.checkpoints -// ) { -// let bot: BotData = await new Promise((resolve, reject) => -// this.context.sdk.aws.dynamodb.get( -// this.context.sdk.configuration.resources.LeoCron, -// this.inputEvent.botId, -// {}, -// (err, data) => err ? reject(err) : resolve(data))); - -// this.container.instances = (bot || {}).instances || {}; -// this.container.checkpoints = (bot || {}).checkpoints || { read: {}, write: {} }; -// this.inputEvent.requested_kinesis = (bot || {}).requested_kinesis || {}; -// readFilterGroups = (bot || {}).read_filter_groups || []; -// if (bot == null) { -// await this.context.sdk.bot.createBot(this.inputEvent.botId, {}); -// } -// } - -// // Find number of requested instances -// let instances = Math.ceil(this.getNeedNeedInstances(this.inputEvent, this.container)); -// instances = Math.max(1, Math.min(instances, this.hooks.maxInstances || 20)); - -// // Add any entries that don't exist -// let command = { -// TableName: this.context.sdk.configuration.resources.LeoCron, -// Key: { -// id: this.inputEvent.botId -// }, -// UpdateExpression: undefined, -// ExpressionAttributeNames: {}, -// ExpressionAttributeValues: {} -// }; -// let toAdd = []; - -// // Filter out any dead groups -// let minEid: string = null; -// let maxEid: string = "z/"; -// let activeInstancesFromFilterGroups = new Set(); -// let maxInstancesFromGroupsPreFilter = readFilterGroups.reduce((a, b) => Math.max(a, b.icount), instances); -// for (let i = 0; i < maxInstancesFromGroupsPreFilter; i++) { -// activeInstancesFromFilterGroups.add(i.toString()); -// } -// Object.entries(this.container.instances) -// .filter(([key]) => activeInstancesFromFilterGroups.has(key)) -// .map(([_key, value]) => value) -// .concat(this.container.checkpoints.read).forEach(inst => { -// Object.entries(inst).forEach(([key, cp]) => { -// if (key.match(/^queue:/) && cp?.checkpoint) { -// minEid = (minEid && minEid.localeCompare(cp.checkpoint) <= 0) ? minEid : cp.checkpoint; -// maxEid = (maxEid && maxEid.localeCompare(cp.checkpoint) >= 0) ? maxEid : cp.checkpoint; -// } -// }); -// }); - -// // Only filter if we have a minEid and there are more than 1 group -// // Otherwise it just removed and adds the group -// if (minEid && readFilterGroups.length > 1) { -// readFilterGroups = readFilterGroups.filter(g => g.eid >= minEid); -// } -// let latest: ReadFilterGroup = readFilterGroups[readFilterGroups.length - 1]; -// if ( -// latest == null || -// ( -// latest.icount != instances && // Have a different icount -// // latest.eid < maxEid && -// (Date.now() - latest.ts) >= FanoutProcessor.ReadFilterGroupChangeDelayMS && // outside the change delay window -// true // (Date.now() - this.context.sdk.streams.eventIdToTimestamp(latest.eid)) <= FanoutProcessor.ReadFilterGroupChangeMaxEidDistanceMS // last group eid is close -// ) -// ) { -// readFilterGroups.push({ -// icount: instances, -// eid: maxEid, //this.container.maxeid, -// ts: Date.now() -// }); -// toAdd.push(`#rfg = :rfg`); -// command.ExpressionAttributeNames["#rfg"] = "read_filter_groups"; -// command.ExpressionAttributeValues[":rfg"] = readFilterGroups; -// } else { -// // Ignore the new count because they aren't allowed to change yet -// // instances = latest.icount; -// } - -// this.container.icount = instances; -// this.container.read_filter_groups = readFilterGroups; - -// let maxInstancesFromGroups = readFilterGroups.reduce((a, b) => Math.max(a, b.icount), instances); - -// // Add the starting point for the instances to be the new max eid -// let startingCheckpoints = Object.keys((this.container.checkpoints || {}).read || {}).reduce((out, queue) => { -// if (queue.match(/^queue:/)) { -// out[queue] = { -// checkpoint: maxEid -// }; -// } -// return out; -// }, {}); - -// for (let i = 0; i < maxInstancesFromGroups; i++) { -// // Instance doesn't exist yet -// if (!this.container.instances[i]) { -// // Add checkpoint location in the DB for this instance id -// this.container.instances[i] = startingCheckpoints; - -// toAdd.push(`#instances.#i${i} = :i${i}`); -// command.ExpressionAttributeNames[`#i${i}`] = `${i}`; -// command.ExpressionAttributeValues[`:i${i}`] = this.container.instances[i]; -// command.ExpressionAttributeNames["#instances"] = "instances"; -// this.container.instances[i].__last_activated = Date.now(); -// } else if (i != 0) { -// let changed = false; -// this.container.instances[i] = { -// ...startingCheckpoints, -// ...this.container.instances[i] -// }; - -// // Reset the starting point for the instances to be the new max eid -// Object.entries(this.container.instances[i]).forEach(([queue, cp]: [string, Checkpoint]) => { -// if (queue.match(/^queue:/) && cp && cp.checkpoint && cp.checkpoint < minEid) { -// changed = true; -// this.container.instances[i][queue] = { -// checkpoint: maxEid -// }; -// } -// }); - -// // Only update if we needed to update position -// if (changed) { -// toAdd.push(`#instances.#i${i} = :i${i}`); -// command.ExpressionAttributeNames[`#i${i}`] = `${i}`; -// command.ExpressionAttributeValues[`:i${i}`] = this.container.instances[i]; -// this.container.instances[i].__last_activated = Date.now(); -// command.ExpressionAttributeNames["#instances"] = "instances"; -// } -// } -// } - -// // Update the cron table with the needed instances -// if (toAdd.length > 0) { -// command.UpdateExpression = `set ${toAdd.join(",")}`; -// logger.log("Updating Worker instances data", JSON.stringify(command, null, 2)); -// await this.context.sdk.aws.dynamodb.docClient.update(command).promise(); -// } - -// let workers: Promise>[] = []; - -// // Start the other workers -// for (let i = 1; i < maxInstancesFromGroups; i++) { -// workers.push(this.invokeSelf(this.inputEvent, i, instances)); -// } - -// // Setup Master Worker -// workers.unshift( -// (async () => { -// logger.log(`Invoking 1/${instances}`); - -// // Handle promise or callback response -// let data: Result; -// let error; -// try { -// data = await this.processor.run(); -// } catch (err) { -// error = err; -// } - -// let response = { -// error: error || this.processor.handlerResponse.error, -// data: data || this.processor.handlerResponse.data, -// iid: this.iid -// }; -// if (response.error && response.error instanceof Error) { -// response.error = { -// name: response.error.name, -// message: response.error.message, -// stack: response.error.stack -// }; -// } - -// logger.log(`Done with instance 1 / ${instances} `); -// return response; - -// })()); - - -// // Wait for all workers to return and figure out what checkpoint to persist -// logger.debug(`Waiting on all Fanout workers: count ${workers.length} `); -// let responses = await Promise.all(workers); -// return this.hooks.reduce ? this.hooks.reduce(responses) : void 0; -// } - -// async workerRun(): Promise { -// logger.log("Fanout Worker", this.iid); -// this.container.cploc = "instances"; - - -// //let context_getRemainingTimeInMillis = this.context.getRemainingTimeInMillis; -// // // save 3 seconds so the response can get back to the parent with time to process and clean up -// // this.context.getRemainingTimeInMillis = () => { -// // return context_getRemainingTimeInMillis.call(context) - (this.hooks.lambdaTimeoutPaddingMillis || (1000 * 3)); -// // }; - -// // wrap callback to send back the response to the parent lambda/process - - - -// let data: Result; -// let error; -// try { -// data = await this.processor.run(); -// } catch (err) { -// error = err; -// } -// let response = { -// error: error || this.processor.handlerResponse.error, -// data: data || this.processor.handlerResponse.data, -// iid: this.iid -// }; -// if (response.error && response.error instanceof Error) { -// response.error = { -// name: response.error.name, -// message: response.error.message, -// stack: response.error.stack -// }; -// } -// this.handlerResponse = this.processor.handlerResponse; -// logger.log("Worker sending data back", this.iid); -// logger.debug("Worker sending back response", this.iid, JSON.stringify(response, null, 2)); - -// if (process.send) { -// // Send response for child process worker to the master -// process.send(response); -// return; -// } else { -// // Send response for lambda worker to the master -// return response as unknown as Result; -// } -// } - -// public async run(): Promise { -// logger.log("Fanout Start"); -// logger.log("Fanout Handler", this.iid); -// logger.debug("Fanout Handler Event", this.iid, JSON.stringify(this.inputEvent, null, 2)); - -// try { -// if (this.isMaster()) { -// return await this.masterRun(); -// } else { -// return await this.workerRun(); -// } -// } -// finally { -// logger.log("Fanout End"); -// } -// } -// } - export class RStreamsBot { sdk: RStreamsSdk; currentContext?: Context; From d52579ee893d466b07a1e2da270eb147d39c84ac Mon Sep 17 00:00:00 2001 From: Clint Zirker Date: Mon, 25 Jul 2022 15:17:20 -0600 Subject: [PATCH 5/5] Added some additional types and added some other fields --- package-lock.json | 4840 +------------------------------- package.json | 1 + test/index.utest.ts | 2 +- wrappers/cron.js | 1 + wrappers/lib/cron-processor.ts | 4 +- wrappers/lib/process.ts | 6 +- wrappers/lib/processor.ts | 5 +- wrappers/smart.ts | 3 +- 8 files changed, 38 insertions(+), 4824 deletions(-) diff --git a/package-lock.json b/package-lock.json index 03923e48..06a9208d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,4705 +1,8 @@ { "name": "leo-sdk", - "version": "6.0.5", - "lockfileVersion": 2, + "version": "6.0.7", + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "leo-sdk", - "version": "6.0.5", - "license": "MIT", - "dependencies": { - "async": "2.6.4", - "backoff": "2.5.0", - "event-stream": "4.0.1", - "extend": "3.0.2", - "fast-csv": "4.3.6", - "flush-write-stream": "2.0.0", - "ini": "2.0.0", - "leo-config": "1.1.0", - "leo-logger": "1.0.1", - "lodash": "4.17.21", - "lodash.merge": "^4.6.2", - "moment": "2.29.2", - "pump": "3.0.0", - "pumpify": "1.5.1", - "readable-stream": "3.4.0", - "split2": "3.1.1", - "through2": "3.0.1", - "uuid": "3.3.2" - }, - "devDependencies": { - "@types/aws-lambda": "^8.10.93", - "@types/chai": "^4.3.0", - "@types/event-stream": "^4.0.0", - "@types/mocha": "^9.1.0", - "@types/node": "^16.11.12", - "@types/pumpify": "^1.4.1", - "@types/sinon": "^10.0.11", - "@types/sinon-chai": "^3.2.8", - "@typescript-eslint/eslint-plugin": "^5.20.0", - "@typescript-eslint/parser": "^5.20.0", - "chai": "^4.2.0", - "cross-env": "^7.0.3", - "eslint": "^8.13.0", - "mocha": "^9.2.2", - "nyc": "^15.1.0", - "react": "^18.1.0", - "replace-in-file": "^6.3.2", - "sinon": "^13.0.1", - "sinon-chai": "^3.7.0", - "ts-node": "^10.7.0", - "typedoc": "^0.22.15", - "typedoc-plugin-no-inherit": "^1.3.1", - "typedoc-plugin-replace-text": "^1.0.0", - "typescript": "^4.6.2" - }, - "peerDependencies": { - "aws-sdk": "^2.1107.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", - "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.7.tgz", - "integrity": "sha512-djHlEfFHnSnTAcPb7dATbiM5HxGOP98+3JLBZtjRb5I7RXrw7kFRoG2dXM8cm3H+o11A8IFH/uprmJpwFynRNQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.7", - "@babel/parser": "^7.17.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", - "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", - "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.7.tgz", - "integrity": "sha512-TKsj9NkjJfTBxM7Phfy7kv6yYc4ZcOo+AaWGqQOKTPDOmcGkIFb5xNA746eKisQkm4yavUYh4InYM9S+VnO01w==", - "dev": true, - "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "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": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.7.tgz", - "integrity": "sha512-bm3AQf45vR4gKggRfvJdYJ0gFLoCbsPxiFLSH6hTVYABptNHY6l9NrhnucVjQ/X+SPtLANT9lc0fFhikj+VBRA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", - "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.1", - "globals": "^13.9.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", - "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@fast-csv/format": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", - "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", - "dependencies": { - "@types/node": "^14.0.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isboolean": "^3.0.3", - "lodash.isequal": "^4.5.0", - "lodash.isfunction": "^3.0.9", - "lodash.isnil": "^4.0.0" - } - }, - "node_modules/@fast-csv/format/node_modules/@types/node": { - "version": "14.18.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", - "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" - }, - "node_modules/@fast-csv/parse": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", - "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", - "dependencies": { - "@types/node": "^14.0.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.groupby": "^4.6.0", - "lodash.isfunction": "^3.0.9", - "lodash.isnil": "^4.0.0", - "lodash.isundefined": "^3.0.1", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/@fast-csv/parse/node_modules/@types/node": { - "version": "14.18.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", - "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==" - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "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": { - "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/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "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": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "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": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.1.tgz", - "integrity": "sha512-Wp5vwlZ0lOqpSYGKqr53INws9HLkt6JDc/pDZcPf7bchQnrXJMXPns8CXx0hFikMSGSWfvtvvpb2gtMVfkWagA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", - "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", - "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", - "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", - "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", - "dev": true - }, - "node_modules/@types/aws-lambda": { - "version": "8.10.93", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.93.tgz", - "integrity": "sha512-Vsyi9ogDAY3REZDjYnXMRJJa62SDvxHXxJI5nGDQdZW058dDE+av/anynN2rLKbCKXDRNw3D/sQmqxVflZFi4A==", - "dev": true - }, - "node_modules/@types/chai": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", - "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", - "dev": true - }, - "node_modules/@types/duplexify": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.1.tgz", - "integrity": "sha512-n0zoEj/fMdMOvqbHxmqnza/kXyoGgJmEpsXjpP+gEqE1Ye4yNqc7xWipKnUoMpWhMuzJQSfK2gMrwlElly7OGQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/event-stream": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/event-stream/-/event-stream-4.0.0.tgz", - "integrity": "sha512-pH/x8o5fdDsJNVpjNNT3X8F2k2vbL0O9P1grFQwUsmWz49zXUFWWUIRKmzbNzzWiuv4px3UyLb34RjTJYs685A==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/mocha": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", - "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", - "dev": true - }, - "node_modules/@types/node": { - "version": "16.11.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz", - "integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==", - "dev": true - }, - "node_modules/@types/pumpify": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@types/pumpify/-/pumpify-1.4.1.tgz", - "integrity": "sha512-l7u/Dnh1OG9T7VH6TvulR0g8oE8hgIW5409mSUKi8Vxw2+JV18aTa06Sv5bvNjrD0zbsB/cuZ/iTFQgFNfzIuw==", - "dev": true, - "dependencies": { - "@types/duplexify": "*", - "@types/node": "*" - } - }, - "node_modules/@types/sinon": { - "version": "10.0.11", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz", - "integrity": "sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g==", - "dev": true, - "dependencies": { - "@types/sinonjs__fake-timers": "*" - } - }, - "node_modules/@types/sinon-chai": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.8.tgz", - "integrity": "sha512-d4ImIQbT/rKMG8+AXpmcan5T2/PNeSjrYhvkwet6z0p8kzYtfgA32xzOBlbU0yqJfq+/0Ml805iFoODO0LP5/g==", - "dev": true, - "dependencies": { - "@types/chai": "*", - "@types/sinon": "*" - } - }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.21.0.tgz", - "integrity": "sha512-fTU85q8v5ZLpoZEyn/u1S2qrFOhi33Edo2CZ0+q1gDaWWm0JuPh3bgOyU8lM0edIEYgKLDkPFiZX2MOupgjlyg==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/type-utils": "5.21.0", - "@typescript-eslint/utils": "5.21.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.21.0.tgz", - "integrity": "sha512-8RUwTO77hstXUr3pZoWZbRQUxXcSXafZ8/5gpnQCfXvgmP9gpNlRGlWzvfbEQ14TLjmtU8eGnONkff8U2ui2Eg==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/typescript-estree": "5.21.0", - "debug": "^4.3.2" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.21.0.tgz", - "integrity": "sha512-XTX0g0IhvzcH/e3393SvjRCfYQxgxtYzL3UREteUneo72EFlt7UNoiYnikUtmGVobTbhUDByhJ4xRBNe+34kOQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/visitor-keys": "5.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.21.0.tgz", - "integrity": "sha512-MxmLZj0tkGlkcZCSE17ORaHl8Th3JQwBzyXL/uvC6sNmu128LsgjTX0NIzy+wdH2J7Pd02GN8FaoudJntFvSOw==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.21.0", - "debug": "^4.3.2", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.21.0.tgz", - "integrity": "sha512-XnOOo5Wc2cBlq8Lh5WNvAgHzpjnEzxn4CJBwGkcau7b/tZ556qrWXQz4DJyChYg8JZAD06kczrdgFPpEQZfDsA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.21.0.tgz", - "integrity": "sha512-Y8Y2T2FNvm08qlcoSMoNchh9y2Uj3QmjtwNMdRQkcFG7Muz//wfJBGBxh8R7HAGQFpgYpdHqUpEoPQk+q9Kjfg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/visitor-keys": "5.21.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.21.0.tgz", - "integrity": "sha512-q/emogbND9wry7zxy7VYri+7ydawo2HDZhRZ5k6yggIvXa7PvBbAAZ4PFH/oZLem72ezC4Pr63rJvDK/sTlL8Q==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.21.0", - "@typescript-eslint/types": "5.21.0", - "@typescript-eslint/typescript-estree": "5.21.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.21.0.tgz", - "integrity": "sha512-SX8jNN+iHqAF0riZQMkm7e8+POXa/fXw5cxL+gjpyP+FI+JVNhii53EmQgDAfDcBpFekYSlO0fGytMQwRiMQCA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.21.0", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/aws-sdk": { - "version": "2.1118.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1118.0.tgz", - "integrity": "sha512-R3g06c4RC0Gz/lwMA7wgC7+FwYf5vaO30sPIigoX5m6Tfb7tdzfCYD7pnpvkPRNUvWJ3f5kQk+pEeW25DstRrQ==", - "peer": true, - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "uuid": "3.3.2", - "xml2js": "0.4.19" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/backoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", - "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", - "dependencies": { - "precond": "0.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "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/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "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/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", - "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001317", - "electron-to-chromium": "^1.4.84", - "escalade": "^3.1.1", - "node-releases": "^2.0.2", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "peer": true, - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001317", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz", - "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "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/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/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/convert-source-map/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/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/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" - }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "node_modules/duplexify/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexify/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/duplexify/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.86", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz", - "integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w==", - "dev": true - }, - "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/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", - "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.2.2", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", - "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", - "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", - "dev": true, - "dependencies": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", - "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", - "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" - } - }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "peer": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/fast-csv": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", - "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", - "dependencies": { - "@fast-csv/format": "4.3.5", - "@fast-csv/parse": "4.3.6" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "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/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "node_modules/flush-write-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-2.0.0.tgz", - "integrity": "sha512-uXClqPxT4xW0lcdSBheb2ObVU+kuqUk3Jk64EwieirEXZx9XUrVwp/JuBfKAWaM4T5Td/VL7QLDWPXp/MvGm/g==", - "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "peer": true - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "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 - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dev": true, - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-processinfo/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/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/istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "peer": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", - "dev": true - }, - "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true - }, - "node_modules/leo-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/leo-config/-/leo-config-1.1.0.tgz", - "integrity": "sha512-h6G8cz/yBdkWG2pDfDLUsJ0cFkxXrfjkRni312REly5BwLTDliQecJYeYs9xjwKCxnub6QIMdwO8Ep2kgs2Lvw==", - "dependencies": { - "lodash.merge": "^4.6.1" - } - }, - "node_modules/leo-config/node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/leo-logger": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/leo-logger/-/leo-logger-1.0.1.tgz", - "integrity": "sha512-mOXR35GHScpuYO+nbRU8jPeP5uK+QdhKXS+vUoSB6jq5ieQzgu2o6JD4kS+Qf5YIeI6wa/1P8aQ71uWREvLIrA==", - "dependencies": { - "lodash": "^4.17.10" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" - }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "node_modules/lodash.groupby": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", - "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E=" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, - "node_modules/lodash.isfunction": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" - }, - "node_modules/lodash.isnil": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", - "integrity": "sha1-SeKM1VkBNFjIFMVHnTxmOiG/qmw=" - }, - "node_modules/lodash.isundefined": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", - "integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g=" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=" - }, - "node_modules/marked": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", - "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/moment": { - "version": "2.29.2", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", - "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==", - "engines": { - "node": "*" - } - }, - "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/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "dependencies": { - "process-on-spawn": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "dependencies": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/nyc/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/nyc/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/nyc/node_modules/decamelize": { - "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" - } - }, - "node_modules/nyc/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/nyc/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": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/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": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/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": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/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/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/nyc/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/nyc/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/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "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/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-to-regexp/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dependencies": { - "through": "~2.3" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/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/pkg-dir/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": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/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": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/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": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/precond": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "dependencies": { - "fromentries": "^1.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "peer": true - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "peer": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/react": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.1.0.tgz", - "integrity": "sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ==", - "dev": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/replace-in-file": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.3.2.tgz", - "integrity": "sha512-Dbt5pXKvFVPL3WAaEB3ZX+95yP0CeAtIPJDwYzHbPP5EAHn+0UoegH/Wg3HKflU9dYBH8UnBC2NvY3P+9EZtTg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.2", - "glob": "^7.2.0", - "yargs": "^17.2.1" - }, - "bin": { - "replace-in-file": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/replace-in-file/node_modules/yargs": { - "version": "17.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", - "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/replace-in-file/node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "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/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", - "peer": true - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sinon": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.1.tgz", - "integrity": "sha512-8yx2wIvkBjIq/MGY1D9h1LMraYW+z1X0mb648KZnKSdvLasvDu7maa0dFaNYdTDczFgbjNw2tOmWdTk9saVfwQ==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.0.0", - "@sinonjs/samsam": "^6.1.1", - "diff": "^5.0.0", - "nise": "^5.1.1", - "supports-color": "^7.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "node_modules/sinon-chai": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", - "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", - "dev": true, - "peerDependencies": { - "chai": "^4.0.0", - "sinon": ">=4.0.0" - } - }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/split2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.1.1.tgz", - "integrity": "sha512-emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q==", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", - "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, - "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==" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "node_modules/through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", - "dependencies": { - "readable-stream": "2 || 3" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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/ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "0.7.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typedoc": { - "version": "0.22.15", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.15.tgz", - "integrity": "sha512-CMd1lrqQbFvbx6S9G6fL4HKp3GoIuhujJReWqlIvSb2T26vGai+8Os3Mde7Pn832pXYemd9BMuuYWhFpL5st0Q==", - "dev": true, - "dependencies": { - "glob": "^7.2.0", - "lunr": "^2.3.9", - "marked": "^4.0.12", - "minimatch": "^5.0.1", - "shiki": "^0.10.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 12.10.0" - }, - "peerDependencies": { - "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x" - } - }, - "node_modules/typedoc-plugin-no-inherit": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/typedoc-plugin-no-inherit/-/typedoc-plugin-no-inherit-1.3.1.tgz", - "integrity": "sha512-3fdPHRaIcCVV1W5gJYpZTdZL+F2VZTlUMd3Hw9xIv931ILh18CcD8IfxZtTtOijJRuKbgRLJ+J/+hVlgCbq1RQ==", - "dev": true, - "peerDependencies": { - "typedoc": ">=0.20.31" - } - }, - "node_modules/typedoc-plugin-replace-text": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typedoc-plugin-replace-text/-/typedoc-plugin-replace-text-1.0.0.tgz", - "integrity": "sha512-SyisHUYA0e6jOeU4S0WLwibDZg79+9exZ7hOReybUQ19/ZRvqmUovgovipf0bBNIh/l7161mtNPa66JrIZg8tQ==", - "dev": true, - "peerDependencies": { - "typedoc": "0.21.x || 0.22.x" - } - }, - "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", - "peer": true, - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/vscode-oniguruma": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", - "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", - "dev": true - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "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/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "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==", - "dev": true, - "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/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==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "peer": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "node_modules/xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", - "peer": true, - "engines": { - "node": ">=4.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==", - "dev": true, - "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==", - "dev": true - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, "dependencies": { "@ampproject/remapping": { "version": "2.1.2", @@ -5522,8 +825,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "acorn-walk": { "version": "8.2.0", @@ -5631,23 +933,6 @@ "lodash": "^4.17.14" } }, - "aws-sdk": { - "version": "2.1118.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1118.0.tgz", - "integrity": "sha512-R3g06c4RC0Gz/lwMA7wgC7+FwYf5vaO30sPIigoX5m6Tfb7tdzfCYD7pnpvkPRNUvWJ3f5kQk+pEeW25DstRrQ==", - "peer": true, - "requires": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "uuid": "3.3.2", - "xml2js": "0.4.19" - } - }, "backoff": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", @@ -5662,12 +947,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "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 - }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -5712,17 +991,6 @@ "picocolors": "^1.0.0" } }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "peer": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -5950,10 +1218,9 @@ } }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" }, "dir-glob": { "version": "3.0.1", @@ -6260,12 +1527,6 @@ "through": "^2.3.8" } }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "peer": true - }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -6548,12 +1809,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "peer": true - }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -6788,12 +2043,6 @@ "istanbul-lib-report": "^3.0.0" } }, - "jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "peer": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7073,6 +2322,14 @@ "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" + }, + "dependencies": { + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + } } }, "moment": { @@ -7521,18 +2778,6 @@ } } }, - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "peer": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "peer": true - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7672,12 +2917,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", - "peer": true - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -7760,8 +2999,7 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", - "dev": true, - "requires": {} + "dev": true }, "slash": { "version": "3.0.0", @@ -7825,14 +3063,6 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -7844,6 +3074,14 @@ "strip-ansi": "^6.0.1" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -8041,15 +3279,13 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/typedoc-plugin-no-inherit/-/typedoc-plugin-no-inherit-1.3.1.tgz", "integrity": "sha512-3fdPHRaIcCVV1W5gJYpZTdZL+F2VZTlUMd3Hw9xIv931ILh18CcD8IfxZtTtOijJRuKbgRLJ+J/+hVlgCbq1RQ==", - "dev": true, - "requires": {} + "dev": true }, "typedoc-plugin-replace-text": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/typedoc-plugin-replace-text/-/typedoc-plugin-replace-text-1.0.0.tgz", "integrity": "sha512-SyisHUYA0e6jOeU4S0WLwibDZg79+9exZ7hOReybUQ19/ZRvqmUovgovipf0bBNIh/l7161mtNPa66JrIZg8tQ==", - "dev": true, - "requires": {} + "dev": true }, "typescript": { "version": "4.6.3", @@ -8074,16 +3310,6 @@ } } }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", - "peer": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -8173,22 +3399,6 @@ "typedarray-to-buffer": "^3.1.5" } }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "peer": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", - "peer": true - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index ddd70837..3d075054 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "async": "2.6.4", "backoff": "2.5.0", + "diff": "^5.1.0", "event-stream": "4.0.1", "extend": "3.0.2", "fast-csv": "4.3.6", diff --git a/test/index.utest.ts b/test/index.utest.ts index 02e7ae22..6992be18 100644 --- a/test/index.utest.ts +++ b/test/index.utest.ts @@ -1038,7 +1038,7 @@ describe('RStreams', function () { let inQueue = "mock-in"; let outQueue = "mock-out"; - let botId = "mock-bot" + let botId = "mock-bot"; let batchGetResponse = { Responses: { diff --git a/wrappers/cron.js b/wrappers/cron.js index 7c50ab70..fb58738b 100644 --- a/wrappers/cron.js +++ b/wrappers/cron.js @@ -139,6 +139,7 @@ module.exports = function(configOverride, botHandler) { leosdk.configuration.registry = config.registry; config.registry.context = context; config.registry.__cron = event.__cron; + config.registry.__event = event; global.cron_run_again = false; if (event.__cron && event.__cron.id) { //If it is in cron, use that regardless config.registry.id = event.__cron.id; diff --git a/wrappers/lib/cron-processor.ts b/wrappers/lib/cron-processor.ts index e7b691cd..e7ad2a87 100644 --- a/wrappers/lib/cron-processor.ts +++ b/wrappers/lib/cron-processor.ts @@ -62,8 +62,8 @@ export class CronProcessor extends Processor await this.reportComplete(this.cron, this.id, error ? InstanceStatus.error : InstanceStatus.complete, error ? error : "", {}); } - override setupRegistory() { - + override setupRegistry() { + super.setupRegistry(); config.registry.__cron = this.cron; config.registry.id = this.cron.id; } diff --git a/wrappers/lib/process.ts b/wrappers/lib/process.ts index 21a7b7c5..048ce10a 100644 --- a/wrappers/lib/process.ts +++ b/wrappers/lib/process.ts @@ -1,7 +1,7 @@ import leoConfig from "../../leoConfigure"; export interface RStreamsProcess extends NodeJS.Process { - __config: unknown; + __config: Record; resources: Record; } @@ -13,7 +13,7 @@ if (rstreamsProcess.__config == null) { rstreamsProcess.resources = process.env.Resources && JSON.parse(process.env.Resources) || {}; } -export const config = leoConfig; -export const registry = leoConfig.registry; +export const config = rstreamsProcess.__config; +export const registry = rstreamsProcess.__config.registry; export const resources = rstreamsProcess.resources; export default rstreamsProcess; diff --git a/wrappers/lib/processor.ts b/wrappers/lib/processor.ts index 60b8bf64..6d3113df 100644 --- a/wrappers/lib/processor.ts +++ b/wrappers/lib/processor.ts @@ -26,7 +26,7 @@ export class Processor implements IProcessor { } initialize() { - this.setupRegistory(); + this.setupRegistry(); } public async run(): Promise { @@ -45,7 +45,8 @@ export class Processor implements IProcessor { } } - protected setupRegistory() { + protected setupRegistry() { + config.registry.context = this.context; if (!config.registry.id) { config.registry.id = process.env.AWS_LAMBDA_FUNCTION_NAME; } diff --git a/wrappers/smart.ts b/wrappers/smart.ts index 5aa4cdcc..92389ec6 100644 --- a/wrappers/smart.ts +++ b/wrappers/smart.ts @@ -127,11 +127,12 @@ export class RStreamsBot { return rstreamsContext; } - setupRegistry(context: Context, _inputEvent: InvocationEvent) { + setupRegistry(context: Context, inputEvent: InvocationEvent) { empty(config.registry); this.sdk.configuration.registry = config.registry; config.registry.context = context; + config.registry.__event = inputEvent; global.cron_run_again = false; }