1
0
Fork 0
mirror of synced 2024-06-26 18:10:51 +12:00

creating new apps ...

This commit is contained in:
michael shanks 2019-06-28 22:59:27 +01:00
parent e321a8b3fa
commit 66ba4e687f
31 changed files with 568 additions and 552 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
.data/
.temp/
# Logs
logs

@ -1 +1 @@
Subproject commit feb2f3412a0e3748d6509a3693b1f38c448bd743
Subproject commit babcb77e70088ddd689062e1d02d8fe2c5bc3417

View file

@ -11,6 +11,7 @@ const rmdir = promisify(fs.rmdir);
const unlink = promisify(fs.unlink);
const readdir = promisify(fs.readdir);
const rename = promisify(fs.rename);
const stat = promisify(fs.stat);
const updateFile = root => async (path, file) =>
await writeFile(
@ -77,6 +78,11 @@ const renameFile = root => async (oldPath, newPath) =>
join(root, newPath)
);
const getFileSize = root => async (path) =>
(await stat(
join(root,path)
)).size;
const datastoreFolder = (applicationId, instanceId) =>
applicationId === "master" ? "master"
: `app.${applicationId}.${instanceId}`;
@ -120,6 +126,7 @@ module.exports.getDatastore = rootFolderPath => ({
writableFileStream: writableFileStream(rootFolderPath),
renameFile: renameFile(rootFolderPath),
getFolderContents: getFolderContents(rootFolderPath),
getFileSize: getFileSize(rootFolderPath),
createEmptyDb: createEmptyDb(rootFolderPath),
datastoreType : "local",
datastoreDescription: rootFolderPath

View file

@ -2,19 +2,19 @@ const Koa = require('koa');
const app = new Koa();
const getMasterAppInternal = require("./utilities/masterAppInternal");
const router = require("./middleware/routers");
const bodyParser = require('koa-bodyparser');
const initialiseRuntimeApps = require("./initialise/initialiseRuntimePackages");
const koaBody = require('koa-body');
const initialiseRuntimePackages = require("./initialise/initialiseRuntimePackages");
module.exports = async (config) => {
app.keys = config.keys;
app.context.master = await getMasterAppInternal(config);
app.context.getAppPackage = await initialiseRuntimeApps(
app.context.getAppPackage = await initialiseRuntimePackages(
config,
app.context.master,
config.latestAppsPath
)
app.use(bodyParser());
app.use(koaBody({ multipart : true }));
app.use(router(config, app).routes());
return app.listen();
};

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,60 @@
const { tmpdir } = require("os");
const { join } = require("path");
const uuid = require("uuid/v1");
const { take, takeRight } = require("lodash/fp");
const { splitKey, $ } = require("budibase-core").common;
const { unzipTarGzPackageToRuntime } = require("../../utilities/targzAppPackage");
const { getRuntimePackageDirectory } = require("../../utilities/runtimePackages");
const { exists } = require("../../utilities/fsawait");
const createInstanceDb = require("../../initialise/createInstanceDb");
module.exports = (config) => ({
initialiseInstance : async ({ instance, apis }) => {
const appKey = $(instance.key, [
splitKey,
take(2)
]);
const application = await apis.recordApi.load(appKey);
const dbConfig = await createInstanceDb(
require(config.datastore),
config.datastoreConfig,
application.id,
instance.id
);
const versionId = $(instance.version.key, [
splitKey,
takeRight(1)
]);
const runtimeDir = getRuntimePackageDirectory(
application.name,
versionId);
if(!await exists(runtimeDir))
await downloadAppPackage(instance, application.name, versionId);
instance.datastoreconfig = JSON.stringify(dbConfig);
instance.isNew = false;
await apis.recordApi.save(instance);
}
});
const downloadAppPackage = async (instance, appName, versionId) => {
const inputStream = apis.recordApi.downloadFile(instance.key, "package.tar.gz");
const tempFilePath = join(tmpdir(), `bbpackage_${uuid()}.tar.gz`);
const outputStream = await app.datastore.writableFileStream(
tempFilePath);
await new Promise((resolve,reject) => {
inputStream.pipe(outputStream);
outputStream.on('error', reject);
outputStream.on('finish', resolve);
});
await unzipTarGzPackageToRuntime(tempFilePath, appName, versionId);
}

View file

@ -0,0 +1,4 @@
module.exports = config => ({
main: require("./main")(config)
});

View file

@ -0,0 +1 @@
{"levels":[{"name":"owner","permissions":[{"type":"create record","nodeKey":"/applications/1-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"create record","nodeKey":"/sessions/17-{id}"},{"type":"update record","nodeKey":"/sessions/17-{id}"},{"type":"delete record","nodeKey":"/sessions/17-{id}"},{"type":"read record","nodeKey":"/sessions/17-{id}"},{"type":"read index","nodeKey":"/mastersessions_by_user"},{"type":"read index","nodeKey":"/all_applications"},{"type":"read index","nodeKey":"/applications/1-{id}/allinstances"},{"type":"read index","nodeKey":"/applications/1-{id}/sessions_by_user"},{"type":"read index","nodeKey":"/applications/1-{id}/user_name_lookup"},{"type":"read index","nodeKey":"/applications/1-{id}/all_versions"},{"type":"read index","nodeKey":"/applications/1-{id}/instances/2-{id}/users_on_this_instance"},{"type":"read index","nodeKey":"/applications/1-{id}/versions/3-{id}/instances_for_this_version"},{"type":"read index","nodeKey":"/applications/1-{id}/versions/3-{id}/instances_on_this_version"},{"type":"write templates"},{"type":"create user"},{"type":"set password"},{"type":"create temporary access"},{"type":"enable or disable user"},{"type":"write access levels"},{"type":"list users"},{"type":"list access levels"},{"type":"manage index"},{"type":"manage collection"},{"type":"set user access levels"}]}],"version":0}

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -8,6 +8,6 @@ module.exports = () => ({
keys: ["secret1", "secret2"],
port: 4001,
latestAppsPath: "./appPackages",
masterPlugins: {},
extraMasterPlugins: {},
customizeMaster: appDefinition => appDefinition
})

View file

@ -18,10 +18,10 @@ module.exports = () => ({
port: 4001,
// path to where your appDefinition etc is stored (dev time)
latestAppsPath: "./appPackages",
latestAppsPath: "./",
// register plugins for master
masterPlugins: {},
extraMasterPlugins: {},
// make modifications to master's appdefinition - e.g. add plugins
customizeMaster: appDefinition => appDefinition

View file

@ -0,0 +1,42 @@
const {
initialiseData,
setupDatastore
} = require("budibase-core");
const constructHierarchy = require("../utilities/constructHierarchy");
const getDatabaseManager = require("../utilities/databaseManager");
const {
getApisForUser,
getApisWithFullAccess
} = require("../utilities/budibaseApi");
const masterDbAppDefinition = require("../appPackages/master/appDefinition.json");
const masterDbAccessLevels = require("../appPackages/master/access_levels.json");
const { masterAppPackage } = require("../utilities/createAppPackage");
module.exports = async (datastoreModule, rootDatastoreConfig, appId, instanceId) => {
try {
const databaseManager = getDatabaseManager(
datastoreModule, rootDatastoreConfig);
await databaseManager.createEmptyInstanceDb(
appId, instanceId);
const dbConfig = databaseManager.getInstanceDatastoreConfig(
appId, instanceId);
const datastore = setupDatastore(
datastoreModule.getDatastore(dbConfig));
await initialiseData(datastore,
constructHierarchy(masterDbAppDefinition));
return dbConfig;
} catch(e) {
throw e;
}
};

View file

@ -1,14 +1,14 @@
const {initialiseData, setupDatastore,
getTemplateApi} = require("budibase-core");
const {initialiseData, setupDatastore} = require("budibase-core");
const constructHierarchy = require("../utilities/constructHierarchy");
const getDatabaseManager = require("../utilities/databaseManager");
const {getApisForUser, getApisWithFullAccess} = require("../utilities/budibaseApi");
const masterDbAppDefinition = require("../appPackages/master/appDefinition.json");
const masterDbAccessLevels = require("../appPackages/master/access_levels.json");
const { masterAppPackage } = require("../utilities/createAppPackage");
module.exports = async (datastoreModule, rootConfig, username, password) => {
module.exports = async (datastoreModule, rootDatastoreConfig, username, password, budibaseConfig) => {
try {
const databaseManager = getDatabaseManager(datastoreModule, rootConfig);
const databaseManager = getDatabaseManager(datastoreModule, rootDatastoreConfig);
await databaseManager.createEmptyMasterDb();
const masterDbConfig = databaseManager.masterDatastoreConfig;
@ -16,23 +16,18 @@ module.exports = async (datastoreModule, rootConfig, username, password) => {
datastoreModule.getDatastore(masterDbConfig)
);
const templateApi = getTemplateApi({datastore});
await initialiseData(datastore, {
hierarchy:templateApi.constructHierarchy(masterDbAppDefinition.hierarchy),
actions:masterDbAppDefinition.actions,
triggers:masterDbAppDefinition.triggers
});
await initialiseData(datastore,
constructHierarchy(masterDbAppDefinition));
const bbMaster = await getApisWithFullAccess(
datastore, masterAppPackage());
datastore, masterAppPackage(budibaseConfig));
await bbMaster.authApi.saveAccessLevels(masterDbAccessLevels);
const user = bbMaster.authApi.getNewUser();
user.name = username;
user.accessLevels= ["owner"];
await bbMaster.authApi.createUser(user, password);
return await getApisForUser(datastore, masterAppPackage(), username, password);
return await getApisForUser(datastore, masterAppPackage(budibaseConfig), username, password);
} catch(e) {
throw e;
}

View file

@ -1,12 +1,11 @@
const create = require("./createMasterDb");
const argv = require("yargs").argv
const readline = require('readline');
const { promisify } = require('util');
const rimraf = promisify(require("rimraf"));
const fs = require("fs")
const { mkdir, rimraf } = require("../utilities/fsawait");
const budibaseConfig = require("../config");
const mkdir = promisify(fs.mkdir);
readline.Interface.prototype.question[promisify.custom] = function(prompt) {
return new Promise(resolve =>
@ -72,5 +71,6 @@ const question = async (q) => {
datastoreModule,
rootconfig,
username,
password)
password,
budibaseConfig)
})()

View file

@ -1,21 +1,7 @@
const util = require("util");
const fs = require("fs");
const { join } = require("path");
const { ncp } = require('ncp');
const { masterAppPackage } = require("../utilities/createAppPackage");
const rimraf = util.promisify(require("rimraf"));
const mkdir = util.promisify(fs.mkdir);
const exists = root => async (path) => {
try {
await util.promisify(fs.access)(
join(root,path)
);
} catch (e) {
return false;
}
return true;
};
const { mkdir, rimraf, exists } = require("../utilities/fsawait");
const { runtimePackagesDirectory } = require("../utilities/runtimePackages");
const copyfolder = (source, destination) =>
new Promise((resolve, reject) => {
@ -28,20 +14,20 @@ const copyfolder = (source, destination) =>
});
});
module.exports = async (config, bbMaster, latestAppsFolder) => {
const appsRuntimeFolder = "./runtime_apps";
// create runtime folder
// copy master into /master/latest
if(await exists(appsRuntimeFolder)) {
if(await exists(runtimePackagesDirectory)) {
try {
await rimraf(appsRuntimeFolder);
await rimraf(runtimePackagesDirectory);
} catch(err) {
console.log(err);
}
}
await mkdir(appsRuntimeFolder);
await mkdir(runtimePackagesDirectory);
/*

View file

@ -1,6 +1,8 @@
const Router = require("koa-router");
const Router = require("@koa/router");
const session = require("./session");
const StatusCodes = require("../utilities/statusCodes");
const fs = require("fs");
module.exports = (config, app) => {
const router = new Router();
@ -148,21 +150,30 @@ module.exports = (config, app) => {
);
ctx.response.status = StatusCodes.OK;
})
.post("/:appname/api/record/:recordkey", async (ctx) => {
.post("/:appname/api/files/*", async (ctx) => {
const file = ctx.request.files.file;
ctx.body = await ctx.instance.recordApi.uploadFile(
getRecordKey(ctx.params.appname, ctx.request.path),
fs.createReadStream(file.path),
file.name
);
ctx.response.status = StatusCodes.OK;
})
.post("/:appname/api/record/*", async (ctx) => {
ctx.body = await ctx.instance.recordApi.save(
ctx.request.body
);
ctx.response.status = StatusCodes.OK;
})
.get("/:appname/api/record/:recordkey", async (ctx) => {
.get("/:appname/api/record/*", async (ctx) => {
ctx.body = await ctx.instance.recordApi.load(
ctx.params.recordKey
getRecordKey(ctx.params.appname, ctx.request.path)
);
ctx.response.status = StatusCodes.OK;
})
.del("/:appname/api/record/:recordkey", async (ctx) => {
.del("/:appname/api/record/*", async (ctx) => {
await ctx.instance.recordApi.delete(
ctx.params.recordKey
getRecordKey(ctx.params.appname, ctx.request.path)
);
ctx.response.status = StatusCodes.OK;
})
@ -185,6 +196,11 @@ module.exports = (config, app) => {
ctx.response.status = StatusCodes.OK;
});
const getRecordKey = (appname, wholePath) =>
wholePath
.replace(`/${appname}/api/record/`, "")
.replace(`/${appname}/api/files/`, "");
return router;
}

View file

@ -12,14 +12,16 @@
"author": "Michael Shanks",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@koa/router": "^8.0.0",
"argon2": "^0.23.0",
"budibase-core": "file:../core/dist",
"koa": "^2.7.0",
"koa-bodyparser": "^4.2.1",
"koa-router": "^7.4.0",
"koa-body": "^4.1.0",
"koa-session": "^5.12.0",
"ncp": "^2.0.0",
"rimraf": "^2.6.3",
"tar-fs": "^2.0.0",
"uuid": "^3.3.2",
"yargs": "^13.2.4"
},
"devDependencies": {

View file

@ -1,10 +1,12 @@
const app = require("./testApp")();
const authenticateMaster = require("./authenticate");
const createNewApp = require("./createNewApp");
beforeAll(async () => await app.start())
afterAll(async () => await app.destroy())
describe("authenticateMaster", () => authenticateMaster(app));
describe("createNewApp", () => createNewApp(app));

View file

@ -1,7 +1,6 @@
const statusCodes = require("../utilities/statusCodes");
const util = require("util");
const fs = require("fs");
const readFile = util.promisify(fs.readFile);
const { readFile } = require("../utilities/fsawait");
const { timeout } = require("./helpers");
module.exports = (app) => {
@ -31,7 +30,6 @@ module.exports = (app) => {
});
let ownerCookie;
it("should return ok correct username and password supplied", async () => {
const response = await app.post("/_master/api/authenticate", {
@ -40,7 +38,7 @@ module.exports = (app) => {
})
.expect(statusCodes.OK);
ownerCookie = response.header['set-cookie'];
app.masterAuth.cookie = response.header['set-cookie'];
});
const testUserName = "test_user";
@ -56,7 +54,7 @@ module.exports = (app) => {
},
password: testPassword
})
.set("cookie", ownerCookie)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
@ -74,7 +72,7 @@ module.exports = (app) => {
newUserCookie = responseNewUser.header['set-cookie'];
expect(newUserCookie).toBeDefined();
expect(newUserCookie).not.toEqual(ownerCookie);
expect(newUserCookie).not.toEqual(app.masterAuth.cookie);
app.get("/_master/api/users/")
.set("cookie", newUserCookie)
@ -86,7 +84,7 @@ module.exports = (app) => {
await app.post("/_master/api/disableUser", {
username: testUserName
})
.set("cookie", ownerCookie)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
await app.get("/_master/api/users/")
@ -114,7 +112,7 @@ module.exports = (app) => {
await app.post("/_master/api/enableUser", {
username: testUserName
})
.set("cookie", ownerCookie)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
await app.post("/_master/api/authenticate", {
@ -124,8 +122,9 @@ module.exports = (app) => {
.expect(statusCodes.OK);
});
let testUserTempCode;
it("should be able to reset password with temporary access", async () => {
// need to sort out behaviour sources for this...
await app.post("/_master/api/createTemporaryAccess", {
username: testUserName
})
@ -133,11 +132,15 @@ module.exports = (app) => {
testPassword = "test_user_new_password";
const tempCode = await readFile(`./tests/.data/tempaccess${testUserName}`, "utf8");
// the behaviour that creates the below file is async,
/// to this timeout is giving it a change to work its magic
await timeout(10);
const testUserTempCode = await readFile(`./tests/.data/tempaccess${testUserName}`, "utf8");
await app.post("/_master/api/setPasswordFromTemporaryCode", {
username: testUserName,
tempCode,
tempCode:testUserTempCode,
newPassword:testPassword
})
.expect(statusCodes.OK);
@ -148,5 +151,30 @@ module.exports = (app) => {
})
.expect(statusCodes.OK);
});
it("should not be able to set password with used temp code", async () => {
await app.post("/_master/api/setPasswordFromTemporaryCode", {
username: testUserName,
tempCode:testUserTempCode,
newPassword:"whatever"
})
.expect(statusCodes.OK);
await app.post("/_master/api/authenticate", {
username: testUserName,
password: "whatever"
})
.expect(statusCodes.UNAUTHORIZED);
await app.post("/_master/api/authenticate", {
username: testUserName,
password: testPassword
})
.expect(statusCodes.OK);
});
};

View file

@ -0,0 +1,72 @@
const statusCodes = require("../utilities/statusCodes");
const constructHierarchy = require("../utilities/constructHierarchy");
const { readFile } = require("../utilities/fsawait");
const {getRecordApi, getAuthApi} = require("budibase-core");
const masterAppDefinition = constructHierarchy(
require("../appPackages/master/appDefinition.json"));
const {getApisWithFullAccess} = require("../utilities/budibaseApi");
const { createTarGzPackage } = require("../utilities/targzAppPackage");
module.exports = (app) => {
let _master;
const getmaster = async () => {
if(!_master)
_master = await getApisWithFullAccess({}, app.masterAppPackage);
return _master;
}
let newAppKey = "";
it("create new application should be successful for owner", async () => {
const master = await getmaster();
const newApp = master.recordApi.getNew("/applications", "application");
newApp.name = app.testAppInfo.name
newAppKey = newApp.key;
await app.post(`/_master/api/record/${newApp.key}`, newApp)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
const response = await app.get(`/_master/api/record/${newApp.key}`)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
expect(response.body.name).toBe(newApp.name);
});
let version1Key = "";
it("should be able to upload new version including package files", async () => {
const master = await getmaster();
const version1 = master.recordApi
.getNew(`${newAppKey}/versions`, "version");
version1.name = "v1";
version1Key = version1.key;
const { path, size } = await createTarGzPackage(app.config, "testApp");
version1.package = { relativePath: "package.tar.gz", size};
await app.post(`/_master/api/record/${version1.key}`, version1)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
await app.post(`/_master/api/files/${version1.key}`)
.attach("file", path, "package.tar.gz")
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
});
it("should be able to create new instance of app", async () => {
const master = await getmaster();
const instance1 = master.recordApi
.getNew(`${newAppKey}/instances`, "instance");
instance1.name = "instance 1";
instance1.active = true;
instance1.version = {key:version1Key, name:"v1"};
instance1.datastoreconfig;
});
}

View file

@ -0,0 +1,9 @@
module.exports.timeout = ms =>
new Promise(resolve => setTimeout(resolve, ms));
module.exports.sleep = async (ms, fn) => {
await timeout(ms);
return await fn(...args);
}

View file

@ -1,16 +1,16 @@
const app = require("../app");
const { promisify } = require("util");
const rimraf = promisify(require("rimraf"));
const { rimraf, mkdir } = require("../utilities/fsawait");
const createMasterDb = require("../initialise/createMasterDb");
const request = require("supertest");
const fs = require("fs");
const { masterAppPackage } = require("../utilities/createAppPackage");
var enableDestroy = require('server-destroy');
const mkdir = promisify(fs.mkdir);
const masterOwnerName = "test_master";
const masterOwnerPassword = "test_master_pass";
const masterPlugins = {
const extraMasterPlugins = {
main: {
outputToFile : ({filename, content}) => {
fs.writeFile(`./tests/.data/${filename}`, content, {encoding:"utf8"});
@ -44,7 +44,8 @@ const config = {
},
keys: ["secret1", "secret2"],
port: 4002,
masterPlugins,
latestAppsPath: "./appPackages",
extraMasterPlugins,
customizeMaster
}
@ -69,12 +70,19 @@ module.exports = () => {
get: (url) => getRequest(server, url),
masterAuth: {
username: masterOwnerName,
password: masterOwnerPassword
password: masterOwnerPassword,
cookie: ""
},
destroy: () => server.destroy()
testAppInfo: {
name: "testApp"
},
destroy: () => server.destroy(),
masterAppPackage: masterAppPackage(config)
})
};
const postRequest = (server, url, body) =>
request(server)
.post(url)
@ -98,7 +106,8 @@ const reInitialize = async () => {
datastoreModule,
config.datastoreConfig,
masterOwnerName,
masterOwnerPassword
masterOwnerPassword ,
config
);
}

View file

@ -0,0 +1,6 @@
const {getTemplateApi} = require("budibase-core");
const templateApi = getTemplateApi({});
module.exports = (appDefinition) => {
appDefinition.hierarchy = templateApi.constructHierarchy(appDefinition.hierarchy);
return appDefinition;
}

View file

@ -1,8 +1,9 @@
const { join } = require("path");
const constructHierarchy = require("./constructHierarchy");
const { common } = require("budibase-core");
const createAppPackage = (appPath) => ({
appDefinition: require(join(appPath, "appDefinition.json")),
behaviourSources: require(join(appPath, "plugins.json")),
behaviourSources: require(join(appPath, "plugins.js")),
appPath
});
@ -13,19 +14,25 @@ module.exports.masterAppPackage = (config) => {
? config.customizeMaster
: a => a;
const appDefinition = customizeMaster(
standardPackage.appDefinition);
const appDefinition = common.$(standardPackage.appDefinition, [
customizeMaster,
constructHierarchy
]);
const plugins = require("../appPackages/master/plugins.js")
(config);
return ({
appDefinition,
behaviourSources: config && config.masterPlugins
? config.masterPlugins
: standardPackage.behaviourSources,
behaviourSources: config && config.extraMasterPlugins
? {...plugins, ...config.extraMasterPlugins}
: plugins,
appPath: standardPackage.appPath
});
}
module.exports.applictionVersionPackage = (appname, versionId) =>
createAppPackage(`../runtimePackages/${appname}/${versionId}`);
module.exports.applictionVersionPackage = (appname, versionId) => {
const pkg = createAppPackage(`../runtimePackages/${appname}/${versionId}`);
pkg.appDefinition = constructHierarchy(appDefinition);
return pkg;
}

View file

@ -0,0 +1,19 @@
const util = require("util");
const fs = require("fs");
module.exports.readFile = util.promisify(fs.readFile);
module.exports.rimraf = util.promisify(require("rimraf"));
module.exports.mkdir = util.promisify(fs.mkdir);
module.exports.unlink = util.promisify(fs.unlink);
module.exports.stat = util.promisify(fs.stat);
module.exports.exists = async (path) => {
try {
await util.promisify(fs.access)(
path
);
} catch (e) {
return false;
}
return true;
};

View file

@ -0,0 +1,8 @@
const { join } = require("path");
const runtimePackagesDirectory = "./runtime_apps";
module.exports.runtimePackagesDirectory = runtimePackagesDirectory;
module.exports.getRuntimePackageDirectory = (appName, versionId) =>
join(runtimePackagesDirectory, appName, versionId);

View file

@ -2,5 +2,6 @@ module.exports = {
OK:200,
UNAUTHORIZED:401,
FORBIDDEN:403,
NOT_FOUND:404
NOT_FOUND:404,
INTERAL_ERROR:500
};

View file

@ -0,0 +1,91 @@
// Based on https://github.com/lafin/node-targz
// MIT license
const fs = require("fs");
const tar = require('tar-fs');
const zlib = require("zlib");
const { join, dirname, sep } = require("path");
const { exists, mkdir, unlink, stat } = require("../utilities/fsawait");
const { getRuntimePackageDirectory } = require("./runtimePackages");
module.exports.createTarGzPackage = async (config, appName) => {
const appPath = join(config.latestAppsPath, appName);
const distPath = join(appPath, "dist");
if(!await exists(distPath)) {
await mkdir(distPath);
}
const packagePath = `${distPath}/package.tar.gz`;
if(await exists(`${packagePath}`)) {
await unlink(packagePath);
}
await compress(appPath, packagePath);
const size = (await stat(packagePath)).size;
return {size, path:packagePath};
}
module.exports.unzipTarGzPackageToRuntime = async (src, appName, versionId) => {
const versionDir = getRuntimePackageDirectory(appName, versionId);
if(await exists(versionDir)) {
await rimraf(versionDir);
}
await mkdir(versionDir);
decompress(src, versionDir);
}
const compress = (src, dest) => new Promise((resolve, reject) => {
// ensure opts
opts = {src, dest};
opts.tar = {ignore: name => dirname(name).split(sep).pop() === "dist"};
opts.gz = opts.gz || {};
// default gzip config
opts.gz.level = opts.gz.level || 6;
opts.gz.memLevel = opts.gz.memLevel || 6;
// ensure src and dest
if(!opts.src) return reject("No source for compress!");
if(!opts.dest) return reject("No destination for compress!");
// go
process.nextTick(function () {
tar.pack(opts.src, opts.tar)
.on('error', reject)
.pipe(zlib.createGzip(opts.gz)
.on('error', reject))
.pipe(fs.createWriteStream(opts.dest)
.on('error', reject)
.on('finish', resolve));
});
});
const decompress = (src, dest) => new Promise((resolve, reject) => {
// ensure opts
opts = {src, dest};
opts.tar = opts.tar || {};
opts.gz = opts.gz || {};
// ensure src and dest
if(!opts.src) return reject("No source for decompress!");
if(!opts.dest) return reject("No destination for decompress!");
// go
process.nextTick(function () {
fs.createReadStream(opts.src)
.on('error', reject)
.pipe(zlib.createGunzip(opts.gz)
.on('error', reject))
.pipe(tar.extract(opts.dest, opts.tar)
.on('error', reject)
.on('finish', resolve));
});
});

View file

@ -285,6 +285,18 @@
"@types/istanbul-reports" "^1.1.1"
"@types/yargs" "^12.0.9"
"@koa/router@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@koa/router/-/router-8.0.0.tgz#fd4ffa6f03d8293a04c023cb4a22b612401fbe70"
integrity sha512-P70CGOGs6JPu/mnrd9lt6ESzlBXLHT/uTK8+5U4M7Oapt8la/tiZv2c7X9jq0ksFsM59RH3AwJYzKOuavDcjIw==
dependencies:
debug "^3.1.0"
http-errors "^1.3.1"
koa-compose "^3.0.0"
methods "^1.0.1"
path-to-regexp "^1.1.1"
urijs "^1.19.0"
"@nx-js/compiler-util@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@nx-js/compiler-util/-/compiler-util-2.0.0.tgz#c74c12165fa2f017a292bb79af007e8fce0af297"
@ -330,6 +342,19 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/events@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
"@types/formidable@^1.0.31":
version "1.0.31"
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-1.0.31.tgz#274f9dc2d0a1a9ce1feef48c24ca0859e7ec947b"
integrity sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==
dependencies:
"@types/events" "*"
"@types/node" "*"
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
@ -350,6 +375,11 @@
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*"
"@types/node@*":
version "12.0.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.10.tgz#51babf9c7deadd5343620055fc8aff7995c8b031"
integrity sha512-LcsGbPomWsad6wmMNv7nBLw7YYYyfdYcz6xryKYQhx89c3XXan+8Q6AJ43G5XDIaklaVkK3mE4fCb0SBvMiPSQ==
"@types/stack-utils@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
@ -612,6 +642,13 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
bl@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88"
integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==
dependencies:
readable-stream "^3.0.1"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -776,15 +813,15 @@ cliui@^5.0.0:
strip-ansi "^5.2.0"
wrap-ansi "^5.1.0"
co-body@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/co-body/-/co-body-6.0.0.tgz#965b9337d7f5655480787471f4237664820827e3"
integrity sha512-9ZIcixguuuKIptnY8yemEOuhb71L/lLf+Rl5JfJEUiDNJk0e02MBt7BPxR2GEh5mw8dPthQYR4jPI/BnS1MQgw==
co-body@^5.1.1:
version "5.2.0"
resolved "https://registry.yarnpkg.com/co-body/-/co-body-5.2.0.tgz#5a0a658c46029131e0e3a306f67647302f71c124"
integrity sha512-sX/LQ7LqUhgyaxzbe7IqwPeTr2yfpfUIQ/dgpKo6ZI4y4lpQA0YxAomWIY+7I7rHWcG02PG+OuPREzMW/5tszQ==
dependencies:
inflation "^2.0.0"
qs "^6.5.2"
raw-body "^2.3.3"
type-is "^1.6.16"
qs "^6.4.0"
raw-body "^2.2.0"
type-is "^1.6.14"
co@^4.6.0:
version "4.6.0"
@ -880,11 +917,6 @@ copy-descriptor@^0.1.0:
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
copy-to@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/copy-to/-/copy-to-2.0.1.tgz#2680fbb8068a48d08656b6098092bdafc906f4a5"
integrity sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU=
core-util-is@1.0.2, core-util-is@^1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -1083,7 +1115,7 @@ emoji-regex@^7.0.1:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
end-of-stream@^1.1.0:
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
@ -1310,7 +1342,7 @@ form-data@^2.3.1, form-data@~2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"
formidable@^1.2.0:
formidable@^1.1.1, formidable@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659"
integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==
@ -1327,6 +1359,11 @@ fresh@~0.5.2:
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs-minipass@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07"
@ -1519,7 +1556,18 @@ http-assert@^1.3.0:
deep-equal "~1.0.1"
http-errors "~1.7.2"
http-errors@1.7.2, http-errors@^1.3.1, http-errors@^1.6.3, http-errors@~1.7.2:
http-errors@1.7.3, http-errors@^1.3.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
dependencies:
depd "~1.1.2"
inherits "2.0.4"
setprototypeof "1.1.1"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-errors@^1.6.3, http-errors@~1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
@ -1589,6 +1637,11 @@ inherits@2, inherits@2.0.3, inherits@~2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
inherits@2.0.4, inherits@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
@ -2322,13 +2375,14 @@ kleur@^3.0.2:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
koa-bodyparser@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/koa-bodyparser/-/koa-bodyparser-4.2.1.tgz#4d7dacb5e6db1106649b595d9e5ccb158b6f3b29"
integrity sha512-UIjPAlMZfNYDDe+4zBaOAUKYqkwAGcIU6r2ARf1UOXPAlfennQys5IiShaVeNf7KkVBlf88f2LeLvBFvKylttw==
koa-body@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/koa-body/-/koa-body-4.1.0.tgz#99295ee2e9543884e5730ae696780930b3821c44"
integrity sha512-rWkMfMaCjFmIAMohtjlrg4BqDzcotK5BdZhiwJu1ONuR1ceoFUjnH3wp0hEV39HuBlc9tI3eUUFMK4Cp6ccFtA==
dependencies:
co-body "^6.0.0"
copy-to "^2.0.1"
"@types/formidable" "^1.0.31"
co-body "^5.1.1"
formidable "^1.1.1"
koa-compose@^3.0.0:
version "3.2.1"
@ -2355,18 +2409,6 @@ koa-is-json@^1.0.0:
resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14"
integrity sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ=
koa-router@^7.4.0:
version "7.4.0"
resolved "https://registry.yarnpkg.com/koa-router/-/koa-router-7.4.0.tgz#aee1f7adc02d5cb31d7d67465c9eacc825e8c5e0"
integrity sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g==
dependencies:
debug "^3.1.0"
http-errors "^1.3.1"
koa-compose "^3.0.0"
methods "^1.0.1"
path-to-regexp "^1.1.1"
urijs "^1.19.0"
koa-session@^5.12.0:
version "5.12.0"
resolved "https://registry.yarnpkg.com/koa-session/-/koa-session-5.12.0.tgz#1e6c6cea86b8ca2cca921c4a8881cfbfcc2628e0"
@ -3113,7 +3155,7 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@^6.5.1, qs@^6.5.2:
qs@^6.4.0, qs@^6.5.1:
version "6.7.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
@ -3123,13 +3165,13 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
raw-body@^2.3.3:
version "2.4.0"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
raw-body@^2.2.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c"
integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==
dependencies:
bytes "3.1.0"
http-errors "1.7.2"
http-errors "1.7.3"
iconv-lite "0.4.24"
unpipe "1.0.0"
@ -3178,6 +3220,15 @@ readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.3.5:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.0.1, readable-stream@^3.1.1:
version "3.4.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
realpath-native@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c"
@ -3601,7 +3652,7 @@ string-width@^3.0.0, string-width@^3.1.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
string_decoder@^1.2.0:
string_decoder@^1.1.1, string_decoder@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d"
integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==
@ -3694,6 +3745,27 @@ symbol-tree@^3.2.2:
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
tar-fs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad"
integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==
dependencies:
chownr "^1.1.1"
mkdirp "^0.5.1"
pump "^3.0.0"
tar-stream "^2.0.0"
tar-stream@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==
dependencies:
bl "^3.0.0"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
readable-stream "^3.1.1"
tar@^4:
version "4.4.10"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1"
@ -3809,7 +3881,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
type-is@^1.6.16:
type-is@^1.6.14, type-is@^1.6.16:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
@ -3870,7 +3942,7 @@ use@^3.1.0:
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
util-deprecate@~1.0.1:
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=