From 53f35661606ba4acb476e93c45e2b9dd9765efd1 Mon Sep 17 00:00:00 2001 From: James Roper Date: Mon, 6 Apr 2020 15:35:44 +1000 Subject: [PATCH] Start on eventing support --- .../eventing/event-acknowledgments.md | 48 + node-support/src/cloudevents.js | 69 + node-support/src/command-helper.js | 59 +- node-support/src/context-failure.js | 25 + node-support/src/effect-serializer.js | 74 + node-support/src/metadata.js | 94 + node-support/src/stateless-support.js | 402 +- node-support/src/stateless.js | 47 +- project/build.properties | 2 +- protocols/example/shoppingcart/products.proto | 34 + .../example/shoppingcart/projection.proto | 29 + .../example/shoppingcart/shoppingcart.proto | 1 - .../frontend/cloudstate/entity_key.proto | 2 +- protocols/frontend/cloudstate/eventing.proto | 46 +- .../cloudstate/legacy_entity_key.proto | 30 + protocols/protocol/cloudstate/entity.proto | 23 + protocols/protocol/cloudstate/function.proto | 63 +- .../proto/cloudstate/proxy/statemanager.proto | 2 + proxy/core/src/main/resources/in-memory.conf | 17 +- proxy/core/src/main/resources/reference.conf | 14 +- .../proxy/EntityDiscoveryManager.scala | 82 +- .../io/cloudstate/proxy/GrpcWebSupport.scala | 13 +- .../scala/io/cloudstate/proxy/HttpApi.scala | 45 +- .../scala/io/cloudstate/proxy/Serve.scala | 173 +- .../cloudstate/proxy/UserFunctionRouter.scala | 41 +- .../proxy/UserFunctionTypeSupport.scala | 18 +- .../io/cloudstate/proxy/crdt/CrdtEntity.scala | 2 +- .../proxy/eventing/EventLogEventing.scala | 132 + .../proxy/eventing/EventingManager.scala | 597 +- .../proxy/eventing/EventingSupport.scala | 98 + .../proxy/eventing/GooglePubsubEventing.scala | 427 +- .../proxy/eventing/OffsetTracking.scala | 27 + .../eventsourced/EventSourcedEntity.scala | 7 +- .../EventSourcedSupportFactory.scala | 4 +- .../proxy/eventsourced/InMemJournal.scala | 204 + .../StatelessFunctionSupportFactory.scala | 122 +- .../proxy/streams/MergeSequence.scala | 107 + .../proxy/crdt/AbstractCrdtEntitySpec.scala | 2 +- .../proxy/streams/MergeSequenceSpec.scala | 83 + .../loadgenerator/GenerateLoad.scala | 65 +- samples/js-shopping-cart/index.js | 9 +- samples/js-shopping-cart/package-lock.json | 4979 ++--------------- samples/js-shopping-cart/package.json | 6 +- samples/js-shopping-cart/products.js | 70 + samples/js-shopping-cart/projection.js | 54 + samples/js-shopping-cart/user-function.desc | Bin 10863 -> 12754 bytes 46 files changed, 3069 insertions(+), 5379 deletions(-) create mode 100644 docs/src/main/paradox/developer/eventing/event-acknowledgments.md create mode 100644 node-support/src/cloudevents.js create mode 100644 node-support/src/context-failure.js create mode 100644 node-support/src/effect-serializer.js create mode 100644 node-support/src/metadata.js create mode 100644 protocols/example/shoppingcart/products.proto create mode 100644 protocols/example/shoppingcart/projection.proto create mode 100644 protocols/frontend/cloudstate/legacy_entity_key.proto create mode 100644 proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventLogEventing.scala create mode 100644 proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventingSupport.scala create mode 100644 proxy/core/src/main/scala/io/cloudstate/proxy/eventing/OffsetTracking.scala create mode 100644 proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/InMemJournal.scala create mode 100644 proxy/core/src/main/scala/io/cloudstate/proxy/streams/MergeSequence.scala create mode 100644 proxy/core/src/test/scala/io/cloudstate/proxy/streams/MergeSequenceSpec.scala create mode 100644 samples/js-shopping-cart/products.js create mode 100644 samples/js-shopping-cart/projection.js diff --git a/docs/src/main/paradox/developer/eventing/event-acknowledgments.md b/docs/src/main/paradox/developer/eventing/event-acknowledgments.md new file mode 100644 index 000000000..b800d979a --- /dev/null +++ b/docs/src/main/paradox/developer/eventing/event-acknowledgments.md @@ -0,0 +1,48 @@ +# Event Acknowledgement + +One of the hardest challenges with a developer friendly event processing API is providing for simple, intuitive event acknowledgement, at the time that the developer means to acknowledge the event. This page seeks to define the problem, and propose solutions. + +## Problem definition + +The simplest event processor is one in which there is a one to one mapping of events in to events out. In this use case, it's dead simple to know and understand when an incoming event should be acknowledged as processed - when the outgoing event is published, the incoming event should be acknowledged. + +We are going to assume that the processing guarantees we want are at-least-once - so, the publish of the outgoing event and acknowledgment of the incoming event don't need to be atomic, after an outgoing event is published, and acknowledgment that that publish is complete (eg, synced to disk or replicated to a minimum number of nodes in the outgoing receiver), the incoming event can be acknowledged in a separate operation. If this acknowledgement fails, then the incoming message will be retried at some point later, resulting in a duplicate outgoing event being published, and so on, until the incoming message is acknowledged. Thus, at least once guarantees are maintained. For now, we will not consider either at most once, or strategies for achieving effectively exactly once processing. + +Where it gets more challenging is when processing of events is not one to one. One to many is very common. Many to one is not as common in microservices (though in streaming data aggregation solutions is very common), but we will discuss it nevertheless. + +### Common use cases + +We will assume a fairly well understood problem domain to frame our use cases in. We have an event sourced entity that represents a user, which is used to provide a user management service. This user entity might have events such as `UserCreated`, `PasswordChanged`, `EmailChanged`. These events are how the user entity internally represents a user, and their schema should be able to change freely to accommodate the needs of the user entity without affecting any downstream services that might consume these events. Migrations may be defined on them to evolve these events over time. For this reason, this stream is not published directly to external services, rather, an anti-corruption layer is used, that translates these events to an external representation of the same events. + +Unlike the internal events from the event log, the external events cannot be changed freely, their schema forms a contract with the downstream services that consumes them. When the internal events change in their structure, the external events must not change in a way that will break backwards compatibility. + +Note that in the use cases below, we're not trying to build a coherent model of a user management service, nor are we trying to design the perfect user management service. Rather, we're saying "let's assume that the business has requirement X", and then discuss how we might provide the mechanism to implement that. These requirements are a given, and hopefully are intuitively relatable, even if that's not how you would necessarily design a system to function. The point is, it's up to the business to make its own decisions, and Cloudstate to provide the mechanisms to make those decisions possible to implement, whatever they are. + +#### Filtering + +Not all events in the internal model will necessarily map to events in the external model. For example, a business decision might be that a users password might be the domain of the user management service and the user management service alone. When a `PasswordChanged` event is emitted from the event log, no event should then be published externally. Effectively, the `PasswordChanged` event must be filtered from the event stream. + +The challenge in acknowledgement here is when should the `PasswordChanged` event be acknowledged, and how should the user function indicate this? Since no event is being output, the output of an event cannot be used to indicate that the `PasswordChanged` event should be acknowledged. Rather, it's the absence of an output event that should indicate that a `PasswordChanged` event should be acknowledged. But how do we differentiate between on output event, and slow processing of the input event? Does the Cloudstate proxy say "well, you haven't output anything for 10 seconds, so I'm going to acknowledge?". This puts a 10 second latency on the processing of all `PasswordChanged` events, so that solution doesn't work. Do we have a specific ack signal, which accompanies no event? + +#### Unfolding + +Sometimes, one input event may need to be unfolded into multiple events. In Akka Streams parlance, this would be a `mapConcat`. Consider the case where in the internal event store, your `UserCreated` event contains an email address, but a business decision is that the external event published when a user created does not contain an email, rather, it should be followed by an email changed event. So, when we process a `UserCreated` internal event, we need to output two external events, a `UserCreated` and an `EmailChanged` event. + +In this case, we do not want to acknowledge the `UserCreated` internal event until both the external events are published. If just the first one is published, and the second one fails, the internal event should not be acknowledged, and it should be reprocessed later. + +So again, the challenge is communicating on which event should the internal event be acknowledged. The mere signal of an output event does not convey enough information to indicate this, it's only the successful publish of the last event from the expanding list of events that should trigger acknowledgement. + +#### Folding + +Sometimes, multiple events need to be folded into a single event. Note here that we're not talking about folding an entire stream into a single value, we're talking about combining multiple events in a stream into a single value. For a use case, the converse of the unfolding scenario applies, perhaps internally, you have a `UserCreated` and `EmailChanged` event, but a business decision is that the external `UserCreated` event should combine these too. + +The challenge with folding is that it is inherently stateful. You need to track events that are in the process of being folded, and delay acknowledging them until the fold is complete. + +However, doing this can be dangerous, and lead to deadlocks. The reason being that typically, the number of unacknowledged events must be limited because unacknowledged events require buffering to track, so that once the head of the queue gets acknowledged, the rest too can be acknowledged. This does depend on message broker though, for message brokers that track acknowledgements to each message independently, it may be fine, but for offset based message brokers like Kafka, it's a problem. Consider the case where a single entity may emit a `UserCreated` and then an `EmailChanged` event, but concurrently, many other entities emit events, so between those two events in the stream for all user events there may be tens, hundreds or thousands of events. If the number of events interleaved here is greater than the configured max outstanding events, then a deadlock will be reached, where the `UserCreated` event can't be acknowledged because the `EmailChanged` event has not yet been received, but the `EmailChanged` event is not being received because the limit of unacknowledged events has been reached, which is being blocked by the waiting `UserCreated` event acknowledgement. + +For this reason, such stateful folds are best done with a persistent save point in the middle, such that the first event can be acknowledged immediately. In this case, what we are essentially doing is a filter with a side effect to a persistent store - when we receive the `UserCreated` event, we persist that we've received it, and emit no event, then when we receive the `EmailChanged` event, we load the persisted state that was caused by the `UserCreated` event, and emit the corresponding folded event. + + + +## Developer experience + diff --git a/node-support/src/cloudevents.js b/node-support/src/cloudevents.js new file mode 100644 index 000000000..46bb3d49f --- /dev/null +++ b/node-support/src/cloudevents.js @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Lightbend Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function toCloudevent(metadata) { + return { + get specversion() { + return metadata["ce-specversion"]; + }, + get id() { + return metadata["ce-id"]; + }, + set id(id) { + metadata["ce-id"] = id; + }, + get source() { + return metadata["ce-source"]; + }, + set source(source) { + metadata["ce-source"] = source; + }, + get type() { + return metadata["ce-type"]; + }, + set type(type) { + metadata["ce-type"] = type; + }, + get datacontenttype() { + return metadata["Content-Type"]; + }, + set datacontenttype(datacontenttype) { + metadata["Content-Type"] = datacontentype; + }, + get dataschema() { + return metadata["ce-dataschema"]; + }, + set dataschema(dataschema) { + metadata["ce-dataschema"] = dataschema; + }, + get subject() { + return metadata["ce-subject"]; + }, + set subject(subject) { + metadata["ce-subject"] = subject; + }, + get time() { + return metadata["ce-time"]; + }, + set time(time) { + metadata["ce-time"] = time; + }, + }; +} + +module.exports = { + toCloudevent +}; \ No newline at end of file diff --git a/node-support/src/command-helper.js b/node-support/src/command-helper.js index 4c8ffa53e..0597e431d 100644 --- a/node-support/src/command-helper.js +++ b/node-support/src/command-helper.js @@ -15,16 +15,8 @@ */ const AnySupport = require("./protobuf-any"); - -class ContextFailure extends Error { - constructor(msg) { - super(msg); - if (Error.captureStackTrace) { - Error.captureStackTrace(this, ContextFailure); - } - this.name = "ContextFailure"; - } -} +const EffectSerializer = require("./effect-serializer"); +const ContextFailure = require("./context-failure"); /** * Creates the base for context objects. @@ -37,7 +29,7 @@ module.exports = class CommandHelper { this.service = service; this.streamId = streamId; this.call = call; - this.allEntities = allEntities; + this.effectSerializer = new EffectSerializer(allEntities); this.debug = debug; this.handlerFactory = handlerFactory; } @@ -215,7 +207,7 @@ module.exports = class CommandHelper { */ effect: (method, message, synchronous = false) => { accessor.ensureActive(); - accessor.effects.push(this.serializeSideEffect(method, message, synchronous)) + accessor.effects.push(this.effectSerializer.serializeSideEffect(method, message, synchronous)) }, /** @@ -227,7 +219,7 @@ module.exports = class CommandHelper { */ thenForward: (method, message) => { accessor.ensureActive(); - accessor.forward = this.serializeEffect(method, message); + accessor.forward = this.effectSerializer.serializeEffect(method, message); }, /** @@ -249,45 +241,4 @@ module.exports = class CommandHelper { }; return accessor; } - - serializeEffect(method, message) { - let serviceName, commandName; - // We support either the grpc method, or a protobufjs method being passed - if (typeof method.path === "string") { - const r = new RegExp("^/([^/]+)/([^/]+)$").exec(method.path); - if (r == null) { - throw new Error(util.format("Not a valid gRPC method path '%s' on object '%o'", method.path, method)); - } - serviceName = r[1]; - commandName = r[2]; - } else if (method.type === "rpc") { - serviceName = method.parent.name; - commandName = method.name; - } - - const service = this.allEntities[serviceName]; - - if (service !== undefined) { - const command = service.methods[commandName]; - if (command !== undefined) { - const payload = AnySupport.serialize(command.resolvedRequestType.create(message), false, false); - return { - serviceName: serviceName, - commandName: commandName, - payload: payload - }; - } else { - throw new Error(util.format("Command [%s] unknown on service [%s].", commandName, serviceName)) - } - } else { - throw new Error(util.format("Service [%s] has not been registered as an entity in this user function, and so can't be used as a side effect or forward.", service)) - } - } - - serializeSideEffect(method, message, synchronous) { - const msg = this.serializeEffect(method, message); - msg.synchronous = synchronous; - return msg; - } - }; \ No newline at end of file diff --git a/node-support/src/context-failure.js b/node-support/src/context-failure.js new file mode 100644 index 000000000..466a3f189 --- /dev/null +++ b/node-support/src/context-failure.js @@ -0,0 +1,25 @@ +/* + * Copyright 2019 Lightbend Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = class ContextFailure extends Error { + constructor(msg) { + super(msg); + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ContextFailure); + } + this.name = "ContextFailure"; + } +}; diff --git a/node-support/src/effect-serializer.js b/node-support/src/effect-serializer.js new file mode 100644 index 000000000..64b4ef17c --- /dev/null +++ b/node-support/src/effect-serializer.js @@ -0,0 +1,74 @@ +/* + * Copyright 2019 Lightbend Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const AnySupport = require("./protobuf-any"); +const util = require("util"); + +module.exports = class EffectSerializer { + + constructor(allEntities) { + this.allEntities = allEntities; + } + + serializeEffect(method, message) { + let serviceName, commandName; + // We support either the grpc method, or a protobufjs method being passed + if (typeof method.path === "string") { + const r = new RegExp("^/([^/]+)/([^/]+)$").exec(method.path); + if (r == null) { + throw new Error(util.format("Not a valid gRPC method path '%s' on object '%o'", method.path, method)); + } + serviceName = r[1]; + commandName = r[2]; + } else if (method.type === "rpc") { + serviceName = this.fullName(method.parent); + commandName = method.name; + } + + const service = this.allEntities[serviceName]; + + if (service !== undefined) { + const command = service.methods[commandName]; + if (command !== undefined) { + const payload = AnySupport.serialize(command.resolvedRequestType.create(message), false, false); + return { + serviceName: serviceName, + commandName: commandName, + payload: payload + }; + } else { + throw new Error(util.format("Command [%s] unknown on service [%s].", commandName, serviceName)) + } + } else { + throw new Error(util.format("Service [%s] has not been registered as an entity in this user function, and so can't be used as a side effect or forward.", service)) + } + } + + fullName(item) { + if (item.parent && item.parent.name !== "") { + return this.fullName(item.parent) + "." + item.name; + } else { + return item.name; + } + } + + serializeSideEffect(method, message, synchronous) { + const msg = this.serializeEffect(method, message); + msg.synchronous = synchronous; + return msg; + } + +}; \ No newline at end of file diff --git a/node-support/src/metadata.js b/node-support/src/metadata.js new file mode 100644 index 000000000..877a8444d --- /dev/null +++ b/node-support/src/metadata.js @@ -0,0 +1,94 @@ +/* + * Copyright 2019 Lightbend Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function Metadata(entries) { + this.getMap = new Proxy({}, { + get: (target, key) => { + for (const idx in entries) { + const entry = entries[idx]; + if (key.toLowerCase() === entry.key.toLowerCase()) { + return entry.value; + } + } + }, + set: (target, key, value) => { + this.set(key, value) + }, + deleteProperty: (target, key) => this.delete(key), + ownKeys: (target) => { + const keys = []; + entries.forEach(entry => { + keys.push(entry.key); + }); + return keys; + }, + has: (target, key) => this.has(key), + defineProperty: () => { + throw new Error("Metadata.getMap does not support defining properties"); + }, + getOwnPropertyDescriptor: (target, key) => { + const value = this.get(key); + return value ? { + value: value, + writable: true, + enumerable: true, + configurable: true + } : undefined; + } + }); + + this.get = key => { + const values = []; + entries.forEach(entry => { + if (key.toLowerCase() === entry.key.toLowerCase()) { + values.push(entry.value); + } + }); + return values; + }; + + + this.set = (key, value) => { + entries.push({key, value}) + }; + + this.delete = key => { + let idx = 0; + while (idx < entries.length) { + const entry = entries[idx]; + if (key.toLowerCase() !== entry.key.toLowerCase()) { + idx++; + } else { + entries.splice(idx, 1); + } + } + }; + + this.has = key => { + for (const idx in entries) { + const entry = entries[idx]; + if (key.toLowerCase() === entry.key.toLowerCase()) { + return true; + } + } + }; + + this.clear = () => { + entries.splice(0, entries.length); + }; +} + +module.exports = Metadata; \ No newline at end of file diff --git a/node-support/src/stateless-support.js b/node-support/src/stateless-support.js index 9ba8e455e..0eaa16fbd 100644 --- a/node-support/src/stateless-support.js +++ b/node-support/src/stateless-support.js @@ -16,33 +16,279 @@ const path = require("path"); const grpc = require("grpc"); const protoLoader = require("@grpc/proto-loader"); -const debug = require("debug")("cloudstate-event-sourcing"); +const debug = require("debug")("cloudstate-stateless"); // Bind to stdout debug.log = console.log.bind(console); const AnySupport = require("./protobuf-any"); +const EffectSerializer = require("./effect-serializer"); +const Metadata = require("./metadata"); +const CloudEvents = require("./cloudevents"); class StatelessSupport { - - constructor(root, service, handlers, allEntities) { + constructor(root, service, commandHandlers, allEntities) { this.root = root; - this.service = service; - this.anySupport = new AnySupport(this.root); - this.commandHandlers = handlers.commandHandlers; - this.allEntities = allEntities; + this.service = service; + this.commandHandlers = commandHandlers; + this.anySupport = new AnySupport(this.root); + this.effectSerializer = new EffectSerializer(allEntities); } +} + +class StatelessHandler { - serialize(obj, requireJsonType) { - return AnySupport.serialize(obj, this.options.serializeAllowPrimitives, this.options.serializeFallbackToJson, requireJsonType); - //return AnySupport.serialize(obj, true, false, requireJsonType); + constructor(support, grpcMethod, commandHandler, call, grpcCallback, metadata) { + this.support = support; + this.grpcMethod = grpcMethod; + this.commandHandler = commandHandler; + this.call = call; + this.grpcCallback = grpcCallback; + + this.streamId = Math.random().toString(16).substr(2, 7); + this.streamDebug("Started new call"); + this.supportedEvents = []; + this.callbacks = {}; + this.ctx = this.createContext(metadata); } - deserialize(any) { - return this.anySupport.deserialize(any); + streamDebug(msg, ...args) { + debug("%s [%s.%s] - " + msg, ...[this.streamId, this.support.service.name, this.grpcMethod.name].concat(args)); } -} + createContext(metadata) { + const call = this.call; + let metadataObject = new Metadata([]); + if (metadata && metadata.entries) { + metadataObject = new Metadata(metadata.entries); + } + const cloudevent = CloudEvents.toCloudevent(metadataObject.getMap); + const ctx = { + get cancelled() { + return call.cancelled; + }, + get metadata() { + return metadata; + }, + get cloudevent() { + return cloudevent; + } + }; + + ctx.on = (eventType, callback) => { + if (this.supportedEvents.includes(eventType)) { + this.callbacks[eventType] = callback; + } else { + throw new Error("Unknown event type: " + eventType); + } + }; + return ctx; + } + + invokeCallback(eventType, ...args) { + if (this.callbacks.hasOwnProperty(eventType)) { + this.invokeUserCallback(eventType + " event", this.callbacks[eventType], ...args) + } + } + + ensureNotCancelled() { + if (this.call.cancelled) { + throw new Error("Already replied to unary command, cannot interact further.") + } + } + + handleUnary() { + this.setupUnaryOutContext(); + const deserializedCommand = this.grpcMethod.resolvedRequestType.decode(this.call.request.payload.value); + const userReturn = this.invokeUserCallback("command", this.commandHandler, deserializedCommand, this.ctx); + if (userReturn !== undefined) { + if (this.call.cancelled) { + this.streamDebug("Unary command handler for command %s.%s both sent a reply through the context and returned a value, ignoring return value.", this.support.service.name, this.grpcMethod.name) + } else { + if (typeof userReturn.then === "function") { + userReturn.then(this.ctx.write, this.ctx.fail) + } else { + this.ctx.write(userReturn); + } + } + } + } + + handleStreamedIn() { + this.setupUnaryOutContext(); + this.setupStreamedInContext(); + const userReturn = this.invokeUserCallback("command", this.commandHandler, this.ctx); + if (userReturn !== undefined) { + if (this.call.cancelled) { + this.streamDebug("Streamed command handler for command %s.%s both sent a reply through the context and returned a value, ignoring return value.", this.support.service.name, this.grpcMethod.name) + } else { + if (typeof userReturn.then === "function") { + userReturn.then(this.ctx.write, this.ctx.fail) + } else { + this.ctx.write(userReturn); + } + } + } + } + + handleStreamedOut() { + this.setupStreamedOutContext(); + const deserializedCommand = this.grpcMethod.resolvedRequestType.decode(this.call.request.payload.value); + this.invokeUserCallback("command", this.commandHandler, deserializedCommand, this.ctx); + } + + handleStreamed() { + this.setupStreamedInContext(); + this.setupStreamedOutContext(); + this.invokeUserCallback("command", this.commandHandler, this.ctx); + } + + setupUnaryOutContext() { + const effects = []; + + this.ctx.forward = (method, message) => { + this.ensureNotCancelled(); + this.streamDebug("Forwarding to %s", method); + const forward = this.support.effectSerializer.serializeEffect(method, message); + this.grpcCallback(null, { + forward: forward, + sideEffects: effects + }); + }; + + this.ctx.write = message => { + this.ensureNotCancelled(); + this.streamDebug("Sending reply"); + const messageProto = this.grpcMethod.resolvedResponseType.fromObject(message); + const replyPayload = AnySupport.serialize(messageProto, false, false, false); + this.grpcCallback(null, { + reply: { + payload: replyPayload + }, + sideEffects: effects + }); + }; + + this.ctx.effect = (method, message, synchronous) => { + this.ensureNotCancelled(); + this.streamDebug("Emitting effect to %s", method); + effects.push(this.support.effectSerializer.serializeEffect(method, message, synchronous)); + }; + + this.ctx.fail = error => { + this.ensureNotCancelled(); + this.streamDebug("Failing with %s", error); + this.grpcCallback(null, { + failure: { + description: error + }, + }); + }; + } + + setupStreamedOutContext() { + this.supportedEvents.push("cancelled"); + + this.call.on("cancelled", () => { + this.streamDebug("Received stream cancelled"); + this.invokeCallback("cancelled", this.ctx); + }); + + this.ctx.end = () => { + if (this.call.cancelled) { + this.streamDebug("end invoked when already cancelled."); + } else { + this.streamDebug("Ending stream out"); + this.call.end(); + } + }; + + this.ctx.forward = (method, message) => { + this.ensureNotCancelled(); + this.streamDebug("Forwarding to %s", method); + const forward = this.support.effectSerializer.serializeEffect(method, message); + this.call.write({ + forward: forward + }); + }; + + this.ctx.write = message => { + this.ensureNotCancelled(); + this.streamDebug("Sending reply"); + const messageProto = this.grpcMethod.resolvedResponseType.fromObject(message); + const replyPayload = AnySupport.serialize(messageProto, false, false, false); + this.call.write({ + reply: { + payload: replyPayload + } + }); + }; + + this.ctx.effect = (method, message, synchronous) => { + this.ensureNotCancelled(); + this.streamDebug("Emitting effect to %s", method); + this.call.write({ + sideEffects: [this.support.effectSerializer.serializeSideEffect(method, message, synchronous)] + }); + }; + this.ctx.fail = error => { + this.ensureNotCancelled(); + this.streamDebug("Failing with %s", error); + this.call.write({ + failure: { + description: error + }, + }); + this.call.end(); + }; + } + setupStreamedInContext() { + this.supportedEvents.push("data"); + this.supportedEvents.push("end"); + + this.call.on("data", (data) => { + this.streamDebug("Received data in"); + const deserializedCommand = this.grpcMethod.resolvedRequestType.decode(data.payload.value); + this.invokeCallback("data", deserializedCommand, this.ctx); + }); + + this.call.on("end", () => { + this.streamDebug("Received stream end"); + this.invokeCallback("end", this.ctx); + }); + + this.ctx.cancel = () => { + if (this.call.cancelled) { + this.streamDebug("cancel invoked when already cancelled."); + } else { + this.call.cancel(); + } + } + } + + invokeUserCallback(callbackName, callback, ...args) { + try { + return callback.apply(null, args); + } catch (err) { + const error = "Error handling " + callbackName; + this.streamDebug(error); + console.error(err); + if (!this.call.cancelled) { + const failure = { + failure: { + description: error + }, + }; + if (this.grpcCallback != null) { + this.grpcCallback(null, failure); + } else { + this.call.write(failure); + this.call.end(); + } + } + } + } +} module.exports = class StatelessServices { @@ -51,9 +297,8 @@ module.exports = class StatelessServices { } addService(entity, allEntities) { - this.services[entity.serviceName] = new StatelessSupport(entity.root, entity.service, { - commandHandlers: entity.commandHandlers - }, allEntities); + this.services[entity.serviceName] = new StatelessSupport(entity.root, entity.service, + entity.commandHandlers, allEntities); } entityType() { @@ -77,96 +322,67 @@ module.exports = class StatelessServices { handleStreamedIn: this.handleStreamedIn.bind(this), handleStreamedOut: this.handleStreamedOut.bind(this), handleStreamed: this.handleStreamed.bind(this), - }); + }); } - handleStreamed(call){ - call.on("data", data => { - const service = this.services[data.serviceName]; - if (service && service.commandHandlers.hasOwnProperty(data.name)) { - const userStream = { - write: (userData) => { - const grpcReturn = service.service.methods[data.name].resolvedResponseType.fromObject(userData); - const requireJsonType =true; - call.write({ - reply:{ - payload: AnySupport.serialize(grpcReturn, false, false, requireJsonType) - } - }); - }, - end: () => call.end(), - } - // We call this every time and send a way to stream back .. not sure if this is a good way to do things? - service.commandHandlers[data.name](userStream, service.deserialize(data.payload)); - }else{ - console.warn("There is no user function with name: " + data.serviceName + "." + data.name); + createHandler(call, callback, data) { + const service = this.services[data.serviceName]; + if (service && service.service.methods.hasOwnProperty(data.name)) { + if (service.commandHandlers.hasOwnProperty(data.name)) { + return new StatelessHandler(service, service.service.methods[data.name], service.commandHandlers[data.name], call, callback, data.metadata) + } else { + this.reportError("Service call " + data.serviceName + "." + data.name + " not implemented", call, callback) } - }); - call.on("end", () => { - console.debug("stream ended") - }); + } else { + this.reportError("No service call named " + data.serviceName + "." + data.name + " found", call, callback) + } } - handleStreamedOut(call){ - const data = call.request; - const service = this.services[data.serviceName]; - if (service && service.commandHandlers.hasOwnProperty(data.name)) { - const userStream = { - write: (userData) => { - const grpcReturn = service.service.methods[data.name].resolvedResponseType.fromObject(userData); - const requireJsonType =true; - call.write({ - reply:{ - payload: AnySupport.serialize(grpcReturn, false, false, requireJsonType) - } - }); - }, - end: () => call.end() + reportError(error, call, callback) { + console.warn(error); + const failure = { + failure: { + description: error } - service.commandHandlers[data.name](userStream, service.deserialize(data.payload)); - }else{ - console.warn("There is no user function with name: "+data.name, service); - } + }; + if (callback !== null) { + callback(null, failure); + } else { + call.write(failure); + call.end(); + } } - handleStreamedIn(call, callback){ + handleStreamed(call) { call.on("data", data => { - const service = this.services[data.serviceName]; - if (service && service.commandHandlers.hasOwnProperty(data.name)) { - const userReturn = service.commandHandlers[data.name](service.deserialize(data.payload)); - const grpcReturn = service.service.methods[data.name].resolvedResponseType.fromObject(userReturn); - const requireJsonType =true; - callback(null, { - reply:{ - payload: AnySupport.serialize(grpcReturn, false, false, requireJsonType) - } - }); - }else{ - console.warn("There is no user function with name: " + call.request.serviceName); - callback(); + const handler = this.createHandler(call, null, data); + if (handler) { + handler.handleStreamed(); } }); - call.on("end", () => { - console.debug("stream ended") - }); + } + handleStreamedOut(call) { + const handler = this.createHandler(call, null, call.request); + if (handler) { + handler.handleStreamedOut(); + } } - handleUnary(call, callback){ - const service = this.services[call.request.serviceName]; - if (service && service.commandHandlers.hasOwnProperty(call.request.name)) { - const userReturn = service.commandHandlers[call.request.name](service.deserialize(call.request.payload)); - const grpcReturn = service.service.methods[call.request.name].resolvedResponseType.fromObject(userReturn); - const requireJsonType =true; - var metadata = new grpc.Metadata(); - callback(null, { - reply:{ - payload: AnySupport.serialize(grpcReturn, false, false, requireJsonType) - } - }, metadata); - }else{ - console.warn("There is no user function with name: " + call.request.serviceName); - callback(); - } + handleStreamedIn(call, callback) { + call.on("data", data => { + const handler = this.createHandler(call, callback, data); + if (handler) { + handler.handleStreamedIn(); + } + }); + } + + handleUnary(call, callback) { + const handler = this.createHandler(call, callback, call.request); + if (handler) { + handler.handleUnary(); + } } + }; diff --git a/node-support/src/stateless.js b/node-support/src/stateless.js index 7aa8f53c5..6f505afa0 100644 --- a/node-support/src/stateless.js +++ b/node-support/src/stateless.js @@ -24,10 +24,11 @@ const CloudState = require("./cloudstate"); const statelessServices = new StatelessSupport(); /** - * A Stateless. + * A stateless entity * - * @memberOf module:cloudstate + * @namespace module:cloudstate.stateless */ + class Stateless { /** @@ -41,11 +42,7 @@ class Stateless { this.options = { ...{ - persistenceId: "entity", - snapshotEvery: 100, includeDirs: ["."], - serializeAllowPrimitives: false, - serializeFallbackToJson: false }, ...options }; @@ -72,7 +69,7 @@ class Stateless { * * The names of the properties must match the names of the service calls specified in the gRPC descriptor * - * @type {Object.} + * @type {Object.} */ this.commandHandlers = {}; } @@ -92,42 +89,6 @@ class Stateless { return this.root.lookupType(messageType); } - /** - * The initial state callback. - * - * @member module:cloudstate.EventSourced#initial - * @type module:cloudstate.EventSourced~initialCallback - */ - - /** - * Set the initial state callback. - * - * @param {module:cloudstate.EventSourced~initialCallback} callback The initial state callback. - * @return {module:cloudstate.EventSourced} This entity. - */ - setInitial(callback) { - this.initial = callback; - return this; - } - - /** - * The behavior callback. - * - * @member module:cloudstate.EventSourced#behavior - * @type module:cloudstate.EventSourced~behaviorCallback - */ - - /** - * Set the behavior callback. - * - * @param {module:cloudstate.EventSourced~behaviorCallback} callback The behavior callback. - * @return {module:cloudstate.EventSourced} This entity. - */ - setBehavior(callback) { - this.behavior = callback; - return this; - } - register(allEntities) { statelessServices.addService(this, allEntities); return statelessServices; diff --git a/project/build.properties b/project/build.properties index 6624da70b..06703e34d 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.5 +sbt.version=1.3.9 diff --git a/protocols/example/shoppingcart/products.proto b/protocols/example/shoppingcart/products.proto new file mode 100644 index 000000000..c787d01e7 --- /dev/null +++ b/protocols/example/shoppingcart/products.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; + +import "google/protobuf/empty.proto"; +import "cloudstate/entity_key.proto"; + +package com.example.shoppingcart; + +option go_package = "tck/shoppingcart"; + +message UpdateCartQuantityRequest { + string product_id = 1 [(.cloudstate.entity_key) = true]; + string user_id = 2; + int32 quantity = 3; +} + +message RemoveProductFromCartRequest { + string product_id = 1 [(.cloudstate.entity_key) = true]; + string user_id = 2; +} + +message GetProductRequest { + string product_id = 1 [(.cloudstate.entity_key) = true]; +} + +message Product { + int32 total_quantities = 1; + int32 total_carts = 2; +} + +service ShoppingCartProducts { + rpc UpdateCartQuantity (UpdateCartQuantityRequest) returns (google.protobuf.Empty); + rpc RemoveProductFromCart (RemoveProductFromCartRequest) returns (google.protobuf.Empty); + rpc GetProduct (GetProductRequest) returns (Product); +} diff --git a/protocols/example/shoppingcart/projection.proto b/protocols/example/shoppingcart/projection.proto new file mode 100644 index 000000000..5c78887f5 --- /dev/null +++ b/protocols/example/shoppingcart/projection.proto @@ -0,0 +1,29 @@ +// This is the public API offered by the shopping cart entity. +syntax = "proto3"; + +import "google/protobuf/empty.proto"; +import "cloudstate/eventing.proto"; +import "shoppingcart/persistence/domain.proto"; + +package com.example.shoppingcart; + +option go_package = "tck/shoppingcart"; + +service ShoppingCartProjection { + rpc HandleItemAdded (com.example.shoppingcart.persistence.ItemAdded) returns (google.protobuf.Empty) { + option (.cloudstate.eventing) = { + in: { + event_log: "shopping-cart" + } + }; + } + + rpc HandleItemRemoved (com.example.shoppingcart.persistence.ItemRemoved) returns (google.protobuf.Empty) { + option (.cloudstate.eventing) = { + in: { + event_log: "shopping-cart" + } + }; + } + +} diff --git a/protocols/example/shoppingcart/shoppingcart.proto b/protocols/example/shoppingcart/shoppingcart.proto index 27e1baf54..e2e871be4 100644 --- a/protocols/example/shoppingcart/shoppingcart.proto +++ b/protocols/example/shoppingcart/shoppingcart.proto @@ -44,7 +44,6 @@ service ShoppingCart { post: "/cart/{user_id}/items/add", body: "*", }; - option (.cloudstate.eventing).in = "items"; } rpc RemoveItem(RemoveLineItem) returns (google.protobuf.Empty) { diff --git a/protocols/frontend/cloudstate/entity_key.proto b/protocols/frontend/cloudstate/entity_key.proto index d2081212e..882c6dc3f 100644 --- a/protocols/frontend/cloudstate/entity_key.proto +++ b/protocols/frontend/cloudstate/entity_key.proto @@ -26,5 +26,5 @@ option java_package = "io.cloudstate"; option go_package = "github.com/cloudstateio/go-support/cloudstate/;cloudstate"; extend google.protobuf.FieldOptions { - bool entity_key = 50002; + bool entity_key = 1080; } diff --git a/protocols/frontend/cloudstate/eventing.proto b/protocols/frontend/cloudstate/eventing.proto index 4ca2c48a7..69f5edcc3 100644 --- a/protocols/frontend/cloudstate/eventing.proto +++ b/protocols/frontend/cloudstate/eventing.proto @@ -25,11 +25,51 @@ option java_package = "io.cloudstate"; option java_multiple_files = true; option java_outer_classname = "EventsProto"; +// Eventing configuration for a gRPC method. message Eventing { - string in = 1; - string out = 2; // Special value "discard" means do not publish + // The event source in configuration. + EventSource in = 1; + + // The event destination out configuration. + // + // Optional, if unset, messages out will not be published anywhere. + EventDestination out = 2; +} + +// Event source configuration +message EventSource { + + // The consumer group id. + // + // By default, all rpc methods on a given service with the same source will be part of the same virtual consumer + // group, messages will be routed to the different methods by type. This can be used to override that, if you want + // multiple methods to act as independent consumers of the same source (ie, if you want the same event to be + // published to each consumer) then give each consumer a unique name. + // + // Note that this does depend on the event source supporting multiple consumer groups. Queue based event sources + // may not support this. + string consumer_group = 1; + + oneof source { + + // A topic source. + // + // This will consume events from the given topic name. + string topic = 2; + + // An event log source. + // + // This will consume events from the given event log with the given persistence id. + string event_log = 3; + } +} + +message EventDestination { + oneof destination { + string topic = 1; + } } extend google.protobuf.MethodOptions { - Eventing eventing = 50003; + Eventing eventing = 1081; } diff --git a/protocols/frontend/cloudstate/legacy_entity_key.proto b/protocols/frontend/cloudstate/legacy_entity_key.proto new file mode 100644 index 000000000..24b6735d3 --- /dev/null +++ b/protocols/frontend/cloudstate/legacy_entity_key.proto @@ -0,0 +1,30 @@ +// Copyright 2019 Lightbend Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Extension for specifying which field in a message is to be considered an +// entity key, for the purposes associating gRPC calls with entities and +// sharding. + +syntax = "proto3"; + +import "google/protobuf/descriptor.proto"; + +package cloudstate; + +option java_package = "io.cloudstate"; +option go_package = "github.com/cloudstateio/go-support/cloudstate/;cloudstate"; + +extend google.protobuf.FieldOptions { + bool legacy_entity_key = 50002; +} diff --git a/protocols/protocol/cloudstate/entity.proto b/protocols/protocol/cloudstate/entity.proto index 671b93826..d06c71e31 100644 --- a/protocols/protocol/cloudstate/entity.proto +++ b/protocols/protocol/cloudstate/entity.proto @@ -27,10 +27,25 @@ import "google/protobuf/descriptor.proto"; option java_package = "io.cloudstate.protocol"; option go_package = "cloudstate/protocol"; +message Metadata { + repeated MetadataEntry entries = 1; +} + +message MetadataEntry { + string key = 1; + string value = 2; +} + // A reply to the sender. message Reply { // The reply payload google.protobuf.Any payload = 1; + + // Metadata for the reply + // + // Metadata attached to a reply will only be propagated if the underlying protocol supports per reply metadata. + // For example, message brokers may support this, but gRPC does not. + cloudstate.Metadata metadata = 2; } // Forwards handling of this request to another entity. @@ -41,6 +56,8 @@ message Forward { string command_name = 2; // The payload. google.protobuf.Any payload = 3; + // The metadata to include with the forward + Metadata metadata = 4; } // An action for the client @@ -73,6 +90,9 @@ message SideEffect { // Whether this side effect should be performed synchronously, ie, before the reply is eventually // sent, or not. bool synchronous = 4; + + // The metadata to include with the side effect + Metadata metadata = 5; } // A command. For each command received, a reply must be sent with a matching command id. @@ -92,6 +112,9 @@ message Command { // Whether the command is streamed or not bool streamed = 5; + + // The command metadata + Metadata metadata = 6; } message StreamCancelled { diff --git a/protocols/protocol/cloudstate/function.proto b/protocols/protocol/cloudstate/function.proto index d0e54ead5..9929c0e56 100644 --- a/protocols/protocol/cloudstate/function.proto +++ b/protocols/protocol/cloudstate/function.proto @@ -26,6 +26,14 @@ import "cloudstate/entity.proto"; option java_package = "io.cloudstate.protocol"; option go_package = "cloudstate/protocol"; +// A function command. +// +// For unary and streamed out calls, the service name, command name and payload will always be set. +// +// For streamed in and duplex streamed calls, the first command sent will just contain the service +// name and command name, but no payload. This will indicate that the function has been invoked. +// Subsequent commands on the stream will only have the payload set, the service name and command +// name will not be set. message FunctionCommand { // The name of the service this function is on. string service_name = 2; @@ -35,6 +43,9 @@ message FunctionCommand { // The command payload. google.protobuf.Any payload = 4; + + // Metadata + Metadata metadata = 5; } message FunctionReply { @@ -45,17 +56,67 @@ message FunctionReply { Forward forward = 3; } - repeated SideEffect side_effects = 4; + repeated SideEffect side_effects = 5; } service StatelessFunction { + // Handle a unary command. + // + // The input command will contain the service name, command name, request metadata and the command + // payload. The reply may contain a direct reply, a forward or a failure, and it may contain many + // side effects. rpc handleUnary(FunctionCommand) returns (FunctionReply) {} + // Handle a streamed in command. + // + // The first message in will contain the request metadata, including the service name and command + // name. It will not have an associated payload set. This will be followed by zero to many messages + // in with a payload, but no service name or command name set. + // + // The semantics of stream closure in this protocol map 1:1 with the semantics of gRPC stream closure, + // that is, when the client closes the stream, the stream is considered half closed, and the server + // should eventually, but not necessarily immediately, send a response message with a status code and + // trailers. + // + // If however the server sends a response message before the client closes the stream, the stream is + // completely closed, and the client should handle this and stop sending more messages. + // + // Either the client or the server may cancel the stream at any time, cancellation is indicated + // through an HTTP2 stream RST message. rpc handleStreamedIn(stream FunctionCommand) returns (FunctionReply) {} + // Handle a streamed out command. + // + // The input command will contain the service name, command name, request metadata and the command + // payload. Zero or more replies may be sent, each containing either a direct reply, a forward or a + // failure, and each may contain many side effects. The stream to the client will be closed when the + // this stream is closed, with the same status as this stream is closed with. + // + // Either the client or the server may cancel the stream at any time, cancellation is indicated + // through an HTTP2 stream RST message. rpc handleStreamedOut(FunctionCommand) returns (stream FunctionReply) {} + // Handle a full duplex streamed command. + // + // The first message in will contain the request metadata, including the service name and command + // name. It will not have an associated payload set. This will be followed by zero to many messages + // in with a payload, but no service name or command name set. + // + // Zero or more replies may be sent, each containing either a direct reply, a forward or a failure, + // and each may contain many side effects. + // + // The semantics of stream closure in this protocol map 1:1 with the semantics of gRPC stream closure, + // that is, when the client closes the stream, the stream is considered half closed, and the server + // should eventually, but not necessarily immediately, close the streamage with a status code and + // trailers. + // + // If however the server closes the stream with a status code and trailers, the stream is immediately + // considered completely closed, and no further messages sent by the client will be handled by the + // server. + // + // Either the client or the server may cancel the stream at any time, cancellation is indicated + // through an HTTP2 stream RST message. rpc handleStreamed(stream FunctionCommand) returns (stream FunctionReply) {} } diff --git a/proxy/core/src/main/proto/cloudstate/proxy/statemanager.proto b/proxy/core/src/main/proto/cloudstate/proxy/statemanager.proto index c75b4092c..776945fe4 100644 --- a/proxy/core/src/main/proto/cloudstate/proxy/statemanager.proto +++ b/proxy/core/src/main/proto/cloudstate/proxy/statemanager.proto @@ -32,6 +32,7 @@ option (scalapb.options) = { message UserFunctionCommand { string name = 1; google.protobuf.Any payload = 2; + cloudstate.Metadata metadata = 3; } message EntityCommand { @@ -39,6 +40,7 @@ message EntityCommand { string name = 2; google.protobuf.Any payload = 3; bool streamed = 4; + cloudstate.Metadata metadata = 5; } message UserFunctionReply { diff --git a/proxy/core/src/main/resources/in-memory.conf b/proxy/core/src/main/resources/in-memory.conf index b51fac2f9..3b8696cb5 100644 --- a/proxy/core/src/main/resources/in-memory.conf +++ b/proxy/core/src/main/resources/in-memory.conf @@ -3,13 +3,26 @@ include "cloudstate-common" akka.persistence { - journal.plugin = "akka.persistence.journal.inmem" + journal.plugin = inmem-journal snapshot-store.plugin = inmem-snapshot-store + +} + +inmem-journal { + class = "akka.persistence.cloudstate.InmemJournal" } inmem-snapshot-store { class = "io.cloudstate.proxy.eventsourced.InMemSnapshotStore" } -cloudstate.proxy.journal-enabled = true \ No newline at end of file +inmem-read-journal { + class = "akka.persistence.cloudstate.InmemReadJournal" +} + +cloudstate.proxy.journal { + enabled = true + read-journal = inmem-read-journal + offset-store = "io.cloudstate.proxy.eventing.InMemoryOffsetTracking" +} \ No newline at end of file diff --git a/proxy/core/src/main/resources/reference.conf b/proxy/core/src/main/resources/reference.conf index e020767a2..b2750aa78 100644 --- a/proxy/core/src/main/resources/reference.conf +++ b/proxy/core/src/main/resources/reference.conf @@ -30,8 +30,16 @@ cloudstate.proxy { action-timeout = 10s action-timeout-poll-period = 5s - # If using a config that enables it, then set to true - journal-enabled = false + journal { + # If using a config that enables it, then set to true + enabled = false + + # The id of the read journal plugin + read-journal = "" + + # The FQCN of the offset store + offset-store = "" + } stats { report-period = 1s @@ -88,7 +96,7 @@ cloudstate.proxy { # Set to "none" to disable TLS. # Valid options are, "none", and "GoogleInternetAuthorityG3.crt" - rootCa = "none" + rootCa = "none" # Valid options are, "none", and "google-application-default". callCredentials = "none" diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/EntityDiscoveryManager.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/EntityDiscoveryManager.scala index 27ee4dc8b..29728ac52 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/EntityDiscoveryManager.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/EntityDiscoveryManager.scala @@ -21,8 +21,7 @@ import akka.actor.{Actor, ActorLogging, CoordinatedShutdown, PoisonPill, Props, import akka.cluster.Cluster import akka.util.Timeout import akka.pattern.pipe -import akka.stream.scaladsl.RunnableGraph -import akka.http.scaladsl.{Http, HttpConnectionContext, UseHttp2} +import akka.http.scaladsl.Http import akka.http.scaladsl.Http.ServerBinding import akka.cluster.singleton.{ ClusterSingletonManager, @@ -31,37 +30,27 @@ import akka.cluster.singleton.{ ClusterSingletonProxySettings } import akka.grpc.GrpcClientSettings +import akka.persistence.query.PersistenceQuery +import akka.persistence.query.scaladsl.EventsByTagQuery import akka.stream.Materializer import com.google.protobuf.DescriptorProtos import com.google.protobuf.Descriptors.{FileDescriptor, ServiceDescriptor} import com.typesafe.config.Config -import io.cloudstate.protocol.entity._ import io.cloudstate.protocol.crdt.Crdt +import io.cloudstate.protocol.entity._ import io.cloudstate.protocol.event_sourced.EventSourced import io.cloudstate.protocol.function.StatelessFunction +import io.cloudstate.proxy.ConcurrencyEnforcer.ConcurrencyEnforcerSettings import io.cloudstate.proxy.StatsCollector.StatsCollectorSettings import io.cloudstate.proxy.autoscaler.Autoscaler.ScalerFactory -import io.cloudstate.proxy.ConcurrencyEnforcer.ConcurrencyEnforcerSettings -import io.cloudstate.proxy.autoscaler.{ - Autoscaler, - AutoscalerSettings, - ClusterMembershipFacadeImpl, - KubernetesDeploymentScaler, - NoAutoscaler, - NoScaler -} +import io.cloudstate.proxy.autoscaler._ import io.cloudstate.proxy.crdt.CrdtSupportFactory +import io.cloudstate.proxy.eventing.{EventLogEventing, EventingManager, EventingSupport, OffsetTracking} import io.cloudstate.proxy.eventsourced.EventSourcedSupportFactory -import io.cloudstate.proxy.eventing.EventingManager import io.cloudstate.proxy.function.StatelessFunctionSupportFactory -import scala.concurrent.Future import scala.concurrent.duration._ -//import io.cloudstate.protocol.entity.{ClientAction, EntityDiscovery, Failure, Reply, UserFunctionError} -import io.cloudstate.protocol.entity.EntityDiscovery -import io.cloudstate.proxy.EntityDiscoveryManager.ServableEntity - object EntityDiscoveryManager { final case class Configuration( devMode: Boolean, @@ -78,7 +67,7 @@ object EntityDiscoveryManager { proxyParallelism: Int, concurrencySettings: ConcurrencyEnforcerSettings, statsCollectorSettings: StatsCollectorSettings, - journalEnabled: Boolean, + journal: Option[JournalConfig], config: Config ) { validate() @@ -101,7 +90,7 @@ object EntityDiscoveryManager { cleanupPeriod = config.getDuration("action-timeout-poll-period").toMillis.millis ), statsCollectorSettings = new StatsCollectorSettings(config.getConfig("stats")), - journalEnabled = config.getBoolean("journal-enabled"), + journal = JournalConfig.fromConfig(config.getConfig("journal")), config = config) } @@ -119,6 +108,24 @@ object EntityDiscoveryManager { } } + final case class JournalConfig( + readJournal: String, + offsetStore: String + ) + + object JournalConfig { + def fromConfig(config: Config): Option[JournalConfig] = + if (config.getBoolean("enabled")) { + Some( + JournalConfig( + readJournal = config.getString("read-journal"), + offsetStore = config.getString("offset-store") + ) + ) + } else None + + } + def props(config: Configuration)(implicit mat: Materializer): Props = Props(new EntityDiscoveryManager(config)) @@ -196,7 +203,7 @@ class EntityDiscoveryManager(config: EntityDiscoveryManager.Configuration)( concurrencyEnforcer = concurrencyEnforcer, statsCollector = statsCollector) ) ++ { - if (config.journalEnabled) + if (config.journal.isDefined) Map( EventSourced.name -> new EventSourcedSupportFactory(context.system, config, @@ -207,6 +214,18 @@ class EntityDiscoveryManager(config: EntityDiscoveryManager.Configuration)( else Map.empty } + private final val eventLogEventingSupport: Option[EventingSupport] = config.journal.map { journal => + // todo probably want to pass in things like an actor system + val offsetTracking: OffsetTracking = + getClass.getClassLoader + .loadClass(journal.offsetStore) + .asSubclass(classOf[OffsetTracking]) + .newInstance() + + val query = PersistenceQuery(system).readJournalFor[EventsByTagQuery](journal.readJournal) + new EventLogEventing(offsetTracking, query, system) + } + entityDiscoveryClient.discover(EntityDiscoveryManager.proxyInfo(supportFactories.keys.toSeq)) pipeTo self override def receive: Receive = { @@ -241,14 +260,14 @@ class EntityDiscoveryManager(config: EntityDiscoveryManager.Configuration)( .mkString(",")}" ) } - } + }.toList val router = new UserFunctionRouter(entities, entityDiscoveryClient) - val eventSupport = EventingManager.createSupport(config.getConfig("eventing")) + val topicSupport = EventingManager.createSupport(config.getConfig("eventing")) + val emitters = EventingManager.createEmitters(entities, topicSupport) - val (route, eventingGraph) = - Serve.createRoute(entities, router, statsCollector, entityDiscoveryClient, descriptors, eventSupport) + val route = Serve.createRoute(entities, router, statsCollector, entityDiscoveryClient, descriptors, emitters) log.debug("Starting gRPC proxy") @@ -259,12 +278,14 @@ class EntityDiscoveryManager(config: EntityDiscoveryManager.Configuration)( interface = config.httpInterface, port = config.httpPort ) pipeTo self + + EventingManager.startConsumers(router, entities, topicSupport, eventLogEventingSupport) } // Start warmup system.actorOf(Warmup.props(spec.entities.exists(_.entityType == EventSourced.name)), "state-manager-warm-up") - context.become(binding(eventingGraph)) + context.become(binding) } catch { case e @ EntityDiscoveryException(message) => @@ -280,10 +301,10 @@ class EntityDiscoveryManager(config: EntityDiscoveryManager.Configuration)( private[this] final def extractService(serviceName: String, descriptor: FileDescriptor): Option[ServiceDescriptor] = { val (pkg, name) = Names.splitPrev(serviceName) - Some(descriptor).filter(_.getPackage == pkg).map(_.findServiceByName(name)) + Some(descriptor).filter(_.getPackage == pkg).flatMap(d => Option(d.findServiceByName(name))) } - private[this] final def binding(eventManager: Option[RunnableGraph[Future[Done]]]): Receive = { + private[this] final def binding: Receive = { case sb: ServerBinding => log.info(s"CloudState proxy online at ${sb.localAddress}") @@ -302,8 +323,6 @@ class EntityDiscoveryManager(config: EntityDiscoveryManager.Configuration)( Http().shutdownAllConnectionPools().map(_ => Done) } - eventManager.foreach(_.run() pipeTo self) - context.become(running) case Status.Failure(cause) => // Failure to bind the HTTP server is fatal, terminate @@ -317,9 +336,6 @@ class EntityDiscoveryManager(config: EntityDiscoveryManager.Configuration)( private[this] final def running: Receive = { case Ready => sender ! true - case Status.Failure(cause) => // Failure in the eventing subsystem, terminate - log.error(cause, "Eventing failed") - system.terminate() case Done => system.terminate() // FIXME context.become(dead) } diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/GrpcWebSupport.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/GrpcWebSupport.scala index e5b9c7f11..dca33db89 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/GrpcWebSupport.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/GrpcWebSupport.scala @@ -33,7 +33,7 @@ object GrpcWebSupport { private val GrpcWebContentTypeParser = "application/grpc-web(-text)?(?:\\+(\\w+))?".r def wrapGrpcHandler( - partial: PartialFunction[HttpRequest, Source[ProtobufAny, NotUsed]] + partial: PartialFunction[HttpRequest, Future[HttpResponse]] )(implicit ec: ExecutionContext, system: ActorSystem): PartialFunction[HttpRequest, Future[HttpResponse]] = Function.unlift { request => request.entity.contentType.mediaType.value match { @@ -54,18 +54,13 @@ object GrpcWebSupport { val newRequest = request.withEntity(decoded) partial - .andThen { protobufs => - // This can likely be simplified further without going via createResponse - Some(Serve.createResponse(request, protobufs).map { response => - encode(request, response, proto) - }) + .andThen { futureResponse => + Some(futureResponse.map(response => encode(request, response, proto))) } .applyOrElse(newRequest, (_: HttpRequest) => None) case _ => - partial - .andThen(protobufs => Some(Serve.createResponse(request, protobufs))) - .applyOrElse(request, (_: HttpRequest) => None) + partial.andThen(Some(_)).applyOrElse(request, (_: HttpRequest) => None) } } diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/HttpApi.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/HttpApi.scala index 3cc31459c..b0b5e34bb 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/HttpApi.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/HttpApi.scala @@ -85,7 +85,7 @@ import java.net.URLDecoder import java.util.regex.{Matcher, Pattern} import akka.grpc.GrpcProtocol -import akka.grpc.internal.{GrpcProtocolNative, Identity} +import akka.grpc.internal.{Codecs, GrpcProtocolNative, Identity} import akka.grpc.scaladsl.headers.`Message-Accept-Encoding` import akka.http.scaladsl.model.HttpEntity.LastChunk import akka.stream.scaladsl.{Sink, Source} @@ -178,7 +178,7 @@ object HttpApi { final class HttpEndpoint( final val methDesc: MethodDescriptor, final val rule: HttpRule, - final val handler: PartialFunction[HttpRequest, Source[ProtobufAny, NotUsed]] + final val handler: PartialFunction[HttpRequest, Future[(List[HttpHeader], Source[ProtobufAny, NotUsed])]] )(implicit sys: ActorSystem, mat: Materializer, ec: ExecutionContext) extends PartialFunction[HttpRequest, Future[HttpResponse]] { private[this] final val log = Logging(sys, rule.pattern.toString) // TODO use other name? @@ -493,7 +493,9 @@ object HttpApi { result.build() } - private[this] final def transformResponse(data: Source[ProtobufAny, NotUsed]): Future[HttpResponse] = { + private[this] final def transformResponse( + futureResponse: Future[(List[HttpHeader], Source[ProtobufAny, NotUsed])] + ): Future[HttpResponse] = { def extractContentTypeFromHttpBody(entityMessage: MessageOrBuilder): ContentType = entityMessage .getField(entityMessage.getDescriptorForType.findFieldByName("content_type")) match { @@ -523,10 +525,11 @@ object HttpApi { else ByteString(jsonPrinter.print(entityMessage)) ++ NEWLINE_BYTES ) - data - .prefixAndTail(1) - .runWith(Sink.head) - .map { + for { + (headers, data) <- futureResponse + firstMessage <- data.prefixAndTail(1).runWith(Sink.head) + } yield { + firstMessage match { case (Seq(protobuf: ProtobufAny), rest) => val entityMessage: MessageOrBuilder = parseResponseBody(protobuf) val contentType = @@ -544,22 +547,28 @@ object HttpApi { entity = HttpEntity.Chunked( contentType, first.concat(following) - ) + ), + headers = headers ) case (Seq(), _) => throw new IllegalStateException("Empty response") } + } } else { - data - .runWith(Sink.head) - .map { protobuf: ProtobufAny => - val entityMessage = parseResponseBody(protobuf) - HttpResponse(entity = if (isHttpBodyResponse) { + for { + (headers, data) <- futureResponse + protobuf <- data.runWith(Sink.head) + } yield { + val entityMessage = parseResponseBody(protobuf) + HttpResponse( + entity = if (isHttpBodyResponse) { HttpEntity(extractContentTypeFromHttpBody(entityMessage), extractDataFromHttpBody(entityMessage)) } else { HttpEntity(ContentTypes.`application/json`, ByteString(jsonPrinter.print(entityMessage))) - }) - } + }, + headers = headers + ) + } } } @@ -620,7 +629,11 @@ object HttpApi { } } - final def serve(services: List[(ServiceDescriptor, PartialFunction[HttpRequest, Source[ProtobufAny, NotUsed]])])( + final def serve( + services: List[ + (ServiceDescriptor, PartialFunction[HttpRequest, Future[(List[HttpHeader], Source[ProtobufAny, NotUsed])]]) + ] + )( implicit sys: ActorSystem, mat: Materializer, ec: ExecutionContext diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/Serve.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/Serve.scala index 16e275a90..18ab56517 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/Serve.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/Serve.scala @@ -17,35 +17,31 @@ package io.cloudstate.proxy import scala.collection.JavaConverters._ -import scala.concurrent.{ExecutionContext, Future, Promise} -import scala.util.Success +import scala.concurrent.{ExecutionContext, Future} import scala.annotation.tailrec -import akka.grpc.scaladsl.{GrpcExceptionHandler, GrpcMarshalling} -import GrpcMarshalling.{marshalStream, unmarshalStream} import akka.grpc.internal.{ CancellationBarrierGraphStage, Codecs, GrpcProtocolNative, GrpcResponseHelpers, - Identity, ServerReflectionImpl } import akka.grpc.scaladsl.headers.`Message-Encoding` -import akka.{Done, NotUsed} +import akka.NotUsed import akka.grpc.{ProtobufSerializer, Trailers} -import akka.grpc.javadsl.ServerReflection -import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse, StatusCodes} +import akka.http.scaladsl.model.{HttpEntity, HttpHeader, HttpRequest, HttpResponse, StatusCodes} import akka.http.scaladsl.model.Uri.Path import akka.actor.{ActorRef, ActorSystem} +import akka.http.scaladsl.model.headers.RawHeader import akka.util.ByteString import akka.stream.Materializer -import akka.stream.scaladsl.{Flow, Keep, RunnableGraph, Source} +import akka.stream.scaladsl.{Flow, Keep, Sink, Source} import com.google.protobuf.{ByteString => ProtobufByteString} import com.google.protobuf.any.{Any => ProtobufAny} import com.google.protobuf.Descriptors.{Descriptor, FileDescriptor, MethodDescriptor} import grpc.reflection.v1alpha.reflection.ServerReflectionHandler import io.cloudstate.protocol.entity._ -import io.cloudstate.proxy.eventing.{Emitter, Emitters, EventingManager, EventingSupport} +import io.cloudstate.proxy.eventing.Emitter import io.cloudstate.proxy.EntityDiscoveryManager.ServableEntity import io.cloudstate.proxy.entity.{UserFunctionCommand, UserFunctionReply} import io.cloudstate.proxy.protobuf.Types @@ -56,6 +52,7 @@ object Serve { private final val log = LoggerFactory.getLogger(getClass) private[this] final val fallback: Any => Any = _ => fallback + private final def checkFallback[B] = fallback.asInstanceOf[Any => B] private final val Http404Response: Future[HttpResponse] = @@ -77,7 +74,7 @@ object Serve { throw new UnsupportedOperationException("operation not supported") } - final class CommandSerializer(val commandName: String, desc: Descriptor) + final class CommandSerializer(val commandName: String, desc: Descriptor, metadata: Metadata) extends ProtobufSerializer[UserFunctionCommand] { final val commandTypeUrl = Types.AnyTypeUrlHostName + desc.getFullName @@ -87,68 +84,75 @@ object Serve { } override final def deserialize(bytes: ByteString): UserFunctionCommand = - parse(ProtobufAny(typeUrl = commandTypeUrl, value = ProtobufByteString.copyFrom(bytes.asByteBuffer))) + parse(ProtobufAny(typeUrl = commandTypeUrl, value = ProtobufByteString.copyFrom(bytes.asByteBuffer)), metadata) - final def parse(any: ProtobufAny): UserFunctionCommand = - UserFunctionCommand(name = commandName, payload = Some(any)) + final def parse(any: ProtobufAny, metadata: Metadata): UserFunctionCommand = + UserFunctionCommand(name = commandName, payload = Some(any), metadata = Some(metadata)) } final class CommandHandler(final val entity: ServableEntity, final val method: MethodDescriptor, final val router: UserFunctionRouter, - final val emitter: Future[Emitter], + final val emitter: Option[Emitter], final val entityDiscoveryClient: EntityDiscoveryClient, - final val log: Logger) { + final val log: Logger)(implicit ec: ExecutionContext) { final val fullCommandName: String = entity.serviceName + "." + method.getName - final val serializer: CommandSerializer = new CommandSerializer(method.getName, method.getInputType) final val unary: Boolean = !method.toProto.getClientStreaming && !method.toProto.getServerStreaming final val expectedReplyTypeUrl: String = Types.AnyTypeUrlHostName + method.getOutputType.getFullName + final val commandTypeUrl = Types.AnyTypeUrlHostName + method.getInputType.getFullName - final val flow: Flow[UserFunctionCommand, ProtobufAny, NotUsed] = { - val handler = router.handle(entity.serviceName) + final def deserialize(metadata: Metadata)(bytes: ByteString): UserFunctionCommand = { + val payload = ProtobufAny(typeUrl = commandTypeUrl, value = ProtobufByteString.copyFrom(bytes.asByteBuffer)) + UserFunctionCommand(name = method.getName, payload = Some(payload), metadata = Some(metadata)) + } - val zipWithEmitter = emitter.value match { - case Some(Success(e)) => handler.map(v => (e, v)) // Cheaper than mapAsync if already completed - case _ => handler.mapAsync(1)(reply => emitter.zip(Future.successful(reply))) - } + final val handler: Flow[UserFunctionCommand, UserFunctionReply, NotUsed] = + router.handle(entity.serviceName) - zipWithEmitter + final val processReplies: Flow[UserFunctionReply, ProtobufAny, NotUsed] = { + val handler = Flow[UserFunctionReply] .map({ - case ( - emitter, - UserFunctionReply(Some(ClientAction(ClientAction.Action.Reply(Reply(some @ Some(payload), _)), _)), _, _) - ) => + case UserFunctionReply(Some(ClientAction(ClientAction.Action.Reply(reply @ Reply(Some(payload), _, _)), _)), + _, + _) => if (payload.typeUrl != expectedReplyTypeUrl) { val msg = - s"${fullCommandName}: Expected reply type_url to be [${expectedReplyTypeUrl}] but was [${payload.typeUrl}]." + s"$fullCommandName: Expected reply type_url to be [$expectedReplyTypeUrl] but was [${payload.typeUrl}]." log.warn(msg) entityDiscoveryClient.reportError(UserFunctionError(s"Warning: $msg")) - } else { - val _ = emitter.emit(payload, method) // TODO: check returned boolean? } - some - case (_, UserFunctionReply(Some(ClientAction(ClientAction.Action.Forward(_), _)), _, _)) => + Some(reply) + case UserFunctionReply(Some(ClientAction(ClientAction.Action.Forward(_), _)), _, _) => log.error("Cannot serialize forward reply, this should have been handled by the UserFunctionRouter") None - case (_, - UserFunctionReply(Some(ClientAction(ClientAction.Action.Failure(Failure(_, message, _)), _)), _, _)) => + case UserFunctionReply(Some(ClientAction(ClientAction.Action.Failure(Failure(_, message, _)), _)), _, _) => log.error("User Function responded with a failure: {}", message) None case _ => None }) .collect(Function.unlift(identity)) + + emitter match { + case Some(e) => + handler.mapAsync(4) { + case Reply(Some(payload), metadata, _) => + e.emit(payload, method, metadata).map(_ => payload) + } + case None => handler.map(_.payload.get) + } + } } - private[proxy] def createResponse(request: HttpRequest, protobufs: Source[ProtobufAny, NotUsed])( + private def createResponse(request: HttpRequest, headers: List[HttpHeader], protobufs: Source[ProtobufAny, NotUsed])( implicit system: ActorSystem - ): Future[HttpResponse] = { + ): HttpResponse = { val responseWriter = GrpcProtocolNative.newWriter(Codecs.negotiate(request)) - Future.successful( + val response = GrpcResponseHelpers(protobufs, Serve.mapRequestFailureExceptions)(Serve.ReplySerializer, responseWriter, system) - ) + response.withHeaders(response.headers ++ headers) } def createRoute(entities: Seq[ServableEntity], @@ -156,15 +160,23 @@ object Serve { statsCollector: ActorRef, entityDiscoveryClient: EntityDiscoveryClient, fileDescriptors: Seq[FileDescriptor], - eventingSupport: Option[EventingSupport])( + emitters: Map[String, Emitter])( implicit sys: ActorSystem, mat: Materializer, ec: ExecutionContext - ): (PartialFunction[HttpRequest, Future[HttpResponse]], Option[RunnableGraph[Future[Done]]]) = { - val (grpcProxy, eventingGraph) = - createGrpcApi(entities, router, statsCollector, entityDiscoveryClient, eventingSupport) + ): PartialFunction[HttpRequest, Future[HttpResponse]] = { + val grpcProxy = createGrpcApi(entities, router, statsCollector, entityDiscoveryClient, emitters) + val grpcHandler = Function.unlift { request: HttpRequest => + val asResponse = grpcProxy.andThen { futureResult => + Some(futureResult.map { + case (headers, messages) => + createResponse(request, headers, messages) + }) + } + asResponse.applyOrElse(request, (_: HttpRequest) => None) + } val routes = Array( - GrpcWebSupport.wrapGrpcHandler(grpcProxy), + GrpcWebSupport.wrapGrpcHandler(grpcHandler), HttpApi.serve(entities.map(_.serviceDescriptor -> grpcProxy).toList), handleNetworkProbe(), ServerReflectionHandler.partial( @@ -172,7 +184,7 @@ object Serve { ) ) - ({ // Creates a fast implementation of multi-PartialFunction composition + { // Creates a fast implementation of multi-PartialFunction composition case req => @tailrec def matchRoutes(req: HttpRequest, idx: Int): Future[HttpResponse] = if (idx < routes.length) { @@ -183,8 +195,9 @@ object Serve { log.debug("Not found request: " + req.getUri()) Http404Response } + matchRoutes(req, 0) - }, eventingGraph) + } } /** @@ -206,64 +219,62 @@ object Serve { router: UserFunctionRouter, statsCollector: ActorRef, entityDiscoveryClient: EntityDiscoveryClient, - eventingSupport: Option[EventingSupport])( + emitters: Map[String, Emitter])( implicit sys: ActorSystem, mat: Materializer, ec: ExecutionContext - ): (PartialFunction[HttpRequest, Source[ProtobufAny, NotUsed]], Option[RunnableGraph[Future[Done]]]) = { - val p = Promise[Emitter] - val eventingGraph = - eventingSupport.flatMap( - support => - EventingManager - .createStreams(router, entityDiscoveryClient, entities, support) - .map(_.mapMaterializedValue({ case (emitter, done) => p.success(emitter); done })) - ) match { - case None => - p.success(Emitters.ignore) - None - case some => - some - } + ): PartialFunction[HttpRequest, Future[(List[HttpHeader], Source[ProtobufAny, NotUsed])]] = { val rpcMethodSerializers = (for { entity <- entities.iterator method <- entity.serviceDescriptor.getMethods.iterator.asScala } yield { - val handler = new CommandHandler(entity, method, router, p.future, entityDiscoveryClient, log) + val handler = + new CommandHandler(entity, method, router, emitters.get(method.getFullName), entityDiscoveryClient, log) (Path / entity.serviceName / method.getName, handler) }).toMap - val routes: PartialFunction[HttpRequest, Source[ProtobufAny, NotUsed]] = { + val routes: PartialFunction[HttpRequest, Future[(List[HttpHeader], Source[ProtobufAny, NotUsed])]] = { case req: HttpRequest if rpcMethodSerializers.contains(req.uri.path) => - val startTime = System.nanoTime() val handler = rpcMethodSerializers(req.uri.path) - // Only report request stats for unary commands, doesn't make sense for streamed - val processRequest = - if (handler.unary) { - statsCollector ! StatsCollector.RequestReceived - handler.flow.watchTermination() { (_, complete) => - complete.onComplete { _ => - statsCollector ! StatsCollector.ResponseSent(System.nanoTime() - startTime) - } - NotUsed - } - } else { - handler.flow - } + val metadata = Metadata(req.headers.map { header => + MetadataEntry(header.name(), header.value) + }) val reader = GrpcProtocolNative.newReader(Codecs.detect(req).get) req.entity.dataBytes .viaMat(reader.dataFrameDecoder)(Keep.none) - .map(handler.serializer.deserialize) + .map(handler.deserialize(metadata)) .via(new CancellationBarrierGraphStage) // In gRPC we signal failure by returning an error code, so we don't want the cancellation bubbled out - .via(processRequest) + .via(handler.handler) + .prefixAndTail(1) + .runWith(Sink.head) + .map { + case (maybeFirstReply, rest) => + val (metadata, replies) = maybeFirstReply match { + case Seq( + reply @ UserFunctionReply(Some(ClientAction(ClientAction.Action.Reply(Reply(_, metadata, _)), _)), + _, + _) + ) => + (metadata.getOrElse(Metadata.defaultInstance), Source.single(reply).concat(rest)) + case other => + (Metadata.defaultInstance, Source(other).concat(rest)) + } + + val headers = metadata.entries.map { + case MetadataEntry(key, value, _) => RawHeader(key, value) + }.toList + + (headers, replies.via(handler.processReplies)) + } } - (routes, eventingGraph) + routes } private case class CommandException(msg: String) extends RuntimeException(msg, null, false, false) + } diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/UserFunctionRouter.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/UserFunctionRouter.scala index dae5ee3bb..672f84e4b 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/UserFunctionRouter.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/UserFunctionRouter.scala @@ -3,7 +3,7 @@ package io.cloudstate.proxy import akka.NotUsed import akka.stream.Materializer import akka.stream.scaladsl.{Flow, Sink, Source} -import io.cloudstate.protocol.entity.{ClientAction, EntityDiscovery, Forward, SideEffect, UserFunctionError} +import io.cloudstate.protocol.entity.{ClientAction, EntityDiscovery, Forward, Metadata, SideEffect, UserFunctionError} import io.cloudstate.proxy.EntityDiscoveryManager.ServableEntity import io.cloudstate.proxy.entity.{UserFunctionCommand, UserFunctionReply} @@ -24,11 +24,17 @@ class UserFunctionRouter(val entities: Seq[ServableEntity], entityDiscovery: Ent final def handle(serviceName: String): Flow[UserFunctionCommand, UserFunctionReply, NotUsed] = Flow[UserFunctionCommand].flatMapConcat { command => - routeMessage(Nil, RouteReason.Initial, serviceName, command.name, command.payload, synchronous = true) + routeMessage(Nil, + RouteReason.Initial, + serviceName, + command.name, + command.payload, + synchronous = true, + command.metadata) } final def handleUnary(serviceName: String, command: UserFunctionCommand): Future[UserFunctionReply] = - routeMessageUnary(Nil, RouteReason.Initial, serviceName, command.name, command.payload) + routeMessageUnary(Nil, RouteReason.Initial, serviceName, command.name, command.payload, command.metadata) private final def route( trace: List[(RouteReason, String, String)] @@ -36,13 +42,15 @@ class UserFunctionRouter(val entities: Seq[ServableEntity], entityDiscovery: Ent Flow[UserFunctionReply].flatMapConcat { response => val sideEffects = Source(response.sideEffects.toList) .flatMapConcat { - case SideEffect(serviceName, commandName, payload, synchronous, _) => - routeMessage(trace, RouteReason.SideEffect, serviceName, commandName, payload, synchronous) + case SideEffect(serviceName, commandName, payload, synchronous, metadata, _) => + routeMessage(trace, RouteReason.SideEffect, serviceName, commandName, payload, synchronous, metadata) } val nextAction = response.clientAction match { - case Some(ClientAction(ClientAction.Action.Forward(Forward(serviceName, commandName, payload, _)), _)) => - routeMessage(trace, RouteReason.Forwarded, serviceName, commandName, payload, synchronous = true) + case Some( + ClientAction(ClientAction.Action.Forward(Forward(serviceName, commandName, payload, metadata, _)), _) + ) => + routeMessage(trace, RouteReason.Forwarded, serviceName, commandName, payload, synchronous = true, metadata) case None | Some(ClientAction(ClientAction.Action.Empty, _)) => Source.empty case _ => @@ -63,7 +71,8 @@ class UserFunctionRouter(val entities: Seq[ServableEntity], entityDiscovery: Ent RouteReason.SideEffect, sideEffect.serviceName, sideEffect.commandName, - sideEffect.payload) + sideEffect.payload, + sideEffect.metadata) if (sideEffect.synchronous) { sideEffectFuture } else { @@ -72,8 +81,10 @@ class UserFunctionRouter(val entities: Seq[ServableEntity], entityDiscovery: Ent } } flatMap { _ => response.clientAction match { - case Some(ClientAction(ClientAction.Action.Forward(Forward(serviceName, commandName, payload, _)), _)) => - routeMessageUnary(trace, RouteReason.Forwarded, serviceName, commandName, payload) + case Some( + ClientAction(ClientAction.Action.Forward(Forward(serviceName, commandName, payload, metadata, _)), _) + ) => + routeMessageUnary(trace, RouteReason.Forwarded, serviceName, commandName, payload, metadata) case _ => Future.successful(response) } @@ -84,13 +95,14 @@ class UserFunctionRouter(val entities: Seq[ServableEntity], entityDiscovery: Ent serviceName: String, commandName: String, payload: Option[com.google.protobuf.any.Any], - synchronous: Boolean): Source[UserFunctionReply, NotUsed] = { + synchronous: Boolean, + metadata: Option[Metadata]): Source[UserFunctionReply, NotUsed] = { val source = entityCommands.get(serviceName) match { case Some(EntityCommands(_, entitySupport, commands)) => if (commands(commandName)) { Source - .single(UserFunctionCommand(commandName, payload)) + .single(UserFunctionCommand(commandName, payload, metadata)) .via(entitySupport.handler(commandName)) .via(route((routeReason, serviceName, commandName) :: trace)) } else { @@ -115,11 +127,12 @@ class UserFunctionRouter(val entities: Seq[ServableEntity], entityDiscovery: Ent routeReason: RouteReason, serviceName: String, commandName: String, - payload: Option[com.google.protobuf.any.Any]): Future[UserFunctionReply] = + payload: Option[com.google.protobuf.any.Any], + metadata: Option[Metadata]): Future[UserFunctionReply] = entityCommands.get(serviceName) match { case Some(EntityCommands(_, entitySupport, commands)) => if (commands(commandName)) { - entitySupport.handleUnary(UserFunctionCommand(commandName, payload)).flatMap { result => + entitySupport.handleUnary(UserFunctionCommand(commandName, payload, metadata)).flatMap { result => routeUnary((routeReason, serviceName, commandName) :: trace, result) } } else { diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/UserFunctionTypeSupport.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/UserFunctionTypeSupport.scala index a75fccc92..169c4306d 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/UserFunctionTypeSupport.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/UserFunctionTypeSupport.scala @@ -2,15 +2,16 @@ package io.cloudstate.proxy import akka.NotUsed import akka.stream.scaladsl.Flow -import com.google.protobuf.Descriptors.{FieldDescriptor, MethodDescriptor, ServiceDescriptor} +import com.google.protobuf.Descriptors.{MethodDescriptor, ServiceDescriptor} import com.google.protobuf.{ByteString, DynamicMessage} import io.cloudstate.protocol.entity.Entity import io.cloudstate.entity_key.EntityKeyProto +import io.cloudstate.legacy_entity_key.LegacyEntityKeyProto import io.cloudstate.proxy.entity.{EntityCommand, UserFunctionCommand, UserFunctionReply} import io.cloudstate.proxy.protobuf.Options import scala.collection.JavaConverters._ -import scala.concurrent.Future +import scala.concurrent.{ExecutionContext, Future} trait UserFunctionTypeSupport { @@ -27,7 +28,7 @@ trait UserFunctionTypeSupportFactory { /** * Abstract support for any user function type that is entity based (ie, has entity id keys). */ -abstract class EntityTypeSupportFactory extends UserFunctionTypeSupportFactory { +abstract class EntityTypeSupportFactory(implicit ec: ExecutionContext) extends UserFunctionTypeSupportFactory { override final def build(entity: Entity, serviceDescriptor: ServiceDescriptor): UserFunctionTypeSupport = { require(serviceDescriptor != null, "ServiceDescriptor not found, please verify the spelling and package name provided when looking it up") @@ -53,7 +54,11 @@ private object EntityMethodDescriptor { final class EntityMethodDescriptor(val method: MethodDescriptor) { private[this] val keyFields = method.getInputType.getFields.iterator.asScala - .filter(field => EntityKeyProto.entityKey.get(Options.convertFieldOptions(field))) + .filter( + field => + EntityKeyProto.entityKey.get(Options.convertFieldOptions(field)) || + LegacyEntityKeyProto.legacyEntityKey.get(Options.convertFieldOptions(field)) + ) .toArray .sortBy(_.getIndex) @@ -75,7 +80,7 @@ final class EntityMethodDescriptor(val method: MethodDescriptor) { private final class EntityUserFunctionTypeSupport(serviceDescriptor: ServiceDescriptor, methodDescriptors: Map[String, EntityMethodDescriptor], - entityTypeSupport: EntityTypeSupport) + entityTypeSupport: EntityTypeSupport)(implicit ec: ExecutionContext) extends UserFunctionTypeSupport { override def handler(name: String): Flow[UserFunctionCommand, UserFunctionReply, NotUsed] = { @@ -91,7 +96,8 @@ private final class EntityUserFunctionTypeSupport(serviceDescriptor: ServiceDesc EntityCommand(entityId = entityId, name = command.name, payload = command.payload, - streamed = method.method.isServerStreaming) + streamed = method.method.isServerStreaming, + metadata = command.metadata) } private def methodDescriptor(name: String): EntityMethodDescriptor = diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/crdt/CrdtEntity.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/crdt/CrdtEntity.scala index e4ba630e7..087ebff2e 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/crdt/CrdtEntity.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/crdt/CrdtEntity.scala @@ -574,7 +574,7 @@ final class CrdtEntity(client: Crdt, configuration: CrdtEntity.Configuration, en case Deleted(_) => // Ignore, we know. - case EntityCommand(_, _, _, streamed, _) => + case EntityCommand(_, _, _, streamed, metadata, _) => val reply = UserFunctionReply( Some(ClientAction(ClientAction.Action.Failure(Failure(description = "Entity deleted")))) ) diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventLogEventing.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventLogEventing.scala new file mode 100644 index 000000000..f2e2b7842 --- /dev/null +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventLogEventing.scala @@ -0,0 +1,132 @@ +package io.cloudstate.proxy.eventing + +import java.net.URLEncoder +import java.util.concurrent.atomic.AtomicBoolean + +import akka.actor.{Actor, ActorSystem, Cancellable, PoisonPill, Props} +import akka.cluster.singleton.{ClusterSingletonManager, ClusterSingletonManagerSettings} +import akka.persistence.query.{EventEnvelope, Offset, Sequence, TimeBasedUUID} +import akka.persistence.query.scaladsl.EventsByTagQuery +import akka.stream.{KillSwitches, Materializer} +import akka.stream.scaladsl.{Flow, Keep, RestartSource, Sink, Source} +import io.cloudstate.eventing +import com.google.protobuf.any.{Any => ProtoAny} + +import scala.concurrent.duration._ + +class EventLogEventing(offsetTracking: OffsetTracking, eventsByTagQuery: EventsByTagQuery, system: ActorSystem)( + implicit mat: Materializer +) extends EventingSupport { + override def name: String = "event-log" + + override def supportsSource: Boolean = true + + override def createSource(source: eventing.EventSource, serviceName: String): EventSource = + source.source match { + case eventing.EventSource.Source.EventLog(persistenceId) => + val consumerGroup = source.consumerGroup match { + case "" => serviceName + case cg => cg + } + + val actorName = URLEncoder.encode(s"$persistenceId/$consumerGroup", "utf-8") + + new EventSource { + override type SourceEventRef = Offset + + override def run(flow: Flow[SourceEvent[Offset], Offset, _]): Cancellable = { + val managerSettings = ClusterSingletonManagerSettings(system) + + val singleton = system.actorOf( + ClusterSingletonManager.props( + Props(new EventLogActor(consumerGroup, persistenceId, flow)), + terminationMessage = PoisonPill, + managerSettings + ), + actorName + ) + + new Cancellable { + private val running = new AtomicBoolean() + + override def cancel(): Boolean = + if (running.compareAndSet(false, true)) { + singleton ! PoisonPill + true + } else { + false + } + + override def isCancelled: Boolean = running.get() + } + } + + } + + } + + override def supportsDestination: Boolean = false + + override def createDestination(destination: eventing.EventDestination): EventDestination = + throw new UnsupportedOperationException + + private class EventLogActor(consumerId: String, tag: String, flow: Flow[SourceEvent[Offset], Offset, _]) + extends Actor { + + import context.dispatcher + + private def createStream() = + Source + .fromFutureSource( + offsetTracking + .loadOffset(consumerId, tag) + .map(offset => eventsByTagQuery.eventsByTag(tag, offset)) + ) + .map(transformEvent) + .via(flow) + .groupedWithin(20, 1.second) + .mapAsync(1) { offsets => + offsetTracking.acknowledge(consumerId, tag, offsets.last) + } + + private val runningStream = + RestartSource + .withBackoff(3.seconds, 30.seconds, 0.2)(createStream) + .viaMat(KillSwitches.single)(Keep.right) + .to(Sink.ignore) + .run() + + private def transformEvent(event: EventEnvelope): SourceEvent[Offset] = { + val cloudEvent = event.event match { + case ProtoAny(typeUrl, bytes, _) => + val entityId = event.persistenceId.dropWhile(_ != '|').tail + CloudEvent( + id = offsetToMessageId(event.offset), + source = tag, + specversion = "1.0", + `type` = typeUrl, + datacontenttype = "application/" + EventingManager.ProtobufAnyMediaSubType, + dataschema = None, + subject = Some(entityId), + time = None, + data = Some(bytes) + ) + case other => + throw new IllegalStateException(s"Don't know how to handle event log message of type ${other.getClass}") + } + + SourceEvent(cloudEvent, event.offset) + } + + override def postStop(): Unit = runningStream.shutdown() + + override def receive: Receive = PartialFunction.empty + } + + private def offsetToMessageId(offset: Offset) = + offset match { + case Sequence(seq) => seq.toString + case TimeBasedUUID(uuid) => uuid.toString + case other => throw new IllegalArgumentException(s"Unsupported offset: $other") + } +} diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventingManager.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventingManager.scala index c3ec5de77..5ebc2c261 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventingManager.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventingManager.scala @@ -1,202 +1,519 @@ package io.cloudstate.proxy.eventing +import java.time.Instant +import java.util.UUID +import java.util.concurrent.atomic.AtomicBoolean + import akka.{Done, NotUsed} -import akka.actor.Cancellable -import akka.stream.{FlowShape, Materializer, OverflowStrategy} -import akka.stream.scaladsl.{Flow, GraphDSL, Merge, Partition, RunnableGraph, Sink, Source} -import io.cloudstate.protocol.entity.{ClientAction, EntityDiscoveryClient, Failure, Reply, UserFunctionError} -import io.cloudstate.proxy.{Serve, UserFunctionRouter} +import akka.actor.{ActorSystem, Cancellable} +import akka.http.scaladsl.model.{MediaType, MediaTypes} +import akka.stream.scaladsl.{Broadcast, Flow, GraphDSL, Partition, Source, Zip} +import akka.stream.{FlowShape, Materializer} +import com.google.protobuf.Descriptors.MethodDescriptor +import com.google.protobuf.TextFormat.ParseException +import com.google.protobuf.any.{Any => ProtobufAny} +import com.google.protobuf.{ByteString, CodedOutputStream, UnsafeByteOperations, WireFormat} +import com.typesafe.config.Config +import io.cloudstate.eventing.{ + Eventing, + EventingProto, + EventDestination => EventDestinationProto, + EventSource => EventSourceProto +} +import io.cloudstate.protocol.entity.{ClientAction, Metadata, MetadataEntry} import io.cloudstate.proxy.EntityDiscoveryManager.ServableEntity -import io.cloudstate.proxy.protobuf.{Options, Types} +import io.cloudstate.proxy.UserFunctionRouter import io.cloudstate.proxy.entity.{UserFunctionCommand, UserFunctionReply} -import io.cloudstate.eventing.{Eventing, EventingProto} -import com.google.protobuf.any.{Any => ProtobufAny} -import com.google.protobuf.Descriptors.MethodDescriptor +import io.cloudstate.proxy.protobuf.Options +import io.cloudstate.proxy.streams.MergeSequence +import org.slf4j.LoggerFactory import scala.collection.JavaConverters._ import scala.concurrent.Future -import com.typesafe.config.Config -import org.slf4j.LoggerFactory -import Serve.CommandHandler trait Emitter { - def emit(payload: ProtobufAny, method: MethodDescriptor): Boolean + def emit(payload: ProtobufAny, method: MethodDescriptor, metadata: Option[Metadata]): Future[Done] } object Emitters { - val ignore: Emitter = new Emitter { - override def emit(payload: ProtobufAny, method: MethodDescriptor): Boolean = false - } -} + val ignore: Emitter = (payload: ProtobufAny, method: MethodDescriptor, metadata: Option[Metadata]) => + Future.successful(Done) + + def eventDestinationEmitter(eventDestination: EventDestination): Emitter = + new EventDestinationEmitter(eventDestination) -trait EventingSupport { - def createSource(sourceName: String, handler: CommandHandler): Source[UserFunctionCommand, Future[Cancellable]] - def createDestination(destinationName: String, handler: CommandHandler): Flow[ProtobufAny, AnyRef, NotUsed] + private class EventDestinationEmitter(eventDestination: EventDestination) extends Emitter { + override def emit(payload: ProtobufAny, method: MethodDescriptor, metadata: Option[Metadata]): Future[Done] = + eventDestination.emitSingle( + EventingManager.createDesintationEvent(payload, method.getService.getFullName, metadata) + ) + } } object EventingManager { final val log = LoggerFactory.getLogger("EventingManager") - final case class EventMapping private (entity: ServableEntity, routes: Map[MethodDescriptor, Eventing]) + final case class EventConsumer(eventSource: EventSourceProto, + entity: ServableEntity, + methods: Map[String, EventConsumerMethod], + outs: Vector[EventDestinationProto]) - private val noEmitter = Future.successful(Emitters.ignore) + final case class EventConsumerMethod(eventing: Eventing, methodDescriptor: MethodDescriptor, outIndex: Option[Int]) - // Contains all entities which has at least one endpoint which accepts events - def createEventMappings(entities: Seq[ServableEntity]): Seq[EventMapping] = + private def createConsumers(entities: List[ServableEntity]): List[EventConsumer] = entities.flatMap { entity => - val endpoints = - entity.serviceDescriptor.getMethods.iterator.asScala.foldLeft(Map.empty[MethodDescriptor, Eventing]) { + val unindexedConsumers = entity.serviceDescriptor.getMethods.iterator.asScala + .foldLeft(Map.empty[EventSourceProto, EventConsumer]) { case (map, method) => EventingProto.eventing.get(Options.convertMethodOptions(method)) match { case None => map case Some(e) => (e.in, e.out) match { - case (null, null) | ("", "") => map - case (in, out) if in == out => + case (None, None) => map + case (Some(in), Some(out)) if in.source.topic.exists(out.destination.topic.contains) => throw new IllegalStateException( - s"Endpoint '${method.getFullName}' has the same input topic as output topic ('${in}'), this is not allowed." + s"Endpoint [${method.getFullName}] has the same input topic as output topic [${in.source.topic.getOrElse("")}], this is not allowed." ) - case (in, out) => - log.debug("EventingProto.events for {}: {} -> {}", - method.getFullName: AnyRef, - in: AnyRef, - out: AnyRef) - map.updated(method, e) + case (Some(in), _) => + map.get(in) match { + case Some(consumer) => + consumer.methods.get(method.getInputType.getFullName).foreach { conflict => + throw new IllegalStateException( + s"Endpoints [${conflict.methodDescriptor.getFullName}] and [${method.getFullName}] both subscribe to the same event source with the same input type. If you wish to define two independent consumers of the same event source, use the consumer_group annotation on the source to identify them." + ) + } + map.updated(in, + consumer.copy( + methods = consumer.methods.updated(method.getInputType.getFullName, + EventConsumerMethod(e, method, None)) + )) + case None => + map.updated( + in, + EventConsumer(in, + entity, + Map(method.getInputType.getFullName -> EventConsumerMethod(e, method, None)), + Vector.empty) + ) + } } } } + .values + + unindexedConsumers.map { consumer => + val outs = consumer.methods.values + .flatMap(_.eventing.out) + .toVector + .distinct - if (endpoints.isEmpty) Nil - else List(EventMapping(entity, endpoints)) + val indexedOuts = outs.zipWithIndex.toMap + + val indexedMethods = consumer.methods.toList.map { + case (inputType, method) => (inputType, method.copy(outIndex = method.eventing.out.flatMap(indexedOuts.get))) + }.toMap + + consumer.copy(methods = indexedMethods, outs = outs) + } } - def createSupport(eventConfig: Config)(implicit materializer: Materializer): Option[EventingSupport] = + def createEmitters( + entities: List[ServableEntity], + topicSupport: Option[EventingSupport] + )(implicit system: ActorSystem, mat: Materializer): Map[String, Emitter] = { + val outs = entities + .flatMap(_.serviceDescriptor.getMethods.iterator().asScala) + .foldLeft(Map.empty[EventDestinationProto, List[MethodDescriptor]]) { + case (map, method) => + EventingProto.eventing.get(Options.convertMethodOptions(method)) match { + case None => map + case Some(Eventing(_, Some(out), _)) => + map.get(out) match { + case Some(methods) => + map.updated(out, method :: methods) + case None => + map.updated(out, method :: Nil) + } + case _ => map + } + } + + outs.flatMap { + case (dest @ EventDestinationProto(EventDestinationProto.Destination.Topic(topic), _), methods) => + val emitter = topicSupport match { + case Some(support) => Emitters.eventDestinationEmitter(support.createDestination(dest)) + case None => + throw new IllegalArgumentException( + s"Service call [${methods.head.getFullName}] declares an event destination topic of [$topic], but not topic support is configured." + ) + } + methods.map(method => method.getFullName -> emitter) + case (_, methods) => + throw new IllegalArgumentException( + s"Service call [${methods.head.getFullName}] has declared an event out with no topic." + ) + } + } + + def createSupport(eventConfig: Config)(implicit system: ActorSystem, + materializer: Materializer): Option[EventingSupport] = eventConfig.getString("support") match { case "none" => log.info("Eventing support turned off in configuration") None case s @ "google-pubsub" => log.info("Creating google-pubsub eventing support") - Some(new GCPubsubEventingSupport(eventConfig.getConfig(s), materializer)) + Some(new GCPubsubEventingSupport(eventConfig.getConfig(s))) case other => throw new IllegalStateException(s"Check your configuration. There is no eventing support named: $other") } - def createStreams(router: UserFunctionRouter, - entityDiscoveryClient: EntityDiscoveryClient, - entities: Seq[ServableEntity], - support: EventingSupport): Option[RunnableGraph[(Emitter, Future[Done])]] = - createEventMappings(entities) match { - case Nil => None - case eventMappings => - val allEligibleOutputsByMethodDescriptor = - eventMappings - .flatMap(_.routes.collect { - case (m, e) if e.out != "" => (m.getFullName, e.out) - }) - .toMap - - type TopicName = String - type Record = (TopicName, ProtobufAny) - - val inNOutBurger: Seq[ - (Option[(TopicName, Source[Record, Future[Cancellable]])], Option[(TopicName, Flow[Record, AnyRef, NotUsed])]) - ] = - for { - EventMapping(entity, routes) <- eventMappings - (mdesc, eventing) <- routes.toSeq // Important since we do not want dedupe that we get from the map otherwise - } yield { - log.info("Creating route for {}", eventing) - val commandHandler = new CommandHandler(entity, mdesc, router, noEmitter, entityDiscoveryClient, log) // Could we reuse these from Serve? - - val in = Option(eventing.in).collect({ - case topic if topic != "" => - val source = - support - .createSource(topic, commandHandler) - .via(commandHandler.flow) - .collect({ case any if eventing.out != "" => (eventing.out, any) }) //Without an out there is nothing to persist - (topic, source) - }) - - val out = Option(eventing.out).collect({ - case topic if topic != "" => - val dest = Flow[Record] - .map(_._2) - .via(support.createDestination(topic, commandHandler)) - .dropWhile(_ => true) - (topic, dest) - }) - - (in, out) - } + def startConsumers( + router: UserFunctionRouter, + entities: List[ServableEntity], + topicSupport: Option[EventingSupport], + eventLogSupport: Option[EventingSupport] + ): Cancellable = { - val sources = inNOutBurger.collect({ case (Some(in), _) => in }) + val consumers = createConsumers(entities) - val deadLetters = - Flow[Record] - .map({ - case (topic, msg) => - log.warn( - s"Message destined for eventing topic '${topic}' discarded since no such topic is found in the configuration." - ) - msg - }) - .dropWhile(_ => true) + consumers match { + case Nil => Cancellable.alreadyCancelled + case consumers => + val running = consumers.map(startConsumer(router, topicSupport, eventLogSupport)) - val destinations = - ("", deadLetters) +: inNOutBurger.collect({ case (_, Some(out)) => out }) + new Cancellable { + override def cancel(): Boolean = + running.foldLeft(true)((success, cancellable) => success || cancellable.cancel()) - val emitter = - Source.queue[Record](destinations.size * 128, OverflowStrategy.backpressure) + override def isCancelled: Boolean = running.forall(_.isCancelled) + } + } + } - val destinationMap = destinations.map(_._1).sorted.zipWithIndex.toMap - val destinationSelector = - (r: Record) => destinationMap.get(r._1).getOrElse(destinationMap("")) // Send to deadLetters if no match + private def startConsumer( + router: UserFunctionRouter, + topicSupport: Option[EventingSupport], + eventLogSupport: Option[EventingSupport] + )(consumer: EventConsumer): Cancellable = { + + val maybeEventingSupport = consumer.eventSource match { + case EventSourceProto(_, EventSourceProto.Source.Topic(_), _) => + topicSupport + case EventSourceProto(_, EventSourceProto.Source.EventLog(_), _) => + eventLogSupport + case EventSourceProto(_, EventSourceProto.Source.Empty, _) => + throw new IllegalArgumentException( + s"Eventing consumer [${consumer.methods.head._1}] has declared an input with no source." + ) + } - val eventingFlow = Flow - .fromGraph(GraphDSL.create() { implicit b => - import GraphDSL.Implicits._ + val eventSource = maybeEventingSupport match { + case Some(eventingSupport) if eventingSupport.supportsSource => + eventingSupport.createSource(consumer.eventSource, consumer.entity.serviceName) + case Some(eventingSupport) => + throw new IllegalArgumentException( + s"Eventing consumer [${consumer.methods.head._1}] has declared an input of [${eventingSupport.name}], but this does not support being used as an event source." + ) + case None => + throw new IllegalArgumentException( + s"Eventing consumer [${consumer.methods.head._1}] has declared a [${consumer.eventSource.source}] event source, but this event source isn't supported." + ) + } - val mergeForPublish = b.add(Merge[Record](sources.size + 1)) // 1 + for emitter + val eventDestinations = consumer.outs.map { + case dest @ EventDestinationProto(EventDestinationProto.Destination.Topic(topic), _) => + topicSupport match { + case Some(support) => support.createDestination(dest) + case None => + throw new IllegalArgumentException( + s"Eventing consumer has declared an output topic [$topic}], but no topic eventing support has been provided." + ) + } + case EventDestinationProto(EventDestinationProto.Destination.Empty, _) => + throw new IllegalArgumentException(s"Eventing consumer has declared an input with no destination.") + } - val routeToDestination = - b.add(Partition[Record](destinations.size, destinationSelector)) + val killSwitch = eventSource.run( + entityToCommand[eventSource.SourceEventRef](consumer) via + routeCommands(router) via + forwardToOutputs(consumer, eventDestinations) + ) + + new Cancellable { + private val cancelled = new AtomicBoolean() + + override def cancel(): Boolean = + if (cancelled.compareAndSet(false, true)) { + killSwitch.cancel() + true + } else { + false + } - val mergeForExit = b.add(Merge[AnyRef](destinations.size)) + override def isCancelled: Boolean = cancelled.get() + } + } - sources.zipWithIndex foreach { - case ((topicName, source), idx) => - b.add(source).out ~> mergeForPublish.in(idx + 1) // 0 we keep for emitter - } + /** + * This flow is responsible for turning an entity that has come in from a event source into a command. + * + * The command is paired with information about which method it should be routed to, and where the result should be + * output to. + */ + private def entityToCommand[Ref](consumer: EventConsumer): Flow[SourceEvent[Ref], CommandIn[Ref], NotUsed] = + Flow[SourceEvent[Ref]].map { sourceEvent => + val cloudEvent = sourceEvent.event + + val messageAny = MediaType.parse(cloudEvent.datacontenttype) match { + case Right(protobuf) if protobuf.isApplication && ProtobufMediaSubTypes(protobuf.subType) => + val messageType = protobuf.params + .get("proto") + .orElse(protobuf.params.get("messageType")) + .getOrElse(cloudEvent.`type`) + ProtobufAny("type.googleapis.com/" + messageType, cloudEvent.data.getOrElse(ByteString.EMPTY)) + + case Right(any) if any.isApplication && any.subType == ProtobufAnyMediaSubType => + // This is the content type that event logging will use + ProtobufAny(cloudEvent.`type`, cloudEvent.data.getOrElse(ByteString.EMPTY)) + + case Right(MediaTypes.`application/json`) => + encodeJsonToAny(cloudEvent.data, cloudEvent.`type`) + + case Right(typedJson) if typedJson.isApplication && typedJson.subType.endsWith("+json") => + encodeJsonToAny(cloudEvent.data, cloudEvent.`type`) + + case Right(utf8) if utf8.isText && utf8.params.get("charset").forall(_ == "utf-8") => + // Fast case for UTF-8 so we don't have to decode and reencode it + encodeUtf8StringBytesToAny(cloudEvent.data) + + case Right(string) if string.isText => + encodeStringToAny(cloudEvent.data.getOrElse(ByteString.EMPTY).toString(string.params("charset"))) + + case _ => + encodeBytesToAny(cloudEvent.data) + } + + // Select a method + val maybeConsumerMethod = + if (messageAny.typeUrl.startsWith("p.cloudstate.io/") || messageAny.typeUrl.startsWith("json.cloudstate.io/")) { + consumer.methods.get(ProtobufAny.scalaDescriptor.fullName) + } else { + val desiredType = messageAny.typeUrl.split("/", 2).last + consumer.methods.get(desiredType).orElse(consumer.methods.get(ProtobufAny.scalaDescriptor.fullName)) + } + + val consumerMethod = maybeConsumerMethod match { + case Some(method) => + method + case None => + throw new IllegalArgumentException( + s"No method can be found to handle protobuf type of [${messageAny.typeUrl}] on input ${consumer.eventSource}. Either declare a method for this type, or declare a method that accepts google.protobuf.Any." + ) + } + + val command = UserFunctionCommand( + name = consumerMethod.methodDescriptor.getName, + payload = Some(messageAny), + metadata = Some(cloudEventToMetadata(cloudEvent)) + ) + + CommandIn(sourceEvent.ref, consumerMethod, command) + } - mergeForPublish.out ~> routeToDestination.in + private def cloudEventToMetadata(cloudEvent: CloudEvent): Metadata = { + // We use the HTTP binary mode transcoding rules + val builder = Seq.newBuilder[MetadataEntry] + builder += MetadataEntry("ce-id", cloudEvent.id) + builder += MetadataEntry("ce-source", cloudEvent.source) + builder += MetadataEntry("ce-specversion", cloudEvent.specversion) + builder += MetadataEntry("ce-type", cloudEvent.`type`) + builder += MetadataEntry("Content-Type", cloudEvent.datacontenttype) - destinations foreach { - case (topicName, flow) => - routeToDestination.out(destinationMap(topicName)) ~> b.add(flow) ~> mergeForExit + cloudEvent.dataschema.foreach(v => builder += MetadataEntry("ce-dataschema", v)) + cloudEvent.subject.foreach(v => builder += MetadataEntry("ce-subject", v)) + cloudEvent.time.foreach(v => builder += MetadataEntry("ce-time", v.toString)) + + Metadata(builder.result()) + } + + /** + * This flow is responsible for routing commands through the router. + */ + private def routeCommands[Ref](router: UserFunctionRouter): Flow[CommandIn[Ref], RouteResult[Ref], NotUsed] = + Flow[CommandIn[Ref]].flatMapConcat { + case CommandIn(eventSourceRef, consumerMethod, command) => + if (consumerMethod.methodDescriptor.isServerStreaming || consumerMethod.methodDescriptor.isClientStreaming) { + Source + .single(command) + .via(router.handle(consumerMethod.methodDescriptor.getService.getFullName)) + .map(reply => ResultPart(consumerMethod.outIndex, reply)) + .concat(Source.single(ResultEnd(eventSourceRef))) + } else { + Source + .fromFuture(router.handleUnary(consumerMethod.methodDescriptor.getService.getFullName, command)) + .mapConcat { reply => + List(ResultPart(consumerMethod.outIndex, reply), ResultEnd(eventSourceRef)) } + } - FlowShape(mergeForPublish.in(0), mergeForExit.out) - }) - - Some( - emitter - .via(eventingFlow) - .toMat(Sink.ignore)((queue, future) => { - val emitter = new Emitter { - override def emit(event: ProtobufAny, method: MethodDescriptor): Boolean = - if (event.value.isEmpty) false - else { - allEligibleOutputsByMethodDescriptor - .get(method.getFullName) // FIXME Check expected type of event compared to method.getOutputType - .map(out => queue.offer((out, event))) // FIXME handle this Future - .isDefined - } - } - (emitter, future) - }) - ) } + + /** + * This flow is responsible for forwarding routing result replies on to the configuration destinations. + */ + private def forwardToOutputs[Ref](consumer: EventConsumer, + destinations: Vector[EventDestination]): Flow[RouteResult[Ref], Ref, NotUsed] = + if (consumer.outs.isEmpty) { + Flow[RouteResult[Ref]].collect { + case ResultEnd(ref) => ref + } + } else { + Flow[RouteResult[Ref]].zipWithIndex + .via(Flow.fromGraph(GraphDSL.create() { implicit b => + import GraphDSL.Implicits._ + + val bypassPort = consumer.outs.size + val ports = bypassPort + 1 + + val broadcast = b.add( + Partition[(RouteResult[Ref], Long)](ports, + result => + result._1 match { + case ResultPart(outIdx, _) => outIdx.getOrElse(bypassPort) + case _ => bypassPort + }) + ) + + val merge = b.add(new MergeSequence[(RouteResult[Ref], Long)](ports)(_._2)) + + destinations.zipWithIndex.foreach { + case (dest, outPort) => + val split = b.add(Broadcast[(RouteResult[Ref], Long)](2)) + val zip = b.add(Zip[AnyRef, (RouteResult[Ref], Long)]()) + + broadcast.out(outPort) ~> split.in + + split.out(0).collect { + case (ResultPart(_, UserFunctionReply(Some(ClientAction(ClientAction.Action.Reply(reply), _)), _, _)), + _) => + createDesintationEvent(reply.payload.get, consumer.entity.serviceName, reply.metadata) + case (ResultPart(_, other), _) => + throw new IllegalStateException(s"Reply from router did not have a reply client action: $other") + // Shouldn't happen: + case t => throw new IllegalStateException(s"result end routed through output flow? $t") + } ~> dest.eventStreamOut ~> zip.in0 + split.out(1) ~> zip.in1 + + zip.out.map(_._2) ~> merge.in(outPort) + } + + broadcast.out(bypassPort) ~> merge.in(bypassPort) + FlowShape(broadcast.in, merge.out) + })) + .collect { + case (ResultEnd(ref), _) => ref + } + } + + def createDesintationEvent(payload: ProtobufAny, serviceName: String, maybeMetadata: Option[Metadata]) = { + val metadata = maybeMetadata + .getOrElse(Metadata.defaultInstance) + .entries + .map(e => e.key -> e.value) + .toMap + + val (ceType, contentType, bytes) = payload.typeUrl match { + case json if json.startsWith("json.cloudstate.io/") => + (json.stripPrefix("json.cloudstate.io/"), "application/json", decodeBytes(payload)) + case "p.cloudstate.io/string" => + ("", "text/plain; charset=utf-8", decodeBytes(payload)) + case "p.cloudstate.io/bytes" => + ("", "application/octet-stream", decodeBytes(payload)) + case generic => + (generic.dropWhile(_ != '/').drop(1), "application/protobuf", payload.value) + } + + DestinationEvent( + CloudEvent( + id = metadata.getOrElse("ce-id", UUID.randomUUID().toString), + source = metadata.getOrElse("ce-source", serviceName), + specversion = metadata.getOrElse("ce-specversion", "1.0"), + `type` = metadata.getOrElse("ce-type", ceType), + datacontenttype = metadata.getOrElse("Content-Type", contentType), + dataschema = metadata.get("ce-dataschema"), + subject = metadata.get("ce-subject"), + // todo the time can be any RFC3339 time string, Instant.parse only parses ISO8601, which is just one allowable + // format in RFC3339 + time = metadata.get("ce-time").map(Instant.parse).orElse(Some(Instant.now())), + data = Some(bytes) + ) + ) + } + + private case class CommandIn[Ref](eventSourceRef: Ref, + consumerMethod: EventConsumerMethod, + command: UserFunctionCommand) + + private sealed trait RouteResult[+Ref] + + private case class ResultPart(outIdx: Option[Int], out: UserFunctionReply) extends RouteResult[Nothing] + + private case class ResultEnd[Ref](sourceEventRef: Ref) extends RouteResult[Ref] + + private val ProtobufMediaSubTypes = Set("protobuf", "x-protobuf", "vnd.google.protobuf") + val ProtobufAnyMediaSubType = "vnd.cloudstate.protobuf.any" + + private def encodeByteArray(maybeBytes: Option[ByteString]) = maybeBytes match { + case None => ByteString.EMPTY + case Some(bytes) if bytes.isEmpty => + // Create a byte array the right size. It needs to have the tag and enough space to hold the length of the data + // (up to 5 bytes). + // Length encoding consumes 1 byte for every 7 bits of the field + val bytesLengthFieldSize = ((31 - Integer.numberOfLeadingZeros(bytes.size())) / 7) + 1 + val byteArray = new Array[Byte](1 + bytesLengthFieldSize) + val stream = CodedOutputStream.newInstance(byteArray) + stream.writeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED) + stream.writeUInt32NoTag(bytes.size()) + UnsafeByteOperations.unsafeWrap(byteArray).concat(bytes) + } + + private def encodeBytesToAny(bytes: Option[ByteString]): ProtobufAny = + ProtobufAny("p.cloudstate.io/bytes", encodeByteArray(bytes)) + + private def encodeJsonToAny(bytes: Option[ByteString], jsonType: String): ProtobufAny = + ProtobufAny("json.cloudstate.io/" + jsonType, encodeByteArray(bytes)) + + private def encodeUtf8StringBytesToAny(bytes: Option[ByteString]): ProtobufAny = + ProtobufAny("p.cloudstate.io/string", encodeByteArray(bytes)) + + private def encodeStringToAny(string: String): ProtobufAny = { + val builder = ByteString.newOutput() + val stream = CodedOutputStream.newInstance(builder) + stream.writeString(1, string) + ProtobufAny("p.cloudstate.io/string", builder.toByteString) + } + + private def decodeBytes(payload: ProtobufAny): ByteString = { + val stream = payload.value.newCodedInput() + @annotation.tailrec + def findField(): ByteString = + stream.readTag() match { + case 0 => + // 0 means EOF + ByteString.EMPTY + case feild1 if WireFormat.getTagFieldNumber(feild1) == 1 => + if (WireFormat.getTagWireType(feild1) == WireFormat.WIRETYPE_LENGTH_DELIMITED) { + stream.readBytes() + } else { + throw new ParseException("Expected length delimited field, tag was: " + feild1) + } + case other => + stream.skipField(other) + findField() + } + findField() + } + } diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventingSupport.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventingSupport.scala new file mode 100644 index 000000000..2b32cfb4e --- /dev/null +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/EventingSupport.scala @@ -0,0 +1,98 @@ +package io.cloudstate.proxy.eventing + +import java.time.Instant + +import akka.actor.Cancellable +import akka.{Done, NotUsed} +import akka.http.scaladsl.model.HttpEntity +import akka.stream.KillSwitch +import akka.stream.scaladsl.Flow +import com.google.protobuf.ByteString +import io.cloudstate.eventing.{EventDestination => EventDestinationProto, EventSource => EventSourceProto} + +import scala.concurrent.Future + +/** + * Eventing support. + * + * Different eventing implementations should implement this. + */ +trait EventingSupport { + def name: String + + /** Can this eventing support implementation be used as a source? */ + def supportsSource: Boolean + + /** Create a source for the given name which should route to the given handler. */ + def createSource(source: EventSourceProto, serviceName: String): EventSource + + /** Can this eventing support implementation be used as a destination? */ + def supportsDestination: Boolean + + /** Create a destination for the given destination name using the given handler. */ + def createDestination(destination: EventDestinationProto): EventDestination +} + +/** An event source. */ +trait EventSource { + + /** + * A type used to refer to the source event. The type of this and what it contains depends on the eventing + * implementation, it could be the whole event, or it could just be an identifier. The purpose for this is to + * allow the reference to be emitted from the flow to run the stream, so that it can be acknowledged. + */ + type SourceEventRef + + /** Run this event source with the given flow. */ + def run(flow: Flow[SourceEvent[SourceEventRef], SourceEventRef, _]): Cancellable +} + +/** An event destination. */ +trait EventDestination { + + /** + * The flow that consumes events to publish. + * + * This flow must produce one element for each event consumed, that message signals the acknowledgement that the + * corresponding consumed element has been successfully published, hence the upstream source can now be acknowledged. + * Order matters, the flow should now emit elements for events received later in the stream until the earlier events + * in the stream have been successfully published with corresponding events emitted for acknowledgement. + * + * In general, implementations should not attempt to retry when publishing fails. Rather, if a failure in publishing + * occurs, the stream should be terminated with an error. Cloudstate will then handle retries, using exponential + * backoffs, or routing to dead letters, etc. + */ + def eventStreamOut: Flow[DestinationEvent, AnyRef, NotUsed] + + /** + * Emit a single destination event. + * + * This is used when emitting events from a service call that have not come from another event source, eg, they + * have come from a gRPC call. + */ + def emitSingle(destinationEvent: DestinationEvent): Future[Done] +} + +/** An event produced by an event source. */ +case class SourceEvent[Ref]( + event: CloudEvent, + /** A reference to this source event */ + ref: Ref +) + +/** An event to be published to a destination */ +case class DestinationEvent( + event: CloudEvent +) + +case class CloudEvent( + id: String, + source: String, + specversion: String, + `type`: String, + datacontenttype: String, + dataschema: Option[String], + subject: Option[String], + time: Option[Instant], + data: Option[ByteString] +) diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/GooglePubsubEventing.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/GooglePubsubEventing.scala index 20efb7820..a7008580d 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/GooglePubsubEventing.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/GooglePubsubEventing.scala @@ -1,18 +1,15 @@ package io.cloudstate.proxy.eventing +import java.time.Instant +import java.time.format.DateTimeFormatter + import com.typesafe.config.{Config, ConfigFactory} -import akka.NotUsed +import akka.{Done, NotUsed} import akka.actor.{ActorSystem, Cancellable} import akka.grpc.GrpcClientSettings -import akka.stream.Materializer -import akka.stream.scaladsl.{Flow, Keep, Sink, Source} -import io.cloudstate.proxy.Serve.CommandHandler -import io.cloudstate.proxy.EntityDiscoveryManager.ServableEntity -import io.cloudstate.proxy.entity.UserFunctionCommand -import io.cloudstate.eventing.Eventing -import com.google.protobuf.any.{Any => ProtobufAny} -import com.google.protobuf.{ByteString => ProtobufByteString} -import com.google.protobuf.Descriptors.MethodDescriptor +import akka.stream.{KillSwitch, KillSwitches, Materializer, OverflowStrategy, SourceShape} +import akka.stream.scaladsl.{Broadcast, Concat, Flow, GraphDSL, Keep, RestartSource, Sink, Source} +import io.cloudstate.eventing.{EventDestination => EventDestinationProto, EventSource => EventSourceProto} import io.grpc.{ CallCredentials => gRPCCallCredentials, Status => gRPCStatus, @@ -21,21 +18,25 @@ import io.grpc.{ import io.grpc.auth.MoreCallCredentials import com.google.auth.oauth2.GoogleCredentials import com.google.pubsub.v1.pubsub.{ - AcknowledgeRequest, PublishRequest, - PublishResponse, PubsubMessage, ReceivedMessage, StreamingPullRequest, + StreamingPullResponse, Subscription, Topic, PublisherClient => ScalaPublisherClient, SubscriberClient => ScalaSubscriberClient } import java.util.Collections +import java.util.concurrent.atomic.AtomicBoolean + +import com.google.protobuf.ByteString +import io.cloudstate.eventing -import scala.util.Try -import scala.concurrent.{Future, Promise} +import scala.collection.immutable +import scala.util.{Success, Try} +import scala.concurrent.{ExecutionContext, Future, Promise} import scala.concurrent.duration._ /** @@ -99,23 +100,23 @@ final class PubSubSettings private ( def createClientSettings()(implicit sys: ActorSystem): GrpcClientSettings = { val sslConfig = rootCa.fold("") { rootCa => s""" - |ssl-config { - | disabledKeyAlgorithms = [] - | trustManager = { - | stores = [ - | { type = "PEM", path = "$rootCa", classpath = true } - | ] - | } - |}""".stripMargin + |ssl-config { + | disabledKeyAlgorithms = [] + | trustManager = { + | stores = [ + | { type = "PEM", path = "$rootCa", classpath = true } + | ] + | } + |}""".stripMargin } val akkaGrpcConfig = s""" - |host = "$host" - |port = $port - | - |$sslConfig - |""".stripMargin + |host = "$host" + |port = $port + | + |$sslConfig + |""".stripMargin val settings = //TODO consider using Discovery and/or other settings from: https://github.com/akka/akka-grpc/blob/master/runtime/src/main/resources/reference.conf#L36 GrpcClientSettings.fromConfig( @@ -136,8 +137,11 @@ object GCPubsubEventingSupport { final val USING_CRD = "using-crd" } -class GCPubsubEventingSupport(config: Config, materializer: Materializer) extends EventingSupport { +class GCPubsubEventingSupport(config: Config)(implicit materializer: Materializer, system: ActorSystem) + extends EventingSupport { + import GCPubsubEventingSupport._ + import system.dispatcher final val projectId: String = config.getString("project-id") final val pollInterval: FiniteDuration = config.getDuration("poll-interval").toMillis.millis @@ -173,10 +177,6 @@ class GCPubsubEventingSupport(config: Config, materializer: Materializer) extend // Create the gRPC clients used to communicate with Google Pubsub final val (subscriberClient, publisherClient) = { - implicit val m = materializer - implicit val s = materializer.system - implicit val d = s.dispatcher - val clientSettings = settings.createClientSettings() // We're reusing the same clients for all communication @@ -184,149 +184,284 @@ class GCPubsubEventingSupport(config: Config, materializer: Materializer) extend val publisherClient = ScalaPublisherClient(clientSettings) // Make sure that we don't leak connections - s.registerOnTermination(subscriberClient.close()) - s.registerOnTermination(publisherClient.close()) + system.registerOnTermination(subscriberClient.close()) + system.registerOnTermination(publisherClient.close()) (subscriberClient, publisherClient) } private[this] val batchResults = if (downstreamBatchDeadline > 0.seconds || downstreamBatchSize > 1) { - Flow[ProtobufAny] - .map(any => PubsubMessage(data = any.toByteString)) + Flow[DestinationEvent] + .map(transformDestinationEvent) .groupedWithin(downstreamBatchSize, downstreamBatchDeadline) - } else Flow[ProtobufAny].map(any => PubsubMessage(data = any.toByteString) :: Nil) + } else Flow[DestinationEvent].map(event => transformDestinationEvent(event) :: Nil) - private[this] final def createManualSource( + private def sourceToSourceToFlow[In, Out, MOut](f: Source[In, NotUsed] => Source[Out, MOut]): Flow[In, Out, NotUsed] = + Flow[In].prefixAndTail(0).flatMapConcat { case (Nil, in) => f(in) } + + private[this] final def runManualFlow( subscription: String, - commandHandler: CommandHandler - ): Source[UserFunctionCommand, Future[Cancellable]] = { - val cancellable = Promise[Cancellable] + processingFlow: Flow[SourceEvent[String], String, _] + ): KillSwitch = { val request = StreamingPullRequest(subscription = subscription, streamAckDeadlineSeconds = upstreamAckDeadlineSeconds) - val pull = Source - .single(request) - .concat( - Source.tick(0.seconds, pollInterval, request.withSubscription("")).mapMaterializedValue(cancellable.success) - ) + val streamingPull: Flow[StreamingPullRequest, StreamingPullResponse, NotUsed] = + sourceToSourceToFlow(subscriberClient.streamingPull) + + val source = RestartSource.withBackoff(3.seconds, 30.seconds, 0.2) { () => + Source.fromGraph(GraphDSL.create[SourceShape[Nothing]]() { implicit builder => + import GraphDSL.Implicits._ + + val concat = builder.add(Concat[StreamingPullRequest](2)) + val outSplitter = builder.add(Broadcast[StreamingPullResponse](2, eagerCancel = true)) + + val responseToEvents = Flow[StreamingPullResponse] + .mapConcat(_.receivedMessages.toVector) // Note: receivedMessages is most likely a Vector already due to impl, so should be a noop + .map(transformReceivedMessage(subscription)) + + val acksToRequest = Flow[String] + .groupedWithin(10, upstreamAckDeadline / 2) + .map(ackIds => StreamingPullRequest(ackIds = ackIds)) + + val circularDeadlockBreaker = Flow[StreamingPullRequest].buffer(1, OverflowStrategy.backpressure) + + // This is the main line through the graph, where we send our pull request for the subscription down the + // streaming pull gRPC request, convert to events, process, convert acks to requests (which acknowledge them), + // then send those back to the streaming pull via the concat + // In the middle we have the outSplitter, it's positioned after the streamingPull, so that when it cancels, + // it immediately cancels the pull, but allows the events in progress to continue to be processed. + Source.single(request) ~> + concat ~> + streamingPull ~> + outSplitter ~> + responseToEvents ~> + processingFlow ~> + acksToRequest ~> + circularDeadlockBreaker ~> + concat + + // Meanwhile, we peel off the output using outSplitter, and ignore everything in it. + // This allows us to return a Source that when a failure occurs anywhere in the stream, will emit a failure, + // so the RestartSource can handle it and restart. It also allows us to cancel the stream via that source when + // we shutdown. + val out = outSplitter.collect(PartialFunction.empty) + + SourceShape(out.outlet) + }) + } - val ackSink = - Flow[ReceivedMessage] - .map(_.ackId) - .groupedWithin(10, upstreamAckDeadline / 2) // TODO adjust these - .mapAsyncUnordered(1 /*parallelism*/ )( - ackIds => subscriberClient.acknowledge(AcknowledgeRequest(subscription = subscription, ackIds = ackIds)) - ) - .toMat(Sink.ignore)(Keep.right) - - subscriberClient // FIXME add retries, backoff etc - .streamingPull(pull) // TODO Consider Source.repeat(()).flatMapConcat(_ => subscriberClient.streamingPull(pull)) - .mapConcat(_.receivedMessages.toVector) // Note: receivedMessages is most likely a Vector already due to impl, so should be a noop - .alsoTo(ackSink) // at-most-once // FIXME Add stats generation/collection so we can track progress here - .collect({ - case ReceivedMessage(_, Some(msg), _) => - commandHandler.serializer.parse(ProtobufAny.parseFrom(msg.data.newCodedInput)) - }) // TODO - investigate ProtobufAny.fromJavaAny(PbAnyJava.parseFrom(msg.data)) - .mapMaterializedValue(_ => cancellable.future) + source + .viaMat(KillSwitches.single)(Keep.right) + .to(Sink.ignore) + .run() } - private[this] final def createByProxyManagedSource( + private[this] final def runByProxyManagedFlow( sourceName: String, subscription: String, - commandHandler: CommandHandler - ): Source[UserFunctionCommand, Future[Cancellable]] = - Source - .setup { (mat, attrs) => - val topic = s"projects/${projectId}/topics/${sourceName}" - implicit val ec = mat.system.dispatcher - val t = Topic(topic) - val s = Subscription( - name = subscription, - topic = topic, - pushConfig = None, - ackDeadlineSeconds = upstreamAckDeadlineSeconds, - retainAckedMessages = false, - messageRetentionDuration = None // TODO configure this? - ) - - Source - .fromFutureSource( - for { - _ <- publisherClient - .createTopic(t) - .recover({ - case ex: gRPCStatusRuntimeException if ex.getStatus.getCode == gRPCStatus.Code.ALREADY_EXISTS => - t - }) - _ <- subscriberClient - .createSubscription(s) - .recover({ - case ex: gRPCStatusRuntimeException if ex.getStatus.getCode == gRPCStatus.Code.ALREADY_EXISTS => - s - }) - } yield createManualSource(subscription, commandHandler) - ) - } - .mapMaterializedValue(_.flatten.flatten) + processingFlow: Flow[SourceEvent[String], String, _] + ): Future[KillSwitch] = { + val topic = s"projects/${projectId}/topics/${sourceName}" + val t = Topic(topic) + val s = Subscription( + name = subscription, + topic = topic, + pushConfig = None, + ackDeadlineSeconds = upstreamAckDeadlineSeconds, + retainAckedMessages = false, + messageRetentionDuration = None // TODO configure this? + ) + + for { + _ <- publisherClient + .createTopic(t) + .recover({ + case ex: gRPCStatusRuntimeException if ex.getStatus.getCode == gRPCStatus.Code.ALREADY_EXISTS => + t + }) + _ <- subscriberClient + .createSubscription(s) + .recover({ + case ex: gRPCStatusRuntimeException if ex.getStatus.getCode == gRPCStatus.Code.ALREADY_EXISTS => + s + }) + } yield runManualFlow(subscription, processingFlow) + } - private[this] final def createUsingCrdManagedSource( + private[this] final def createUsingCrdManagedFlow( sourceName: String, subscription: String, - commandHandler: CommandHandler - ): Source[UserFunctionCommand, Future[Cancellable]] = + processingFlow: Flow[SourceEvent[String], String, _] + ): Future[KillSwitch] = throw new IllegalStateException("NOT IMPLEMENTED YET") // FIXME IMPLEMENT THIS: create CRD-requests - override final def createSource(sourceName: String, - commandHandler: CommandHandler): Source[UserFunctionCommand, Future[Cancellable]] = { - val subscription = s"projects/${projectId}/subscriptions/${sourceName}_${commandHandler.fullCommandName}" - manageTopicsAndSubscriptions match { - case MANUALLY => createManualSource(subscription, commandHandler) - case USING_CRD => createUsingCrdManagedSource(sourceName, subscription, commandHandler) - case BY_PROXY => createByProxyManagedSource(sourceName, subscription, commandHandler) + override def name: String = "Google PubSub" + + override def supportsSource: Boolean = true + + override def createSource(source: EventSourceProto, serviceName: String): EventSource = new EventSource { + override type SourceEventRef = String + + override def run(flow: Flow[SourceEvent[String], String, _]): Cancellable = { + val consumerGroup = source.consumerGroup match { + case "" => serviceName + case cg => cg + } + val subscription = source.source match { + case EventSourceProto.Source.Topic(topic) => + s"projects/$projectId/subscriptions/${topic}_$consumerGroup" + case other => + throw new IllegalArgumentException(s"Google PubSub source unable to be used to server $other") + + } + val killSwitch = manageTopicsAndSubscriptions match { + case MANUALLY => runManualFlow(subscription, flow) + case USING_CRD => futureKillSwitch(createUsingCrdManagedFlow(consumerGroup, subscription, flow)) + case BY_PROXY => futureKillSwitch(runByProxyManagedFlow(consumerGroup, subscription, flow)) + } + + new Cancellable { + private val running = new AtomicBoolean() + + override def cancel(): Boolean = + if (running.compareAndSet(false, true)) { + killSwitch.shutdown() + true + } else { + false + } + + override def isCancelled: Boolean = running.get() + } } } - private[this] final def createDestination(topic: String): Flow[ProtobufAny, AnyRef, NotUsed] = - batchResults - .mapAsyncUnordered(1 /*parallelism*/ )( - batch => - publisherClient.publish(PublishRequest(topic = topic, messages = batch)) // FIXME add retries, backoff etc - ) + private def transformReceivedMessage(source: String)(receivedMessage: ReceivedMessage): SourceEvent[String] = { + val message = + receivedMessage.message.getOrElse(throw new IllegalArgumentException("Received message has no message")) + // Using the spec here, which was not merged, to handle cloudevents: + // https://github.com/google/knative-gcp/pull/1 + // case sensitivity? + val maybeContentType = message.attributes.get("Content-Type").orElse(message.attributes.get("content-type")) + val cloudEvent = maybeContentType match { + case Some("application/cloudevents+json") => + // Todo: handle structured cloudevents + throw new UnsupportedOperationException("CloudEvents structured binding not yet supported") + + case defaultCt if message.attributes.contains("ce-specversion") => + CloudEvent( + id = message.attributes.getOrElse("ce-id", message.messageId), + source = message.attributes.getOrElse("ce-source", source), + specversion = message.attributes("ce-specversion"), + `type` = message.attributes.getOrElse("ce-type", ""), + datacontenttype = + message.attributes.get("ce-datacontenttype").orElse(defaultCt).getOrElse("application/octet-stream"), + dataschema = message.attributes.get("ce-dataschema"), + subject = message.attributes.get("ce-subject"), + time = message.attributes + .get("ce-time") + .flatMap(t => Try(Instant.from(DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(t))).toOption), + data = Some(message.data) + ) + + case _ => + CloudEvent( + id = message.messageId, + source = source, + specversion = "1.0", + `type` = "", + datacontenttype = maybeContentType.getOrElse("application/octet-stream"), + dataschema = None, + subject = None, + time = message.publishTime.map(t => Instant.ofEpochSecond(t.seconds, t.nanos)), + data = Some(message.data) + ) + } + + SourceEvent(cloudEvent, receivedMessage.ackId) + } + + override def supportsDestination: Boolean = true + + override def createDestination(destination: eventing.EventDestination): EventDestination = new EventDestination { + + private val topic = destination.destination match { + case EventDestinationProto.Destination.Topic(topic) => + s"projects/$projectId/topic/$topic" + case other => + throw new IllegalArgumentException(s"Google PubSub source unable to be used to server $other") + } + + private val topicReady: Future[Done] = manageTopicsAndSubscriptions match { + case MANUALLY => Future.successful(Done) + case USING_CRD => createUsingCrdManagedDestination(topic = topic) + case BY_PROXY => createByProxyManagedDestination(topic = topic) + } + + private val destinationFlow: Flow[DestinationEvent, AnyRef, NotUsed] = + batchResults + .mapAsyncUnordered(1 /*parallelism*/ )( + batch => publisherClient.publish(PublishRequest(topic = topic, messages = batch)) + ) + .mapConcat(_.messageIds.to[immutable.Seq]) + + override def eventStreamOut: Flow[DestinationEvent, AnyRef, NotUsed] = + topicReady.value match { + case Some(Success(_)) => + destinationFlow + case _ => + Flow + .lazyInitAsync(() => topicReady.map(_ => destinationFlow)) + .mapMaterializedValue(_ => NotUsed) + } - private[this] final def createByProxyManagedDestination(topic: String): Flow[ProtobufAny, AnyRef, NotUsed] = - Flow - .setup { (mat, attrs) => - implicit val ec = mat.system.dispatcher - val destination = createDestination(topic = topic) - val t = Topic(topic) - val f = publisherClient - .createTopic(t) - .recover({ - case ex: gRPCStatusRuntimeException if ex.getStatus.getCode == gRPCStatus.Code.ALREADY_EXISTS => - t - }) - .map(_ => destination) - - Flow.lazyInitAsync(() => f) + override def emitSingle(destinationEvent: DestinationEvent): Future[Done] = + topicReady.value match { + case Some(Success(_)) => + publisherClient + .publish(PublishRequest(topic, Seq(transformDestinationEvent(destinationEvent)))) + .map(_ => Done) + case _ => + for { + _ <- topicReady + _ <- publisherClient.publish(PublishRequest(topic, Seq(transformDestinationEvent(destinationEvent)))) + } yield Done } - .mapMaterializedValue(_ => NotUsed) + } - private[this] final def createUsingCrdManagedDestination(topic: String): Flow[ProtobufAny, AnyRef, NotUsed] = + private[this] final def createByProxyManagedDestination(topic: String): Future[Done] = + publisherClient + .createTopic(Topic(topic)) + .map(_ => Done) + .recover({ + case ex: gRPCStatusRuntimeException if ex.getStatus.getCode == gRPCStatus.Code.ALREADY_EXISTS => + Done + }) + + private def transformDestinationEvent(destinationEvent: DestinationEvent): PubsubMessage = { + val attributes = Map( + "ce-id" -> destinationEvent.event.id, + "ce-source" -> destinationEvent.event.source, + "ce-specversion" -> destinationEvent.event.specversion, + "ce-type" -> destinationEvent.event.`type`, + "ce-datacontenttype" -> destinationEvent.event.datacontenttype + ) ++ + destinationEvent.event.subject.map(s => "ce-subject" -> s) ++ + destinationEvent.event.dataschema.map(d => "ce-dataschema" -> d) ++ + destinationEvent.event.time.map(t => "ce-time" -> DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(t)) + + PubsubMessage(destinationEvent.event.data.getOrElse(ByteString.EMPTY), attributes) + } + + private[this] final def createUsingCrdManagedDestination(topic: String): Future[Done] = throw new IllegalStateException("NOT IMPLEMENTED YET") // FIXME IMPLEMENT THIS: create CRD-requests - //FIXME Add stats generation/collection so we can track progress here - override final def createDestination(destinationName: String, - handler: CommandHandler): Flow[ProtobufAny, AnyRef, NotUsed] = - if (destinationName == "") - Flow[ProtobufAny] - else { - val topic = s"projects/${projectId}/topics/${destinationName}" - manageTopicsAndSubscriptions match { - case MANUALLY => createDestination(topic = topic) - case USING_CRD => createUsingCrdManagedDestination(topic = topic) - case BY_PROXY => createByProxyManagedDestination(topic = topic) - } - } + private def futureKillSwitch(future: Future[KillSwitch])(implicit ec: ExecutionContext): KillSwitch = new KillSwitch { + override def shutdown(): Unit = future.foreach(_.shutdown()) + + override def abort(ex: Throwable): Unit = future.foreach(_.abort(ex)) + } } diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/OffsetTracking.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/OffsetTracking.scala new file mode 100644 index 000000000..ddb1a5585 --- /dev/null +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/eventing/OffsetTracking.scala @@ -0,0 +1,27 @@ +package io.cloudstate.proxy.eventing + +import akka.Done +import akka.persistence.query.{NoOffset, Offset} + +import scala.collection.concurrent.TrieMap +import scala.concurrent.Future + +trait OffsetTracking { + + def acknowledge(consumerId: String, sourceId: String, offset: Offset): Future[Done] + + def loadOffset(consumerId: String, sourceId: String): Future[Offset] + +} + +class InMemoryOffsetTracking extends OffsetTracking { + private val trackingMap = TrieMap.empty[(String, String), Offset] + + override def acknowledge(consumerId: String, sourceId: String, offset: Offset): Future[Done] = { + trackingMap.put((consumerId, sourceId), offset) + Future.successful(Done) + } + + override def loadOffset(consumerId: String, sourceId: String): Future[Offset] = + Future.successful(trackingMap.getOrElse((consumerId, sourceId), NoOffset)) +} diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/EventSourcedEntity.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/EventSourcedEntity.scala index 124832c9e..c8ce8835e 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/EventSourcedEntity.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/EventSourcedEntity.scala @@ -23,6 +23,7 @@ import akka.NotUsed import akka.actor._ import akka.cluster.sharding.ShardRegion import akka.persistence._ +import akka.persistence.journal.Tagged import akka.stream.scaladsl._ import akka.stream.{Materializer, OverflowStrategy} import akka.util.Timeout @@ -115,7 +116,7 @@ object EventSourcedEntity { final case class Configuration( serviceName: String, - userFunctionName: String, + entityTypeName: String, passivationTimeout: Timeout, sendQueueSize: Int ) @@ -146,7 +147,7 @@ final class EventSourcedEntity(configuration: EventSourcedEntity.Configuration, statsCollector: ActorRef) extends PersistentActor with ActorLogging { - override final def persistenceId: String = configuration.userFunctionName + entityId + override final def persistenceId: String = configuration.entityTypeName + "|" + entityId private val actorId = EventSourcedEntity.actorCounter.incrementAndGet() @@ -262,7 +263,7 @@ final class EventSourcedEntity(configuration: EventSourcedEntity.Configuration, } else { reportDatabaseOperationStarted() var eventsLeft = events.size - persistAll(events) { _ => + persistAll(events.map(payload => Tagged(payload, Set(configuration.entityTypeName)))) { _ => eventsLeft -= 1 if (eventsLeft <= 0) { // Remove this hack when switching to Akka Persistence Typed reportDatabaseOperationFinished() diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/EventSourcedSupportFactory.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/EventSourcedSupportFactory.scala index 940abe4fc..d7290f82c 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/EventSourcedSupportFactory.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/EventSourcedSupportFactory.scala @@ -68,9 +68,9 @@ class EventSourcedSupportFactory(system: ActorSystem, val methodsWithoutKeys = methodDescriptors.values.filter(_.keyFieldsCount < 1) if (methodsWithoutKeys.nonEmpty) { val offendingMethods = methodsWithoutKeys.map(_.method.getName).mkString(",") - throw new EntityDiscoveryException( + throw EntityDiscoveryException( s"Event sourced entities do not support methods whose parameters do not have at least one field marked as entity_key, " + - "but ${serviceDescriptor.getFullName} has the following methods without keys: ${offendingMethods}" + s"but ${serviceDescriptor.getFullName} has the following methods without keys: $offendingMethods" ) } } diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/InMemJournal.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/InMemJournal.scala new file mode 100644 index 000000000..a927b4ae9 --- /dev/null +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/eventsourced/InMemJournal.scala @@ -0,0 +1,204 @@ +// This is in the akka package so that we can look up the inmem journal from the read journal +package akka.persistence.cloudstate + +import akka.NotUsed +import akka.actor.{ActorLogging, ActorRef, ExtendedActorSystem} +import akka.persistence.{AtomicWrite, Persistence, PersistentRepr} +import akka.persistence.journal.{AsyncWriteJournal, Tagged} +import akka.persistence.query.scaladsl.{EventsByTagQuery, ReadJournal} +import akka.persistence.query.{EventEnvelope, NoOffset, Offset, ReadJournalProvider, Sequence} +import akka.stream.{ActorMaterializer, Materializer, OverflowStrategy} +import akka.stream.scaladsl.{BroadcastHub, Keep, Source} +import akka.util.Timeout +import com.typesafe.config.Config + +import scala.collection.immutable +import scala.concurrent.Future +import scala.concurrent.duration._ +import scala.util.Try + +object InmemJournal { + case class EventsByTag(tag: String, fromOffset: Long) +} + +private[persistence] class InmemJournal extends AsyncWriteJournal with InmemMessages with ActorLogging { + + import InmemJournal.EventsByTag + + override implicit protected lazy val mat: Materializer = ActorMaterializer() + + override def asyncWriteMessages(messages: immutable.Seq[AtomicWrite]): Future[immutable.Seq[Try[Unit]]] = { + for (w <- messages; p <- w.payload) + add(p) + Future.successful(Nil) // all good + } + + override def asyncReadHighestSequenceNr(persistenceId: String, fromSequenceNr: Long): Future[Long] = + Future.successful(highestSequenceNr(persistenceId)) + + override def asyncReplayMessages(persistenceId: String, fromSequenceNr: Long, toSequenceNr: Long, max: Long)( + recoveryCallback: PersistentRepr => Unit + ): Future[Unit] = { + val highest = highestSequenceNr(persistenceId) + if (highest != 0L && max != 0L) + read(persistenceId, fromSequenceNr, math.min(toSequenceNr, highest), max).foreach(recoveryCallback) + Future.successful(()) + } + + override def asyncDeleteMessagesTo(persistenceId: String, toSequenceNr: Long): Future[Unit] = { + val toSeqNr = math.min(toSequenceNr, highestSequenceNr(persistenceId)) + var snr = 1L + while (snr <= toSeqNr) { + delete(persistenceId, snr) + snr += 1 + } + Future.successful(()) + } + + override def receivePluginInternal: Receive = { + case EventsByTag(tag, fromOffset) => + log.info("Received EventsByTag query for tag {} from offset {}", tag, fromOffset) + sender() ! eventsByTagQuery(tag, fromOffset) + } +} + +class InmemReadJournal(system: ExtendedActorSystem, config: Config) + extends EventsByTagQuery + with ReadJournalProvider + with ReadJournal + with akka.persistence.query.javadsl.ReadJournal { + + private def inmemJournal: ActorRef = Persistence(system).journalFor("inmem-journal") + private implicit val timeout = Timeout(5.seconds) + import akka.pattern.ask + import InmemJournal._ + + override def scaladslReadJournal(): ReadJournal = this + + override def javadslReadJournal(): akka.persistence.query.javadsl.ReadJournal = + // todo this is obviously not right + this + + override def eventsByTag(tag: String, offset: Offset): Source[EventEnvelope, NotUsed] = { + val fromOffset = offset match { + case Sequence(seq) => seq + case NoOffset => 0L + case unsupported => + throw new IllegalArgumentException(s"$unsupported is an unsupported offset type for the in memory read journal") + } + + Source + .fromFutureSource( + (inmemJournal ? EventsByTag(tag, fromOffset)).mapTo[Source[EventEnvelope, NotUsed]] + ) + .mapMaterializedValue(_ => NotUsed) + } +} + +/** + * INTERNAL API. + */ +trait InmemMessages { + private var messages = Map.empty[String, Vector[Message]] + private var allMessages = Vector.empty[Message] + private var offset: Long = 0 + + private val (broadcast, source) = Source + .actorRef[Message](8, OverflowStrategy.fail) + .toMat(BroadcastHub.sink)(Keep.both) + .run() + + protected implicit def mat: Materializer + + def add(p: PersistentRepr): Unit = { + val message = toMessage(p) + + messages = messages + (messages.get(p.persistenceId) match { + case Some(ms) => p.persistenceId -> (ms :+ message) + case None => p.persistenceId -> Vector(message) + }) + + allMessages :+= message + broadcast ! message + } + + def update(pid: String, snr: Long)(f: PersistentRepr => PersistentRepr): Unit = { + messages = messages.get(pid) match { + case Some(ms) => + messages + (pid -> ms.map(sp => if (sp.repr.sequenceNr == snr) sp.copy(repr = f(sp.repr)) else sp)) + case None => messages + } + allMessages = allMessages.map { message => + if (message.repr.persistenceId == pid && message.repr.sequenceNr == snr) { + // todo can tags change? + message.copy(repr = f(message.repr)) + } else { + message + } + } + } + + def delete(pid: String, snr: Long): Unit = { + messages = messages.get(pid) match { + case Some(ms) => + messages + (pid -> ms.filterNot(_.repr.sequenceNr == snr)) + case None => messages + } + allMessages = allMessages.filterNot { message => + message.repr.persistenceId == pid && message.repr.sequenceNr == snr + } + } + + def read(pid: String, fromSnr: Long, toSnr: Long, max: Long): immutable.Seq[PersistentRepr] = + messages.get(pid) match { + case Some(ms) => + ms.view + .dropWhile(_.repr.sequenceNr < fromSnr) + .takeWhile(_.repr.sequenceNr <= toSnr) + .take(safeLongToInt(max)) + .map(_.repr) + .to[immutable.Seq] + case None => Nil + } + + def highestSequenceNr(pid: String): Long = { + val snro = for { + ms <- messages.get(pid) + m <- ms.lastOption + } yield m.repr.sequenceNr + snro.getOrElse(0L) + } + + def eventsByTagQuery(tag: String, fromOffset: Long): Source[EventEnvelope, NotUsed] = + // Technically, there's a race condition that could result in messages being dropped here, if a new message is + // persisted after this returns, but before the subscriber to the query materializes this stream, that message + // will be dropped. But this is only the in memory journal which makes no sense to use in production. + Source(allMessages) + .concat(source) + .dropWhile(_.offset < fromOffset) + .filter(_.tags(tag)) + .map { message => + EventEnvelope(Sequence(message.offset), + message.repr.persistenceId, + message.repr.sequenceNr, + message.repr.payload) + } + + private def safeLongToInt(l: Long): Int = + if (Int.MaxValue < l) Int.MaxValue else l.toInt + + private def toMessage(repr: PersistentRepr): Message = { + offset += 1 + repr.payload match { + case Tagged(payload, tags) => Message(offset, repr.withPayload(payload), tags) + case _ => Message(offset, repr, Set.empty) + } + } + +} + +private case class Message( + offset: Long, + repr: PersistentRepr, + tags: Set[String] +) diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/function/StatelessFunctionSupportFactory.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/function/StatelessFunctionSupportFactory.scala index 7e047070b..2df4900ba 100644 --- a/proxy/core/src/main/scala/io/cloudstate/proxy/function/StatelessFunctionSupportFactory.scala +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/function/StatelessFunctionSupportFactory.scala @@ -5,13 +5,13 @@ import akka.actor.{ActorRef, ActorSystem} import akka.event.Logging import akka.grpc.GrpcClientSettings import akka.stream.Materializer -import akka.stream.scaladsl.{Flow, Keep, Sink, Source} +import akka.stream.scaladsl.{Flow, Source} import akka.util.Timeout -import com.google.protobuf.Descriptors.ServiceDescriptor +import com.google.protobuf.Descriptors.{MethodDescriptor, ServiceDescriptor} import io.cloudstate.protocol.entity.{ClientAction, Entity} import io.cloudstate.proxy._ import io.cloudstate.protocol.function._ -import io.cloudstate.proxy.entity.{EntityCommand, UserFunctionReply} +import io.cloudstate.proxy.entity.{UserFunctionCommand, UserFunctionReply} import scala.concurrent.{ExecutionContext, Future} import scala.collection.JavaConverters._ @@ -21,85 +21,84 @@ class StatelessFunctionSupportFactory(system: ActorSystem, grpcClientSettings: GrpcClientSettings, concurrencyEnforcer: ActorRef, statsCollector: ActorRef)(implicit ec: ExecutionContext, mat: Materializer) - extends EntityTypeSupportFactory { + extends UserFunctionTypeSupportFactory { private final val log = Logging.getLogger(system, this.getClass) private final val statelessFunctionClient = StatelessFunctionClient(grpcClientSettings) - override def buildEntityTypeSupport(entity: Entity, - serviceDescriptor: ServiceDescriptor, - methodDescriptors: Map[String, EntityMethodDescriptor]): EntityTypeSupport = { + override def build(entity: Entity, serviceDescriptor: ServiceDescriptor): UserFunctionTypeSupport = { log.debug("Starting StatelessFunction entity for {}", entity.persistenceId) - validate(serviceDescriptor, methodDescriptors) + val methodDescriptors = serviceDescriptor.getMethods.asScala.map { method => + method.getName -> method + }.toMap new StatelessFunctionSupport(entity.serviceName, + methodDescriptors, statelessFunctionClient, config.proxyParallelism, config.relayTimeout, ec) } - - private[this] final def validate(serviceDescriptor: ServiceDescriptor, - methodDescriptors: Map[String, EntityMethodDescriptor]): Unit = () } private final class StatelessFunctionSupport(serviceName: String, + methodDescriptors: Map[String, MethodDescriptor], statelessFunctionClient: StatelessFunctionClient, parallelism: Int, private implicit val relayTimeout: Timeout, private implicit val ec: ExecutionContext) - extends EntityTypeSupport { - import akka.pattern.ask + extends UserFunctionTypeSupport { + + private def methodDescriptor(name: String): MethodDescriptor = + methodDescriptors.getOrElse(name, throw EntityDiscoveryException(s"Unknown command $name on service $serviceName")) private final val unaryFlow = - Flow[EntityCommand].mapAsync(1)(handleUnary) + Flow[UserFunctionCommand].mapAsync(1)(handleUnary) private final val streamOutFlow = - Flow[EntityCommand] + Flow[UserFunctionCommand] .flatMapConcat( - ec => + ufc => statelessFunctionClient - .handleStreamedOut(entityCommandToFunctionCommand(ec)) + .handleStreamedOut(convertUnaryIn(ufc)) .map(functionReplyToUserFunctionReply) ) - private final val streamInFlow = - Flow - .setup[EntityCommand, UserFunctionReply, NotUsed] { (mat, attr) => - implicit val materializer = mat - - val (outSubscriber, outSource) = Source.asSubscriber[FunctionCommand].preMaterialize() - val outSink = Flow[EntityCommand].map(entityCommandToFunctionCommand).to(Sink.fromSubscriber(outSubscriber)) - - val inSource = - Source.fromFuture(statelessFunctionClient.handleStreamedIn(outSource)).map(functionReplyToUserFunctionReply) - - Flow.fromSinkAndSource(outSink, inSource) - } - .mapMaterializedValue(_ => NotUsed) - - private final val streamedFlow = Flow - .setup[EntityCommand, UserFunctionReply, NotUsed] { (mat, attr) => - implicit val materializer = mat - - val (outSubscriber, outSource) = Source.asSubscriber[FunctionCommand].preMaterialize() - val outSink = Flow[EntityCommand].map(entityCommandToFunctionCommand).to(Sink.fromSubscriber(outSubscriber)) - - val (publisher, inSink) = Sink.asPublisher[FunctionReply](false).preMaterialize() - val inSource = Source.fromPublisher(publisher).map(functionReplyToUserFunctionReply) - - statelessFunctionClient.handleStreamed(outSource).runWith(inSink) - Flow.fromSinkAndSource(outSink, inSource) - } - .mapMaterializedValue(_ => NotUsed) + private def streamInFlow(command: String) = + sourceToSourceToFlow((in: Source[UserFunctionCommand, NotUsed]) => { + Source.fromFuture(statelessFunctionClient.handleStreamedIn(convertStreamIn(command, in))) + }).map(functionReplyToUserFunctionReply) + + private def streamedFlow(command: String) = + sourceToSourceToFlow( + (in: Source[UserFunctionCommand, NotUsed]) => statelessFunctionClient.handleStreamed(convertStreamIn(command, in)) + ).map(functionReplyToUserFunctionReply) + + private def convertStreamIn(command: String, + in: Source[UserFunctionCommand, NotUsed]): Source[FunctionCommand, NotUsed] = + Source + .single( + FunctionCommand( + serviceName = serviceName, + name = command + ) + ) + .concat( + in.map { cmd => + FunctionCommand( + payload = cmd.payload + ) + } + ) - override final def handler(method: EntityMethodDescriptor): Flow[EntityCommand, UserFunctionReply, NotUsed] = { - val streamIn = method.method.isClientStreaming - val streamOut = method.method.isServerStreaming - if (streamIn && streamOut) streamedFlow - else if (streamIn) streamInFlow + override def handler(command: String): Flow[UserFunctionCommand, UserFunctionReply, NotUsed] = { + val method = methodDescriptor(command) + val streamIn = method.isClientStreaming + val streamOut = method.isServerStreaming + if (streamIn && streamOut) streamedFlow(command) + else if (streamIn) streamInFlow(command) else if (streamOut) streamOutFlow else unaryFlow } @@ -118,19 +117,20 @@ private final class StatelessFunctionSupport(serviceName: String, ) } - private def entityCommandToFunctionCommand(command: EntityCommand): FunctionCommand = + private def convertUnaryIn(command: UserFunctionCommand): FunctionCommand = FunctionCommand( serviceName = serviceName, name = command.name, - payload = command.payload + payload = command.payload, + metadata = command.metadata ) - override final def handleUnary(entityCommand: EntityCommand): Future[UserFunctionReply] = - if (entityCommand.streamed) { - throw new IllegalStateException("Request streaming is not yet supported for StatelessFunctions") - } else { - statelessFunctionClient - .handleUnary(entityCommandToFunctionCommand(entityCommand)) - .map(functionReplyToUserFunctionReply) - } + override final def handleUnary(userFunctionCommand: UserFunctionCommand): Future[UserFunctionReply] = + statelessFunctionClient + .handleUnary(convertUnaryIn(userFunctionCommand)) + .map(functionReplyToUserFunctionReply) + + private def sourceToSourceToFlow[In, Out, MOut](f: Source[In, NotUsed] => Source[Out, MOut]): Flow[In, Out, NotUsed] = + Flow[In].prefixAndTail(0).flatMapConcat { case (Nil, in) => f(in) } + } diff --git a/proxy/core/src/main/scala/io/cloudstate/proxy/streams/MergeSequence.scala b/proxy/core/src/main/scala/io/cloudstate/proxy/streams/MergeSequence.scala new file mode 100644 index 000000000..a13cc9907 --- /dev/null +++ b/proxy/core/src/main/scala/io/cloudstate/proxy/streams/MergeSequence.scala @@ -0,0 +1,107 @@ +package io.cloudstate.proxy.streams + +import akka.stream.{Attributes, Inlet, Outlet, UniformFanInShape} +import akka.stream.stage.{GraphStage, GraphStageLogic, InHandler, OutHandler} + +import scala.collection.mutable + +object MergeSequence { + + private case class Pushed[T](in: Inlet[T], sequence: Long, elem: T) + + private implicit def ordering[T]: Ordering[Pushed[T]] = Ordering.by[Pushed[T], Long](_.sequence).reverse +} + +class MergeSequence[T](inputPorts: Int)(extractSequence: T => Long) extends GraphStage[UniformFanInShape[T, T]] { + private val in: IndexedSeq[Inlet[T]] = Vector.tabulate(inputPorts)(i => Inlet[T]("MergeSequence.in" + i)) + private val out: Outlet[T] = Outlet("MergeSequence.out") + override val shape: UniformFanInShape[T, T] = UniformFanInShape(out, in: _*) + + import MergeSequence._ + + override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = + new GraphStageLogic(shape) with OutHandler { + private var nextSequence = 0L + private val available = mutable.PriorityQueue.empty[Pushed[T]] + private var complete = 0 + + setHandler(out, this) + + in.zipWithIndex.foreach { + case (inPort, idx) => + setHandler( + inPort, + new InHandler { + override def onPush(): Unit = { + val elem = grab(inPort) + val sequence = extractSequence(elem) + if (sequence < nextSequence) { + failStage( + new IllegalStateException(s"Sequence regression from $nextSequence to $sequence on port $idx") + ) + } else if (sequence == nextSequence && isAvailable(out)) { + push(out, elem) + tryPull(inPort) + nextSequence += 1 + } else { + available.enqueue(Pushed(inPort, sequence, elem)) + detectMissedSequence() + } + } + + override def onUpstreamFinish(): Unit = { + complete += 1 + if (complete == inputPorts && available.isEmpty) { + completeStage() + } else { + detectMissedSequence() + } + } + } + ) + } + + def onPull(): Unit = + if (available.nonEmpty && available.head.sequence == nextSequence) { + val pushed = available.dequeue() + push(out, pushed.elem) + if (complete == inputPorts && available.isEmpty) { + completeStage() + } else { + if (available.nonEmpty && available.head.sequence == nextSequence) { + failStage( + new IllegalStateException( + s"Duplicate sequence $nextSequence on ports ${pushed.in} and ${available.head.in}" + ) + ) + } + tryPull(pushed.in) + nextSequence += 1 + } + } else { + detectMissedSequence() + } + + private def detectMissedSequence(): Unit = + // Cheap to calculate, but doesn't give the right answer, because there might be input ports + // that are both complete and still have one last buffered element. + if (isAvailable(out) && available.size + complete >= inputPorts) { + // So in the event that this was true we count the number of ports that we have elements buffered for that + // are not yet closed, and add that to the complete ones, to see if we're in a dead lock. + if (available.count(pushed => !isClosed(pushed.in)) + complete == inputPorts) { + failStage( + new IllegalStateException( + s"Expected sequence $nextSequence, but all input ports have pushed or are complete, " + + "but none have pushed the next sequence number. Pushed sequences: " + + available.toVector.map(p => s"${p.in}: ${p.sequence}").mkString(", ") + ) + ) + } + } + + override def preStart(): Unit = + in.foreach(pull) + } + + override def toString: String = s"MergeSequence($inputPorts)" +} diff --git a/proxy/core/src/test/scala/io/cloudstate/proxy/crdt/AbstractCrdtEntitySpec.scala b/proxy/core/src/test/scala/io/cloudstate/proxy/crdt/AbstractCrdtEntitySpec.scala index a8660bcff..8b2bf2b1c 100644 --- a/proxy/core/src/test/scala/io/cloudstate/proxy/crdt/AbstractCrdtEntitySpec.scala +++ b/proxy/core/src/test/scala/io/cloudstate/proxy/crdt/AbstractCrdtEntitySpec.scala @@ -184,7 +184,7 @@ abstract class AbstractCrdtEntitySpec protected def expectCommand(name: String, payload: ProtoAny, streamed: Boolean = false): Long = inside(toUserFunction.expectMsgType[CrdtStreamIn].message) { - case CrdtStreamIn.Message.Command(Command(eid, cid, n, p, s, _)) => + case CrdtStreamIn.Message.Command(Command(eid, cid, n, p, s, m, _)) => eid should ===(entityId) n should ===(name) p shouldBe Some(payload) diff --git a/proxy/core/src/test/scala/io/cloudstate/proxy/streams/MergeSequenceSpec.scala b/proxy/core/src/test/scala/io/cloudstate/proxy/streams/MergeSequenceSpec.scala new file mode 100644 index 000000000..12c1f0f3d --- /dev/null +++ b/proxy/core/src/test/scala/io/cloudstate/proxy/streams/MergeSequenceSpec.scala @@ -0,0 +1,83 @@ +package io.cloudstate.proxy.streams + +import akka.actor.ActorSystem +import akka.stream.{ActorMaterializer, SourceShape} +import akka.stream.scaladsl.{GraphDSL, Sink, Source} +import akka.testkit.TestKit +import org.scalatest.{AsyncWordSpecLike, Matchers} + +import scala.collection.immutable +import scala.concurrent.Future + +class MergeSequenceSpec extends TestKit(ActorSystem("MergeSequenceSpec")) with AsyncWordSpecLike with Matchers { + + private implicit val mat = ActorMaterializer() + + "MergeSequence" should { + "merge interleaved streams" in { + merge( + List(0L, 4L, 8L, 9L, 11L), + List(1L, 3L, 6L, 10L, 13L), + List(2L, 5L, 7L, 12L) + ).map { result => + result should contain theSameElementsInOrderAs (0L to 13L) + } + } + + "merge non interleaved streams" in { + merge( + List(5L, 6L, 7L, 8L, 9L), + List(0L, 1L, 2L, 3L, 4L), + List(10L, 11L, 12L, 13L) + ).map { result => + result should contain theSameElementsInOrderAs (0L to 13L) + } + } + + "fail on duplicate sequence numbers" in recoverToSucceededIf[IllegalStateException] { + merge( + List(0L, 1L, 2L), + List(2L) + ) + } + + "fail on missing sequence numbers" in recoverToSucceededIf[IllegalStateException] { + merge( + List(0L, 4L, 8L, 9L, 11L), + List(1L, 3L, 10L, 13L), + List(2L, 5L, 7L, 12L) + ) + } + + "fail on missing sequence numbers if some streams have completed" in recoverToSucceededIf[IllegalStateException] { + merge( + List(0L, 4L, 8L, 9L, 11L), + List(1L, 3L, 6L, 10L, 13L, 15L), + List(2L, 5L, 7L, 12L) + ) + } + + "fail on sequence in a single stream" in recoverToSucceededIf[IllegalStateException] { + merge( + List(0L, 4L, 8L, 7L, 9L, 11L), + List(1L, 3L, 6L, 10L, 13L), + List(2L, 5L, 7L, 12L) + ) + } + + } + + private def merge(seqs: immutable.Seq[Long]*): Future[immutable.Seq[Long]] = + Source + .fromGraph(GraphDSL.create() { implicit builder => + import GraphDSL.Implicits._ + val merge = builder.add(new MergeSequence[Long](seqs.size)(identity)) + seqs.foreach { seq => + Source(seq) ~> merge + } + + SourceShape(merge.out) + }) + .runWith(Sink.seq) + +} diff --git a/samples/js-shopping-cart-load-generator/src/main/scala/io/cloudstate/loadgenerator/GenerateLoad.scala b/samples/js-shopping-cart-load-generator/src/main/scala/io/cloudstate/loadgenerator/GenerateLoad.scala index b962058dc..5b907c005 100644 --- a/samples/js-shopping-cart-load-generator/src/main/scala/io/cloudstate/loadgenerator/GenerateLoad.scala +++ b/samples/js-shopping-cart-load-generator/src/main/scala/io/cloudstate/loadgenerator/GenerateLoad.scala @@ -15,18 +15,24 @@ import com.google.protobuf.empty.Empty import scala.concurrent.duration._ import scala.util.Random -object GenerateLoad extends App{ - +object GenerateLoad extends App { val system = ActorSystem() - val loadGenerator = system.actorOf(BackoffSupervisor.props(BackoffOpts.onFailure( - childProps = Props[LoadGeneratorActor], - childName = "load-generator", - minBackoff = 3.seconds, - maxBackoff = 30.seconds, - randomFactor = 0.2d - ).withReplyWhileStopped(Done)), "load-generator-supervisor") + val loadGenerator = system.actorOf( + BackoffSupervisor.props( + BackoffOpts + .onFailure( + childProps = Props[LoadGeneratorActor], + childName = "load-generator", + minBackoff = 3.seconds, + maxBackoff = 30.seconds, + randomFactor = 0.2d + ) + .withReplyWhileStopped(Done) + ), + "load-generator-supervisor" + ) CoordinatedShutdown(system).addTask(CoordinatedShutdown.PhaseServiceRequestsDone, "stop-making-requests") { () => import akka.pattern.ask @@ -97,7 +103,8 @@ class LoadGeneratorActor extends Actor with Timers { implicit val ec = context.dispatcher private val clients = { - val settings = GrpcClientSettings.connectToServiceAt(serviceName, servicePort) + val settings = GrpcClientSettings + .connectToServiceAt(serviceName, servicePort) .withTls(false) .withDeadline(1.minute) @@ -119,25 +126,23 @@ class LoadGeneratorActor extends Actor with Timers { override def receive = starting - override def postStop(): Unit = { + override def postStop(): Unit = if (stoppingRef != null) { stoppingRef ! Done } - } - override def preStart(): Unit = { + override def preStart(): Unit = clients.head.getCart(GetShoppingCart("user1")) pipeTo self - } /** - * Let's say that we want to achieve 13 requests per second, with a 250ms tick interval. To do this, we need to make - * 3.25 requests per tick. But you can't make 0.25 requests. If we round this up or down, we're going to end up with - * a requests per second that is greater or less than our desired rate. We could track the rate across ticks, but - * there's no simple way to do that, especially during warmup, and taking back off due to response lag into - * consideration. Instead, we round 3.25 up or down randomly, weighted according to its decimal part, so on average, - * 75% of the time we round down, 25% we round up, and therefore end up with an average of 13 requests a second, as - * desired. - */ + * Let's say that we want to achieve 13 requests per second, with a 250ms tick interval. To do this, we need to make + * 3.25 requests per tick. But you can't make 0.25 requests. If we round this up or down, we're going to end up with + * a requests per second that is greater or less than our desired rate. We could track the rate across ticks, but + * there's no simple way to do that, especially during warmup, and taking back off due to response lag into + * consideration. Instead, we round 3.25 up or down randomly, weighted according to its decimal part, so on average, + * 75% of the time we round down, 25% we round up, and therefore end up with an average of 13 requests a second, as + * desired. + */ private def roundRandomWeighted(d: Double): Int = { val floor = d.floor val remainder = d - floor @@ -217,9 +222,15 @@ class LoadGeneratorActor extends Actor with Timers { val responsesASecond = responsesReceivedSinceLastReport.toDouble / reportInterval * nanos val failuresASecond = failuresSinceLastReport.toDouble / reportInterval * nanos - println("%s Report: %4.0f req/s %4.0f success/s %4.0f failure/s with %d outstanding requests".format( - dateTimeFormatter.format(ZonedDateTime.now()), - requestsASecond, responsesASecond, failuresASecond, outstandingRequests)) + println( + "%s Report: %4.0f req/s %4.0f success/s %4.0f failure/s with %d outstanding requests".format( + dateTimeFormatter.format(ZonedDateTime.now()), + requestsASecond, + responsesASecond, + failuresASecond, + outstandingRequests + ) + ) lastReportNanos = reportTime requestsMadeSinceLastReport = 0 @@ -229,7 +240,7 @@ class LoadGeneratorActor extends Actor with Timers { def stopping: Receive = { case Tick => - // Ignore + // Ignore case _: Cart | _: Empty => responsesReceivedSinceLastReport += 1 @@ -246,4 +257,4 @@ class LoadGeneratorActor extends Actor with Timers { context stop self } } -} \ No newline at end of file +} diff --git a/samples/js-shopping-cart/index.js b/samples/js-shopping-cart/index.js index 009d03d8a..e56d6ddec 100644 --- a/samples/js-shopping-cart/index.js +++ b/samples/js-shopping-cart/index.js @@ -14,4 +14,11 @@ * limitations under the License. */ -require("./shoppingcart.js").start(); +const CloudState = require("cloudstate").CloudState; + +const server = new CloudState(); +server.addEntity(require("./shoppingcart")); +server.addEntity(require("./products")); +server.addEntity(require("./projection")); + +server.start(); \ No newline at end of file diff --git a/samples/js-shopping-cart/package-lock.json b/samples/js-shopping-cart/package-lock.json index 14a7ee15b..2f466ca85 100644 --- a/samples/js-shopping-cart/package-lock.json +++ b/samples/js-shopping-cart/package-lock.json @@ -245,124 +245,6 @@ "unzipper": "^0.9.15" }, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", - "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", - "requires": { - "@babel/types": "^7.5.5", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - } - } - }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "requires": { - "@babel/types": "^7.4.4" - } - }, - "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", - "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==" - }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" - } - }, - "@babel/traverse": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", - "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.5.5", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.5.5", - "@babel/types": "^7.5.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - } - } - }, - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - } - } - }, "@grpc/proto-loader": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.1.0.tgz", @@ -429,28 +311,28 @@ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" }, "@types/bytebuffer": { - "version": "5.0.40", - "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.40.tgz", - "integrity": "sha512-h48dyzZrPMz25K6Q4+NCwWaxwXany2FhQg/ErOcdZS1ZpsaDnDMZg8JYLMTGz7uvXKrcKGJUZJlZObyfgdaN9g==", + "version": "5.0.41", + "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.41.tgz", + "integrity": "sha512-Mdrv4YcaHvpkx25ksqqFaezktx3yZRcd51GZY0rY/9avyaqZdiT/GiWRhfrJhMpgzXqTOSHgGvsumGxJFNiZZA==", "requires": { "@types/long": "*", "@types/node": "*" } }, "@types/lodash": { - "version": "4.14.138", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.138.tgz", - "integrity": "sha512-A4uJgHz4hakwNBdHNPdxOTkYmXNgmUAKLbXZ7PKGslgeV0Mb8P3BlbYfPovExek1qnod4pDfRbxuzcVs3dlFLg==" + "version": "4.14.155", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.155.tgz", + "integrity": "sha512-vEcX7S7aPhsBCivxMwAANQburHBtfN9RdyXFk84IJmu2Z4Hkg1tOFgaslRiEqqvoLtbCBi6ika1EMspE+NZ9Lg==" }, "@types/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", - "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, "@types/node": { - "version": "9.6.51", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.51.tgz", - "integrity": "sha512-5lhC7QM2J3b/+epdwaNfRuG2peN4c9EX+mkd27+SqLKhJSdswHTZvc4aZLBZChi+Wo32+E1DeMZs0fSpu/uBXQ==" + "version": "9.6.56", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.56.tgz", + "integrity": "sha512-Cq4pUCsBL6H7X0HFAOap75lmQqcnSmUDSP0R03lz9UsxRvBu5QsnKLLgIoitlFBX+j6LmciWYQAbOSmGMi7vwA==" }, "abbrev": { "version": "1.1.1", @@ -458,136 +340,35 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", - "requires": { - "string-width": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==" - }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, - "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==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "requires": { - "sprintf-js": "~1.0.2" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, "ascli": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", @@ -610,96 +391,26 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -709,9 +420,9 @@ } }, "big-integer": { - "version": "1.6.44", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.44.tgz", - "integrity": "sha512-7MzElZPTyJ2fNvBkPxtFQ2fWIkVmuzw41+BZHSzpEq3ymB2MfeKp1+yXl/tS75xCx+WnyV+yb0kp+K1C3UNwmQ==" + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" }, "binary": { "version": "0.3.0", @@ -722,64 +433,11 @@ "chainsaw": "~0.1.0" } }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" - }, "bluebird": { "version": "3.4.7", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=" }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -789,38 +447,6 @@ "concat-map": "0.0.1" } }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "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==" - }, "buffer-indexof-polyfill": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz", @@ -846,68 +472,16 @@ } } }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", - "requires": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" - } - }, "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==" - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, - "catharsis": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", - "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", - "requires": { - "lodash": "^4.17.14" - } - }, - "chai": { - "version": "4.2.0", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "requires": { - "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" - } - }, "chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", @@ -916,80 +490,10 @@ "traverse": ">=0.3.0 <0.4" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "cliui": { "version": "3.2.0", @@ -1006,28 +510,6 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "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==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, "colour": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", @@ -1041,107 +523,21 @@ "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "optional": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - } - } - }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==" - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "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=" }, - "cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", - "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" - } - }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -1163,137 +559,25 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "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==", - "requires": { - "type-detect": "^4.0.0" - } - }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "requires": { - "strip-bom": "^3.0.0" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" - }, - "dom-serializer": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", - "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" - }, - "entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" - } - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "requires": { - "is-obj": "^1.0.0" - } + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, "duplexer2": { "version": "0.1.4", @@ -1303,11 +587,6 @@ "readable-stream": "^2.0.2" } }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1317,303 +596,25 @@ "safer-buffer": "^2.1.0" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "^1.4.0" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "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=" - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "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==" }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-cache-dir": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "requires": { - "is-buffer": "~2.0.3" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", - "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - } - } + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "forever-agent": { "version": "0.6.1", @@ -1630,12 +631,12 @@ "mime-types": "^2.1.12" } }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "requires": { - "map-cache": "^0.2.2" + "minipass": "^2.6.0" } }, "fs.realpath": { @@ -1643,553 +644,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "optional": true - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true - }, - "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=", - "optional": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", - "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "optional": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "optional": true, - "requires": { - "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" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "optional": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "optional": true - }, - "minipass": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", - "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", - "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "optional": true - }, - "needle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz", - "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", - "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", - "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "optional": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "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=", - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "optional": true - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "optional": true, - "requires": { - "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" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "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==", - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "optional": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "optional": true - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "optional": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "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==", - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "optional": true - }, - "tar": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", - "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "optional": true - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "optional": true - } - } - }, "fstream": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", @@ -2201,34 +655,21 @@ "rimraf": "2" } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "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==" - }, - "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=" - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "requires": { - "pump": "^3.0.0" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -2238,9 +679,9 @@ } }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2250,729 +691,91 @@ "path-is-absolute": "^1.0.0" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "google-protobuf": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.12.2.tgz", + "integrity": "sha512-4CZhpuRr1d6HjlyrxoXoocoGFnRYgKULgMtikMddA9ztRyYR59Aondv2FioyxWVamRo0rF2XpYawkTCBEQOSkA==" + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "grpc": { + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.24.3.tgz", + "integrity": "sha512-EDemzuZTfhM0hgrXqC4PtR76O3t+hTIYJYR5vgiW0yt2WJqo4mhxUqZUirzUQz34Psz7dbLp38C6Cl7Ij2vXRQ==", "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "@types/bytebuffer": "^5.0.40", + "lodash.camelcase": "^4.3.0", + "lodash.clone": "^4.5.0", + "nan": "^2.13.2", + "node-pre-gyp": "^0.15.0", + "protobufjs": "^5.0.3" }, "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "protobufjs": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", + "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", "requires": { - "is-extglob": "^2.1.0" + "ascli": "~1", + "bytebuffer": "~5", + "glob": "^7.0.5", + "yargs": "^3.10.0" } } } }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { - "ini": "^1.3.4" + "ajv": "^6.5.5", + "har-schema": "^2.0.0" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, - "google-protobuf": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.9.1.tgz", - "integrity": "sha512-tkz7SVwBktFbqFK3teXFUY/VM57+mbUgV9bSD+sZH1ocHJ7uk7BfEWMRdU24dd0ciUDokreA7ghH2fYFIczQdw==" - }, - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - } + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, - "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "requires": { + "minimatch": "^3.0.4" + } }, - "grpc": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.23.3.tgz", - "integrity": "sha512-7vdzxPw9s5UYch4aUn4hyM5tMaouaxUUkwkgJlwbR4AXMxiYZJOv19N2ps2eKiuUbJovo5fnGF9hg/X91gWYjw==", - "requires": { - "@types/bytebuffer": "^5.0.40", - "lodash.camelcase": "^4.3.0", - "lodash.clone": "^4.5.0", - "nan": "^2.13.2", - "node-pre-gyp": "^0.13.0", - "protobufjs": "^5.0.3" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.2", - "resolved": "", - "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "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=" - }, - "debug": { - "version": "3.2.6", - "resolved": "", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, - "fs-minipass": { - "version": "1.2.6", - "resolved": "", - "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==", - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.4", - "resolved": "", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "requires": { - "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" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "minipass": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", - "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", - "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "needle": { - "version": "2.4.0", - "resolved": "", - "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz", - "integrity": "sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==" - }, - "npm-packlist": { - "version": "1.4.4", - "resolved": "", - "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "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=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "protobufjs": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", - "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", - "requires": { - "ascli": "~1", - "bytebuffer": "~5", - "glob": "^7.0.5", - "yargs": "^3.10.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "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" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "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==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "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==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "tar": { - "version": "4.4.10", - "resolved": "", - "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.5", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" - } - } - }, - "handlebars": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", - "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", - "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", - "requires": { - "is-stream": "^1.0.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "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==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -2988,128 +791,11 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, - "ink-docstrap": { - "version": "1.3.2", - "integrity": "sha512-STx5orGQU1gfrkoI/fMU7lX6CSP7LBGO10gXNgOZhwKhUqbtNjCkYSewJtNnLmWP1tAGN6oyEpG1HFPw5vpa5Q==", - "requires": { - "moment": "^2.14.1", - "sanitize-html": "^1.13.0" - } - }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" - }, - "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", - "requires": { - "ci-info": "^1.5.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -3118,288 +804,26 @@ "number-is-nan": "^1.0.0" } }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - } - }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "requires": { - "has-symbols": "^1.0.0" - } - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, - "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==" - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==" - }, - "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "istanbul-reports": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", - "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", - "requires": { - "handlebars": "^4.1.2" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "js2xmlparser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.0.tgz", - "integrity": "sha512-WuNgdZOXVmBk5kUPMcTcVUpbGRzLfNkv7+7APq7WiDihpXVKrgxo6wwRpRl9OQeEBgKCVk9mR7RbzrnNWC8oBw==", - "requires": { - "xmlcreate": "^2.0.0" - } - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, - "jsdoc": { - "version": "3.6.3", - "integrity": "sha512-Yf1ZKA3r9nvtMWHO1kEuMZTlHOF8uoQ0vyo5eH7SQy5YeIiHM+B0DgKnn+X6y6KDYZcF7G2SPkKF+JORCXWE/A==", - "requires": { - "@babel/parser": "^7.4.4", - "bluebird": "^3.5.4", - "catharsis": "^0.8.11", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.0", - "klaw": "^3.0.0", - "markdown-it": "^8.4.2", - "markdown-it-anchor": "^5.0.2", - "marked": "^0.7.0", - "mkdirp": "^0.5.1", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.0.1", - "taffydb": "2.6.2", - "underscore": "~1.9.1" - }, - "dependencies": { - "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" - }, - "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==" - } - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -3439,27 +863,6 @@ "verror": "1.10.0" } }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, - "klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "requires": { - "graceful-fs": "^4.1.9" - } - }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", - "requires": { - "package-json": "^4.0.0" - } - }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", @@ -3468,46 +871,11 @@ "invert-kv": "^1.0.0" } }, - "linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", - "requires": { - "uc.micro": "^1.0.1" - } - }, "listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=" }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - } - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", @@ -3523,183 +891,24 @@ "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "requires": { - "chalk": "^2.0.1" - } - }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } - }, - "markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", - "requires": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - }, - "markdown-it-anchor": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.4.tgz", - "integrity": "sha512-n8zCGjxA3T+Mx1pG8HEgbJbkB8JFUuRkeTZQuIM8iPY6oQ8sWOPRZJDFC9a/pNg2QkHEjjGkhBEl/RSyzaDZ3A==" - }, - "marked": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.44.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -3709,256 +918,53 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" } }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "requires": { - "minimist": "0.0.8" + "minipass": "^2.9.0" } }, - "mocha": { - "version": "6.1.4", - "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "2.2.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "node-environment-flags": "1.0.5", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.2.2", - "yargs-parser": "13.0.0", - "yargs-unparser": "1.5.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - } - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "requires": { - "invert-kv": "^2.0.0" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" - }, - "yargs": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", - "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", - "requires": { - "cliui": "^4.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.0.0" - } - } + "minimist": "^1.2.5" } }, - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" - }, - "nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node-environment-flags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", - "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "nodemon": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.1.tgz", - "integrity": "sha512-/DXLzd/GhiaDXXbGId5BzxP1GlsqtMGM9zTmkWrgXtSqjKmGSbLicM/oAy4FR0YWm14jCHRwnR31AHS2dYFHrg==", + "needle": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz", + "integrity": "sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==", "requires": { - "chokidar": "^2.1.5", - "debug": "^3.1.0", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.6", - "semver": "^5.5.0", - "supports-color": "^5.2.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" }, "dependencies": { "debug": { @@ -3968,55 +974,67 @@ "requires": { "ms": "^2.1.1" } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } } } }, + "node-pre-gyp": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz", + "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.3", + "needle": "^2.5.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "requires": { - "abbrev": "1" + "abbrev": "1", + "osenv": "^0.1.4" } }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "npm-normalize-package-bin": "^1.0.1" } }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "requires": { - "path-key": "^2.0.0" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, - "npm-watch": { - "version": "0.6.0", - "integrity": "sha512-qt3jZd8gKX45m5czKv/CsxzWDMgblu/meL5PefeoViq07e8l7+DBNC8RgHAI0DfA+jJq2n/pJLaNL4yfHR+0qw==", + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { - "nodemon": "^1.18.7", - "through2": "^2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -4024,219 +1042,22 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, - "nyc": { - "version": "14.1.1", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", - "requires": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", - "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" - }, - "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.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": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - }, - "dependencies": { - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - } - } - }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - } + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "wrappy": "1" } }, "optjs": { @@ -4257,190 +1078,39 @@ "lcid": "^1.0.0" } }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "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==" - }, - "package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - } + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, "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=" }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - } - } - }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "requires": { - "find-up": "^3.0.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - }, - "postcss": { - "version": "7.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", - "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" - }, "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==" }, "protobufjs": { - "version": "6.8.8", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", - "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.9.0.tgz", + "integrity": "sha512-LlGVfEWDXoI/STstRDdZZKb/qusoAWUnmLg9R8OLSO473mBLWHowx8clbX5/+mKDEI+v7GzjoK9tRPZMMcoTrg==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -4452,41 +1122,22 @@ "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.0", - "@types/node": "^10.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", "long": "^4.0.0" }, "dependencies": { "@types/node": { - "version": "10.14.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.17.tgz", - "integrity": "sha512-p/sGgiPaathCfOtqu2fx5Mu1bcjuP8ALFg4xpGgNkcin7LwRyzUKniEHBKdcE1RPsenq5JVPIpMTJSygLboygQ==" + "version": "13.13.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.12.tgz", + "integrity": "sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw==" } } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, "psl": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.3.1.tgz", - "integrity": "sha512-2KLd5fKOdAfShtY2d/8XDWVRnmp3zp40Qt6ge2zBPFARLXOGUf2fHD5eg+TV/5oxBtQKVhjUaKFsAaE4HnwfSA==" - }, - "pstree.remy": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", - "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" }, "punycode": { "version": "2.1.1", @@ -4504,489 +1155,94 @@ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "requires": { "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "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" - }, - "dependencies": { - "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==" - } - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "requires": { - "rc": "^1.0.1" - } - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "requires": { - "es6-error": "^4.0.1" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "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==" - }, - "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", - "requires": { - "lodash": "^4.17.14" - } - }, - "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", - "requires": { - "path-parse": "^1.0.6" - } - }, - "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==" - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sanitize-html": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz", - "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==", - "requires": { - "chalk": "^2.4.1", - "htmlparser2": "^3.10.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.mergewith": "^4.6.1", - "postcss": "^7.0.5", - "srcset": "^1.0.0", - "xtend": "^4.0.1" - } - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" - }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "requires": { - "semver": "^5.0.3" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" } }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "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==", "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "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" } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" - }, - "spawn-wrap": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", - "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" } }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "glob": "^7.1.3" } }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + "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==" }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "requires": { - "extend-shallow": "^3.0.0" - } + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "srcset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", - "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", - "requires": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" - } + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "sshpk": { "version": "1.16.1", @@ -5004,25 +1260,6 @@ "tweetnacl": "~0.14.0" } }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -5039,13 +1276,6 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" - }, - "dependencies": { - "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==" - } } }, "strip-ansi": { @@ -5056,168 +1286,32 @@ "ansi-regex": "^2.0.0" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=" - }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "requires": { - "execa": "^0.7.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - } - } - }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" - }, - "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=" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "requires": { - "nopt": "~1.0.10" + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" } }, "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } + "psl": "^1.1.28", + "punycode": "^2.1.1" } }, "traverse": { @@ -5225,11 +1319,6 @@ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -5243,122 +1332,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "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==" - }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" - }, - "uglify-js": { - "version": "3.6.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz", - "integrity": "sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw==", - "optional": true, - "requires": { - "commander": "~2.20.3", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true - } - } - }, - "undefsafe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", - "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", - "requires": { - "debug": "^2.2.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - } - } - }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" - }, "unzipper": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.9.15.tgz", @@ -5375,28 +1348,6 @@ "setimmediate": "~1.0.4" } }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" - }, - "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -5405,42 +1356,15 @@ "punycode": "^2.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "requires": { - "prepend-http": "^1.0.1" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "verror": { "version": "1.10.0", @@ -5452,19 +1376,6 @@ "extsprintf": "^1.2.0" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -5473,53 +1384,11 @@ "string-width": "^1.0.2 || 2" } }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "requires": { - "string-width": "^2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "window-size": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" - }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -5534,40 +1403,15 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" - }, - "xmlcreate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.1.tgz", - "integrity": "sha512-MjGsXhKG8YjTKrDCXseFo3ClbMGvUD4en29H2Cev1dv4P/chlpw6KdYmlCWDkhosBVKRDjM836+3e3pm1cBNJA==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" }, "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yargs": { "version": "3.32.0", @@ -5582,137 +1426,6 @@ "window-size": "^0.1.4", "y18n": "^3.2.0" } - }, - "yargs-parser": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", - "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } - } - }, - "yargs-unparser": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", - "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.11", - "yargs": "^12.0.5" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "requires": { - "invert-kv": "^2.0.0" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } } } }, diff --git a/samples/js-shopping-cart/package.json b/samples/js-shopping-cart/package.json index 41fcfd502..ab7bee833 100644 --- a/samples/js-shopping-cart/package.json +++ b/samples/js-shopping-cart/package.json @@ -39,10 +39,10 @@ }, "scripts": { "test": "mocha", - "prestart": "compile-descriptor ../../protocols/example/shoppingcart/shoppingcart.proto", - "pretest": "compile-descriptor ../../protocols/example/shoppingcart/shoppingcart.proto", - "postinstall": "compile-descriptor ../../protocols/example/shoppingcart/shoppingcart.proto", "prepare-node-support": "cd ../../node-support && npm install && cd ../samples/js-shopping-cart", + "prestart": "compile-descriptor -I../../protocols/example ../../protocols/example/shoppingcart/shoppingcart.proto ../../protocols/example/shoppingcart/projection.proto ../../protocols/example/shoppingcart/products.proto", + "pretest": "compile-descriptor -I../../protocols/example ../../protocols/example/shoppingcart/shoppingcart.proto ../../protocols/example/shoppingcart/projection.proto ../../protocols/example/shoppingcart/products.proto", + "postinstall": "compile-descriptor -I../../protocols/example shoppingcart/shoppingcart.proto shoppingcart/projection.proto shoppingcart/products.proto", "start": "node index.js", "start-no-prestart": "node index.js", "dockerbuild": "npm run prepare-node-support && docker build -f ../../Dockerfile.js-shopping-cart -t ${DOCKER_PUBLISH_TO:-cloudstateio}/js-shopping-cart:latest ../..", diff --git a/samples/js-shopping-cart/products.js b/samples/js-shopping-cart/products.js new file mode 100644 index 000000000..ccf418395 --- /dev/null +++ b/samples/js-shopping-cart/products.js @@ -0,0 +1,70 @@ +/* + * Copyright 2020 Lightbend Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +const crdt = require("cloudstate").crdt; + +const entity = new crdt.Crdt( + "shoppingcart/products.proto", + "com.example.shoppingcart.ShoppingCartProducts", + { + includeDirs: ["../../protocols/example"] + } +); + +entity.commandHandlers = { + UpdateCartQuantity: updateCartQuantity, + RemoveProductFromCart: removeProductFromCart, + GetProduct: getProduct, +}; + +// The default value is an ORMap, whose default values are GCounters, so essentially, this is a map of user ids to +// the quantity of items that that user has in their cart. +// There are some problems with using this though, if a user removes an item from their cart, and then immediately +// adds it back, then it's possible when that's replicated that the old counter value will survive, this is due to how +// ORMap works. +entity.defaultValue = () => new crdt.ORMap(); + +entity.onStateSet = map => { + map.defaultValue = () => new crdt.GCounter(); +}; + +function updateCartQuantity(request, ctx) { + console.log("Product entity received update cart quantity for product " + request.productId); + ctx.state.get(request.userId).increment(request.quantity); + return {}; +} + +function removeProductFromCart(request, ctx) { + console.log("Product entity received remove product from cart for product " + request.productId); + ctx.state.delete(request.userId); + return {}; +} + +function getProduct(request, ctx) { + let totalQuantity = 0; + for (const cart of ctx.state.values()) { + totalQuantity += cart.value; + } + return { + totalQuantities: totalQuantity, + totalCarts: ctx.state.size + } +} + + +// Export the entity +module.exports = entity; diff --git a/samples/js-shopping-cart/projection.js b/samples/js-shopping-cart/projection.js new file mode 100644 index 000000000..832d3fbfd --- /dev/null +++ b/samples/js-shopping-cart/projection.js @@ -0,0 +1,54 @@ +/* + * Copyright 2020 Lightbend Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const Stateless = require("cloudstate").Stateless; + +const projection = new Stateless( + ["shoppingcart/projection.proto", "shoppingcart/products.proto"], + "com.example.shoppingcart.ShoppingCartProjection", + { + includeDirs: ["../../protocols/example"] + } +); + +const products = projection.root.lookupService("com.example.shoppingcart.ShoppingCartProducts").methods; + +projection.commandHandlers = { + HandleItemAdded: handleItemAdded, + HandleItemRemoved: handleItemRemoved, +}; + +function handleItemAdded(itemAdded, ctx) { + console.log("Projection received itemAdded event for user " + + ctx.cloudevent.subject + " and product " + itemAdded.item.productId); + ctx.forward(products.UpdateCartQuantity, { + productId: itemAdded.item.productId, + userId: ctx.cloudevent.subject, + quantity: itemAdded.item.quantity, + }); +} + +function handleItemRemoved(itemRemoved, ctx) { + console.log("Projection received itemRemoved event for user " + + ctx.cloudevent.subject + " and product " + itemRemoved.productId); + ctx.forward(products.RemoveProductFromCart, { + productId: itemRemoved.productId, + userId: ctx.cloudevent.subject, + }); +} + +// Export the entity +module.exports = projection; diff --git a/samples/js-shopping-cart/user-function.desc b/samples/js-shopping-cart/user-function.desc index 1e48bd4e4b2545450e00ec04704a5f22cd503f49..c2dbc26a20b99c4ecf78246b1099456b3a13e333 100644 GIT binary patch delta 1769 zcmb_cJ8u&~5XL@i>@j(*APL5a;SwBMfwKq_2&5okheuF&4^5qp~uyrg8>*9IrwxV(X$%^&u64KzpFqAhze%vD;Xxy2N)R zX;SZLg-?ng8iW`nj%7%qwbTPCv(xyIdGNA9$I7taqC@4%f-3xU|S-a$m+CLF$-k z&RI5|rMQkVu*LVVyJVToc}mJknH`z9nmYr%IUV%iY-y|Pc%Wm>$%sgj8#| z`4%_*TiWruLm#T!K9&cdFWuAXc%SedqA<__Z^^h{z&BRI2ibokD51rEYTPZhSOsZY zz~wmX3@3_#FFesZyO6uoP$}%V=$QQD5HC0HSNQKjAL{s?&({m70Ktu}J&A-K9SwCB zN&GQMJudcY)Yy%S-l1sIc)>;cMwiJ;gY)O;D0cyhhTv3Wq`aPVq>Ko z9!9+gTY_t-0R5EEN^L0^!otGOk$g<=lkq?+9!6P6HA{u~p8Nr0I>?Zi$(vegPTiWz) Lfj$-lH!|cevUWr) delta 301 zcmcbV{61tuq#Wb@$x(8?jCPY($muX&YGa;!O)iD`Gb7jJ`RY=W!{zn$b+|ZO%Tn`7 zGV{`f__&xd^CTD*7&SP9fGi;aF6R8w5(y@-0ElT7G>kz0ih5F$Z)vJ9 a>QDZ!slq*Ls{}h2YbMaC#haD2j2Ho&FIe#a