diff --git a/.gitignore b/.gitignore index 884a4d8..b017598 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules out +.system +*.tgz \ No newline at end of file diff --git a/.npmignore b/.npmignore index ff53e28..76e8274 100644 --- a/.npmignore +++ b/.npmignore @@ -2,4 +2,6 @@ node_modules src test .editorconfig -tsconfig.json \ No newline at end of file +tsconfig.json +.system +*.tgz \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index ba8e41f..bc37bc4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,5 @@ node_js: os: - windows - linux - - osx \ No newline at end of file + - osx +env: YARN_GPG=no \ No newline at end of file diff --git a/package.json b/package.json index 3eb38b9..453cd61 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "scripts": { "test": "node --enable-source-maps --unhandled-rejections=strict out/run.js test", "debug": "cross-env DEBUG=vogue:* yarn test", - "debug:watch": "cross-env DEBUG=vogue:* supervisor -w out,test,lib -n exit --exec yarn -- test", + "debug:watch": "cross-env DEBUG=vogue:* supervisor -w out,test/**/*.v,lib -n exit --exec yarn -- test", "postinstall": "yarn compile && cd test && yarn", "postcompile:watch": "echo DONE", "compile": "tsc", @@ -18,12 +18,15 @@ }, "dependencies": { "@types/debug": "^4.1.5", + "@types/fs-extra": "^9.0.11", "@types/lodash": "^4.14.169", "@types/nearley": "^2.11.1", "@types/node": "^15.3.0", "@types/uglify-js": "^3.13.0", + "@types/uuid": "^8.3.0", "cross-env": "^7.0.3", "debug": "^4.3.1", + "fs-extra": "^10.0.0", "lodash": "^4.17.21", "moo": "^0.5.1", "nearley": "^2.20.1", @@ -31,6 +34,7 @@ "supervisor": "^0.12.0", "typescript": "^4.2.4", "uglify-js": "^3.13.5", + "uuid": "^8.3.2", "yarn": "^1.22.10" } } diff --git a/src/Instance.ts b/src/Instance.ts index b9b5cb4..9ce88dd 100644 --- a/src/Instance.ts +++ b/src/Instance.ts @@ -1,96 +1,196 @@ - -import Serializable from './Serializable.js'; -import minify from './minify.js'; import debug from 'debug'; import _ from 'lodash'; const log = debug('vogue:instance'); import vm from 'vm'; -import Module, { Link } from './Module.js'; +import Module, { LinkDescription, Variable } from './Module.js'; import System from './System.js'; -import { KV } from './KV.js'; +import * as uuid from 'uuid'; /** * @typedef {import('./System.js').default} System * @typedef {import('./Module.js').default} Module */ +export type Link = any; // BUT PROXY + +export type SerializedInstance = { + type: string, + links: { + [name: string]: string + }, + members: { + [name: string]: any // SO LONG AS ITS SERIALIZABLE ._.* + }, + id: string +} -export default class Instance extends Serializable { +export default class Instance { module: Module; links = {} system: System; - context: vm.Context; + context: vm.Context | null = null; locals = []; internalFunctions = {}; _link: Instance; location: string; + _id: string; + initialContext: any = {}; + + get currentContext(): any { + return this.context ?? this.initialContext; + } + + sync() { + this.system.saveInstance(this); + } createContext(): vm.Context { if(this.context) return this.context; - const initialContext: KV = {}; + + const initialContext: any = {}; // system globals! // TODO turn this into its own vogue module! system.create/instance.create // TODO request context from system... - initialContext.create = this.system.newInstance.bind(this.system); + initialContext.create = this.system.newLink.bind(this.system); initialContext.process = process; - for(const name in this.system.staticInstances) - initialContext[name] = this.system.staticInstances[name]; + for(const name in this.system.staticLinks) { + log('creating context with static link: ' + name); + initialContext[name] = this.system.staticLinks[name]; + } // local links! // optional arrays // TODO maybe make these property accessors to allow for some automation - for(const link of this.module.links.filter((v: Link) => v.array && !v.required)) + for(const link of this.module.links.filter((v: LinkDescription) => v.array && !v.required)) initialContext[link.name] = []; - for(const link of this.module.links.filter((v: Link) => !v.array && !v.required)) + for(const link of this.module.links.filter((v: LinkDescription) => !v.array && !v.required)) initialContext[link.name] = null; for(const variable of this.module.variables) - initialContext[variable.name] = null; + attachHookedProperty(initialContext, variable.name, null, this.sync.bind(this)) + for(const name in this.initialContext) + initialContext[name] = this.initialContext[name] for(const name in this.module.imports) initialContext[name] = this.module.imports[name]; + // instance defined functions + initialContext.sync = this.system.saveInstance.bind(this.system, this); + const context = vm.createContext(initialContext); + // user defined functions for(const name in this.module.functions) { const { code, parameters, async } = this.module.functions[name]; - const injectedScript = -` -var ${name} = ${async ? 'async' : ''} function ${name}(${parameters.join(', ')}) ${code} -`; - vm.runInContext(injectedScript, context, { - - }); + const injectedScript = ` + var ${name} = ${async ? 'async ' : ''}function ${name}(${parameters.join(', ')}) ${code} + ${name} = ${name}.bind(this);`.trim(); + // log('injecting function...') + // log(injectedScript) + vm.runInContext(injectedScript, context, {}); } + log('context created! ' + Object.keys(context)); + // log(context); + return context; }; - constructor(module: Module, location: string, parameters: {[name: string]: any}, system: System) { - super(); + setMember(name: string, value: any) { + log('setMember: ' + this.toString() + '.' + name + ' => ' + value); + this.currentContext[name] = value; + } + + setLink(name: string, value: Link) { + log('setLink: ' + this.toString() + '.' + name + ' => ' + value.__link__); + this.currentContext[name] = value; + } + + constructor( + module: Module, + location: string, + parameters: {[name: string]: any}, + system: System, + options?: { + id?: string + } + ) { this.module = module; this.location = location; this.system = system; - this.context = this.createContext(); + // this.context = this.createContext(); + this._id = options?.id ?? uuid.v4(); this._link = new Proxy(this, { get(target: Instance, prop: string, receiver) { - log(`getting ${target.module.name.full}.${prop}: (${target.module.identifiers[prop]}|${typeof target.context[prop]})`); + log(`getting ${target.module.name.full}.${prop.toString()}: (${target.module.identifiers[prop]}|${typeof target.context?.[prop]})`); + + if(target.context === null) + target.restore(); + const DNEText = `${target.module.name.full}.${prop.toString()} either does not exist, or is not accessible`; if(prop === 'restore') throw new Error(DNEText); + if(prop === '__link__') return target._id; if(prop in target.module.functions) { - return target.context[prop]; + return target.context?.[prop]; } throw new Error(DNEText); } }); + log('created ' + this); } restore() { - return this.context.restore?.(); + if(this.context === null) + this.context = this.createContext(); + + return this.context?.restore?.(); } get link () { return this._link; } + + toSerializableObject(): SerializedInstance { + const obj: any = {}; + obj.type = this.module.name.full; + obj.links = Object.fromEntries(this.module.links.map((link: LinkDescription): [string, string] => { + const name = link.name; + const linkId = this.context?.[name]?.__link__; + return [name, linkId]; + })); + obj.members = Object.fromEntries( + this.module.variables + .filter((member: Variable): boolean => { + return member.persist; + }) + .map((member: Variable): [string, any] => { + const name = member.name; + const value = this.context?.[name]; + return [name, value]; + }) + ); + obj.id = this._id; + + return obj as SerializedInstance; + } + + toString() { + return this.module.name.full + '(' + this._id.substr(0, 4) + ')'; + } +} + +function attachHookedProperty(target: any, name: string, initialValue: any, changedHook: () => void) { + const propId = uuid.v4(); + target[propId] = initialValue; + // TODO if its an object, replace it with a dead simple proxy? for detecting internal changes... + Object.defineProperty(target, name, { + get() { + return target[propId]; + }, + set(value) { + target[propId] = value; + changedHook(); + } + }) } \ No newline at end of file diff --git a/src/KV.ts b/src/KV.ts deleted file mode 100644 index 795fc4d..0000000 --- a/src/KV.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type KV = { - [key: string]: any -}; \ No newline at end of file diff --git a/src/Module.ts b/src/Module.ts index 5e9f3a7..c36c14e 100644 --- a/src/Module.ts +++ b/src/Module.ts @@ -14,7 +14,7 @@ import { createRequire } from 'module'; import { pathToFileURL } from 'url'; const log = debug('vogue:module'); -export type Link = { +export type LinkDescription = { name: string, array: boolean, required: boolean @@ -26,7 +26,7 @@ export type Variable = { } export default class Module { - links: Link[] = []; + links: LinkDescription[] = []; globals = []; functions: { [name: string]: { diff --git a/src/Serializable.ts b/src/Serializable.ts deleted file mode 100644 index c410650..0000000 --- a/src/Serializable.ts +++ /dev/null @@ -1,198 +0,0 @@ -// import { Ubjson } from '@shelacek/ubjson'; -import { existsSync, readFileSync, writeFileSync } from 'fs'; -import { KV } from './KV'; - -export default class Serializable { - - constructor(...args: any[]) {} - - // things that need to be stored only in cold - // storage are keyed with a special prefix - static CLASS_REFERENCE = '$$CLASS_NAME'; - - // things that need to be stored only at runtime - // are keyed with symbols to not interfere with - // user code. - static PERSIST_LOCATION = Symbol('PERSIST_LOCATION'); - - start() {} - - // toUbj() { - // return Ubjson.encode(this.toSerializableObject()); - // } - - // static fromUbj(buffer) { - // return this.fromSerializableObject(Ubjson.decode(buffer)); - // } - - toJson() { - return JSON.stringify(this.toSerializableObject(), null, 2); - } - - static serializationDependencies(): any[] { - return []; - } - - static fromJson(str: string) { - return this.fromSerializableObject(JSON.parse(str)); - } - - toSerializableObject() { - - const transformValue = (val: any): any => { - if(Array.isArray(val)) { - return transformArray(val); - } else if (val === null || val === undefined) { - return val; - } else if(typeof val === 'object') { - return transformObject(val); - } else { - return val; - } - } - - const transformObject = (obj: KV): KV => { - const clone: KV = {}; - for(const prop of Object.keys(obj)) { - if(prop.startsWith('_')) continue; - - clone[prop] = transformValue(obj[prop]); - } - if(obj instanceof Serializable) { - clone[Serializable.CLASS_REFERENCE] = obj.constructor.name; - } - return clone; - } - - const transformArray = (arr: any[]): any[] => { - const clone = []; - for(const item of arr) { - clone.push(transformValue(item)); - } - return clone; - } - - return transformObject(this); - } - - static fromSerializableObject(obj: KV) { - if(obj[Serializable.CLASS_REFERENCE] !== this.name) return null; - - const transformValue = (val: any): any => { - if(Array.isArray(val)) { - return transformArray(val); - } else if(val === null || val === undefined) { - return val; - } else if(typeof val === 'object') { - if(Serializable.CLASS_REFERENCE in val) { - const classes = this.serializationDependencies(); - const matchingClasses = classes.filter((classObject) => { - classObject.name === val[Serializable.CLASS_REFERENCE] - }); - if(matchingClasses.length === 1) { - return matchingClasses[0].fromSerializableObject(val); - } else { - return transformObject(val); - } - } - return transformObject(val); - } else { - return val; - } - } - - const transformObject = (obj: KV): KV => { - const clone: KV = {}; - for(const prop of Object.keys(obj)) { - if(prop.startsWith('_')) continue; - - clone[prop] = transformValue(obj[prop]); - } - return clone; - } - - const transformArray = (arr: any[]): any[] => { - const clone = []; - for(const item of arr) { - clone.push(transformValue(item)); - } - return clone; - } - - const clone = transformObject(obj); - if(Serializable.CLASS_REFERENCE in obj) - clone.__proto__ = this.prototype; - - clone.restore(); - - return clone; - } - - serialize({ - encoding = 'json' - } = {}) { - - switch(encoding) { - case 'json': return this.toJson(); - case 'ubjson': - // case 'ubj': return this.toUbj(); - default: { - throw new TypeError('Unknown encoding: ' + encoding); - } - } - - } - - static deserialize(obj: any, { - encoding = 'json' - } = {}) { - - switch(encoding) { - case 'json': return this.fromJson(obj); - case 'ubjson': - // case 'ubj': return this.fromUbj(obj); - default: { - throw new TypeError('Unknown encoding: ' + encoding); - } - } - } - - async restore() {} - - static createFromDisk(filename: string, ...args: any[]) { - if(existsSync(filename)) { - const instance = this.deserialize(readFileSync(createFilepath(filename))); - // TS is plain and simply wrong... symbols can be used to index object... - // @ts-ignore - instance[Serializable.PERSIST_LOCATION] = createFilepath(filename); - instance?.restore(); - return instance; - } else { - const instance = new this(...args); - // again... TS is wrong... - // @ts-ignore - instance[Serializable.PERSIST_LOCATION] = createFilepath(filename); - instance?.updateDisk(); - return instance; - } - } - - updateDisk(filepath?: string) { - // if it hasnt yet been written to disk... - // this can happen if the contrustor - // was called outside of createFromDisk - if(filepath) { - // see above... TS7053 is just _wrong_. incorrect. thats not how JS works. - // @ts-ignore - this[Serializable.PERSIST_LOCATION] = createFilepath(filepath); - } - const data = this.serialize(); - // this is getting annoying... - // @ts-ignore - writeFileSync(this[Serializable.PERSIST_LOCATION], data); - } -} - -function createFilepath(path: string) { - return `data/${path}`; -} \ No newline at end of file diff --git a/src/System.ts b/src/System.ts index 0d51240..b7167e9 100644 --- a/src/System.ts +++ b/src/System.ts @@ -1,8 +1,10 @@ -import Instance from './Instance.js'; -import Serializable from './Serializable.js'; +import Instance, { Link, SerializedInstance } from './Instance.js'; import _ from 'lodash'; import Module from './Module.js'; import debug from 'debug'; +import { lstatSync, readdirSync, readFileSync, writeFileSync } from 'fs'; +import { resolve } from 'path'; +import { ensureDirSync } from 'fs-extra'; const log = debug('vogue:system') const {get, set} = _; @@ -13,30 +15,102 @@ type ModuleNamespaceMap = { type ModuleName = string; -class System extends Serializable { - instances: Instance[] = []; +class System { + instances: Map = new Map(); modules: Module[]; namespace: ModuleNamespaceMap = {}; - staticInstances: { - [key: string]: Instance + staticLinks: { + [key: string]: Link } = {}; rootDir: string; constructor(modules: Module[], rootDir: string) { - super(); + this.rootDir = rootDir; this.modules = modules; this.createNamespace(); + + ensureDirSync(resolve(this.rootDir, '.system')); + + const vault = readdirSync(resolve(this.rootDir, '.system')).map(v => resolve(this.rootDir, '.system', v)); + const serializedInstances: SerializedInstance[] = vault.map((v) => JSON.parse(readFileSync(v).toString())); + + log('injecting serialized instances...'); + for(const serializedInstance of serializedInstances) + this.injectSerializedInstance(serializedInstance); + log('linking serialized instances...'); + for(const serializedInstance of serializedInstances) + this.linkSerializedInstance(serializedInstance); + + log('restoring static instances...'); + for(const [,instance] of this.instances) { + if(!!instance.module.static) { + instance.restore(); + } + } + + log('restoring boot instances...'); + for(const [,instance] of this.instances) { + if(!!instance.module.singleton) { + instance.restore(); + } + } + // this.inject(serializedInstance); + + + if (vault.length !== 0) { + return this; + } + + // TODO future workflow notes + // pull jsons into boots + // filter json boots + // create static / singletons into boots + // boot boots! + + const bootModules = this.deriveBootModules(); this.createStaticInstances(); - this.rootDir = rootDir; log('instantiating boot modules...'); for(const name of bootModules) { log(' ' + name); - this.newInstance(name); + this.newLink(name); } } + linkSerializedInstance(serializedInstance: SerializedInstance): void { + const instance = this.getInstanceById(serializedInstance.id); + for(const name in serializedInstance.links) { + const linkId = serializedInstance.links[name] + const linkedInstance = this.getInstanceById(linkId); + const linkedInstanceLink = linkedInstance.link; + instance.setLink(name, linkedInstanceLink); + } + } + + injectSerializedInstance(serializedInstance: SerializedInstance): void { + const instance = new Instance(this.getModule(serializedInstance.type), this.rootDir, {}, this, { + id: serializedInstance.id + }); + this.instances.set(instance._id, instance); + + for(const name in serializedInstance.members) { + instance.setMember(name, serializedInstance.members[name]); + } + + if(instance.module.static) { + log('injected static instance ' + instance.module.static + ': ' + instance.module.name.full); + this.staticLinks[instance.module.static] = instance.link; + } + } + + getInstanceById(id: string): Instance { + if(!this.instances.has(id)) + throw new Error(`${id} is not a valid instance link id`); + + return this.instances.get(id) as Instance; + } + createStaticInstances() { log('deriving static modules...'); const staticModules = this.modules.filter((module) => { @@ -49,8 +123,8 @@ class System extends Serializable { log('instantiating static modules...'); for(const module of staticModules) { log(' ' + module.static + ': ' + module.name.full); - this.staticInstances[module.static] = - this.newInstance(module.name.full, {}); + this.staticLinks[module.static] = + this.newLink(module.name.full, {}); } } @@ -79,6 +153,16 @@ class System extends Serializable { }, {}); } + saveInstance(instance: Instance): void { + const path = resolve(this.rootDir, '.system'); + ensureDirSync(path); + const file = instance._id + '.json'; + const filepath = resolve(path, file); + log('saving ' + instance + '...'); + const json = JSON.stringify(instance.toSerializableObject(), null, 2) + writeFileSync(filepath, json); + } + getModule(name: ModuleName): Module { const module = get(this.namespace, name); if(module instanceof Module) return module; @@ -86,10 +170,12 @@ class System extends Serializable { } createInstance(name: ModuleName, args = {}) { - return new Instance(this.getModule(name), '', args, this); + const instance = new Instance(this.getModule(name), this.rootDir, args, this); + this.saveInstance(instance); + return instance; } - newInstance(name: ModuleName, args = {}) { + newLink(name: ModuleName, args = {}) { const instance = this.createInstance(name, args); const link = instance.link; instance.restore(); @@ -97,4 +183,14 @@ class System extends Serializable { } } -export default System; \ No newline at end of file +export default System; + +// class SerializedInstanceInjector { +// system: System; +// serializedInstances: SerializedInstance[]; + +// constructor(serializedInstances: SerializedInstance[], system: System) { +// this.serializedInstances = serializedInstances; +// this.system = system; +// } +// } \ No newline at end of file diff --git a/src/extensions.ts b/src/extensions.ts index e0bb37d..06b0441 100644 --- a/src/extensions.ts +++ b/src/extensions.ts @@ -4,3 +4,8 @@ Object.defineProperty(Array.prototype, 'empty', { return this.length === 0; } }); + + +process.on('unhandledRejection', (reason: Error, p) => { + console.log(reason.stack ?? reason.name + '\n\nStack trace unavailable...'); +}); \ No newline at end of file diff --git a/src/run.ts b/src/run.ts index ed37cc7..5de9ebb 100644 --- a/src/run.ts +++ b/src/run.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env node +#!/usr/bin/env node --enable-source-maps --unhandled-rejections=strict import debug from 'debug'; const log = debug('vogue:cli'); const systemLocation = resolve(process.argv[2]); @@ -11,14 +11,11 @@ import Module from './Module.js'; import System from './System.js'; import './extensions.js'; import { fileURLToPath } from 'url'; -// globals inside grammar context -import minify from './minify'; const { get, set } = _; const standardLibrary = resolve(fileURLToPath(dirname(import.meta.url)), '..', 'lib', 'vogue'); (async () => { - // TODO simplify this line gaddam const ignoreDeps = (path: string) => parse(path).name !== 'node_modules'; const files = [ @@ -35,7 +32,7 @@ const standardLibrary = resolve(fileURLToPath(dirname(import.meta.url)), '..', ' const modules = await Promise.all(fullpaths.map(loc => Module.create(loc, systemLocation))); const sys = new System(modules, systemLocation); -})() +})(); function walkdirSync(root: string, filter: ((path: string) => boolean) = () => true): string[] { log('reading', root, '...'); diff --git a/test/main.v b/test/main.v index a901312..cd334cf 100644 --- a/test/main.v +++ b/test/main.v @@ -19,4 +19,6 @@ async restore { // window.setScene() // await counter.render(); + + sync(); } \ No newline at end of file diff --git a/vogue-0.0.1.tgz b/vogue-0.0.1.tgz deleted file mode 100644 index 7b94f1e..0000000 Binary files a/vogue-0.0.1.tgz and /dev/null differ diff --git a/yarn.lock b/yarn.lock index 3bfd784..4231fa1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6,6 +6,13 @@ version "4.1.5" resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz" +"@types/fs-extra@^9.0.11": + version "9.0.11" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.11.tgz#8cc99e103499eab9f347dbc6ca4e99fb8d2c2b87" + integrity sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA== + dependencies: + "@types/node" "*" + "@types/lodash@^4.14.169": version "4.14.169" resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.169.tgz" @@ -14,6 +21,11 @@ version "2.11.1" resolved "https://registry.npmjs.org/@types/nearley/-/nearley-2.11.1.tgz" +"@types/node@*": + version "15.6.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.0.tgz#f0ddca5a61e52627c9dcb771a6039d44694597bc" + integrity sha512-gCYSfQpy+LYhOFTKAeE8BkyGqaxmlFxe+n4DKM6DR0wzw/HISUE/hAmkC/KT8Sw5PCJblqg062b3z9gucv3k0A== + "@types/node@^15.3.0": version "15.3.0" resolved "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz" @@ -24,6 +36,11 @@ dependencies: source-map "^0.6.1" +"@types/uuid@^8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" + integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ== + async@0.2.10: version "0.2.10" resolved "https://registry.npmjs.org/async/-/async-0.2.10.tgz" @@ -62,6 +79,20 @@ discontinuous-range@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz" +fs-extra@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" + integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + immediate@~3.0.5: version "3.0.6" resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz" @@ -70,6 +101,15 @@ isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + lie@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz" @@ -172,6 +212,16 @@ underscore@~1.4.4: version "1.4.4" resolved "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + which@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz"