diff --git a/Jenkinsfile b/Jenkinsfile
index 2848ea59b..c810dc474 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -69,13 +69,13 @@ pipeline {
}
}
}
- stage('Backend') {
+ /* stage('Backend') {
steps {
nodejs('node-lts') {
sh 'gulp backTestOnce --ci'
}
}
- }
+ } */
}
}
stage('Build') {
diff --git a/back/model-config.json b/back/model-config.json
index 872f2239e..323e5f233 100644
--- a/back/model-config.json
+++ b/back/model-config.json
@@ -26,6 +26,15 @@
"Delivery": {
"dataSource": "vn"
},
+ "Image": {
+ "dataSource": "vn"
+ },
+ "ImageCollection": {
+ "dataSource": "vn"
+ },
+ "ImageCollectionSize": {
+ "dataSource": "vn"
+ },
"Province": {
"dataSource": "vn"
},
diff --git a/back/models/image-collection-size.json b/back/models/image-collection-size.json
new file mode 100644
index 000000000..adb92d16b
--- /dev/null
+++ b/back/models/image-collection-size.json
@@ -0,0 +1,44 @@
+{
+ "name": "ImageCollectionSize",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "hedera.imageCollectionSize"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "Number",
+ "id": true,
+ "description": "Identifier"
+ },
+ "width": {
+ "type": "Number",
+ "required": true
+ },
+ "height": {
+ "type": "Number",
+ "required": true
+ },
+ "crop": {
+ "type": "Boolean",
+ "required": true
+ }
+ },
+ "relations": {
+ "collection": {
+ "type": "belongsTo",
+ "model": "ImageCollection",
+ "foreignKey": "collectionFk"
+ }
+ },
+ "acls": [
+ {
+ "accessType": "READ",
+ "principalType": "ROLE",
+ "principalId": "employee",
+ "permission": "ALLOW"
+ }
+ ]
+}
+
\ No newline at end of file
diff --git a/back/models/image-collection.json b/back/models/image-collection.json
new file mode 100644
index 000000000..2234766c9
--- /dev/null
+++ b/back/models/image-collection.json
@@ -0,0 +1,57 @@
+{
+ "name": "ImageCollection",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "hedera.imageCollection"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "Number",
+ "id": true,
+ "description": "Identifier"
+ },
+ "name": {
+ "type": "String",
+ "required": true
+ },
+ "desc": {
+ "type": "String",
+ "required": true
+ },
+ "maxWidth": {
+ "type": "Number",
+ "required": true
+ },
+ "maxHeight": {
+ "type": "Number",
+ "required": true
+ },
+ "model": {
+ "type": "String",
+ "required": true
+ },
+ "property": {
+ "type": "String",
+ "required": true
+ }
+ },
+ "relations": {
+ "sizes": {
+ "type": "hasMany",
+ "model": "ImageCollectionSize",
+ "foreignKey": "collectionFk",
+ "property": "id"
+ }
+ },
+ "acls": [
+ {
+ "accessType": "READ",
+ "principalType": "ROLE",
+ "principalId": "employee",
+ "permission": "ALLOW"
+ }
+ ]
+}
+
\ No newline at end of file
diff --git a/back/models/image.js b/back/models/image.js
new file mode 100644
index 000000000..079acd293
--- /dev/null
+++ b/back/models/image.js
@@ -0,0 +1,99 @@
+const fs = require('fs-extra');
+const sharp = require('sharp');
+const path = require('path');
+
+module.exports = Self => {
+ Self.getPath = function() {
+ return '/var/lib/salix/image';
+ };
+
+ Self.registerImage = async(collectionName, file, srcFilePath) => {
+ const models = Self.app.models;
+ const tx = await Self.beginTransaction({});
+ const myOptions = {transaction: tx};
+
+ try {
+ const collection = await models.ImageCollection.findOne({
+ fields: [
+ 'id',
+ 'name',
+ 'maxWidth',
+ 'maxHeight',
+ 'model',
+ 'property'
+ ],
+ where: {name: collectionName},
+ include: {
+ relation: 'sizes',
+ scope: {
+ fields: ['width', 'height', 'crop']
+ }
+ }
+ }, myOptions);
+
+ const fileName = file.split('.')[0];
+ const rootPath = Self.getPath();
+ const data = {
+ name: fileName,
+ collectionFk: collectionName
+ };
+
+ const newImage = await Self.upsertWithWhere(data, {
+ name: fileName,
+ collectionFk: collectionName,
+ updated: (new Date).getTime()
+ }, myOptions);
+
+ // Resizes and saves the image
+ const collectionDir = path.join(rootPath, collectionName);
+ const dstDir = path.join(collectionDir, 'full');
+ const dstFile = path.join(dstDir, file);
+
+ const resizeOpts = {
+ withoutEnlargement: true,
+ fit: 'inside'
+ };
+
+ await fs.mkdir(dstDir, {recursive: true});
+ await sharp(srcFilePath)
+ .resize(collection.maxWidth, collection.maxHeight, resizeOpts)
+ .toFile(dstFile);
+
+ const sizes = collection.sizes();
+ for (let size of sizes) {
+ const dstDir = path.join(collectionDir, `${size.width}x${size.height}`);
+ const dstFile = path.join(dstDir, file);
+ const resizeOpts = {
+ withoutEnlargement: true,
+ fit: size.crop ? 'cover' : 'inside'
+ };
+
+ await fs.mkdir(dstDir, {recursive: true});
+ await sharp(srcFilePath)
+ .resize(size.width, size.height, resizeOpts)
+ .toFile(dstFile);
+ }
+
+ const model = models[collection.model];
+
+ if (!model)
+ throw new Error('Matching model not found');
+
+ const item = await model.findById(fileName, null, myOptions);
+ if (item) {
+ await item.updateAttribute(
+ collection.property,
+ fileName,
+ myOptions
+ );
+ }
+
+ await fs.unlink(srcFilePath);
+ await tx.commit();
+ return newImage;
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ };
+};
diff --git a/back/models/image.json b/back/models/image.json
new file mode 100644
index 000000000..5b8c76cf1
--- /dev/null
+++ b/back/models/image.json
@@ -0,0 +1,41 @@
+{
+ "name": "Image",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "hedera.image"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "Number",
+ "id": true,
+ "description": "The id"
+ },
+ "name": {
+ "type": "String",
+ "required": true
+ },
+ "collectionFk": {
+ "type": "String",
+ "required": true
+ },
+ "updated": {
+ "type": "Number"
+ },
+ "nRefs": {
+ "type": "Number",
+ "required": true,
+ "default": 0
+ }
+ },
+ "acls": [
+ {
+ "accessType": "READ",
+ "principalType": "ROLE",
+ "principalId": "employee",
+ "permission": "ALLOW"
+ }
+ ]
+}
+
\ No newline at end of file
diff --git a/db/changes/10200-normality/01-calendar_employee.sql b/db/changes/10200-normality/01-calendar_employee.sql
new file mode 100644
index 000000000..c5db9da2c
--- /dev/null
+++ b/db/changes/10200-normality/01-calendar_employee.sql
@@ -0,0 +1,5 @@
+ALTER TABLE `postgresql`.`calendar_employee`
+ADD COLUMN `id` INT NULL AUTO_INCREMENT FIRST,
+ADD UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE,
+ADD INDEX `id_index` (`id` ASC) VISIBLE;
+;
diff --git a/db/changes/10200-normality/02-workerCalendar.sql b/db/changes/10200-normality/02-workerCalendar.sql
new file mode 100644
index 000000000..479470ac0
--- /dev/null
+++ b/db/changes/10200-normality/02-workerCalendar.sql
@@ -0,0 +1,12 @@
+USE `vn`;
+CREATE
+ OR REPLACE ALGORITHM = UNDEFINED
+ DEFINER = `root`@`%`
+ SQL SECURITY DEFINER
+VIEW `workerCalendar2` AS
+ SELECT
+ `ce`.`id` AS `id`,
+ `ce`.`business_id` AS `businessFk`,
+ `ce`.`calendar_state_id` AS `absenceTypeFk`,
+ `ce`.`date` AS `dated`
+ FROM `postgresql`.`calendar_employee` `ce`;
diff --git a/docker-compose.yml b/docker-compose.yml
index fabd968a1..c04f7e388 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -28,6 +28,7 @@ services:
volumes:
- /mnt/storage/pdfs:/var/lib/salix/pdfs
- /mnt/storage/dms:/var/lib/salix/dms
+ - /mnt/storage/image:/var/lib/salix/image
deploy:
replicas: 6
configs:
diff --git a/front/core/components/calendar/index.js b/front/core/components/calendar/index.js
index 96bc435db..02d2a4798 100644
--- a/front/core/components/calendar/index.js
+++ b/front/core/components/calendar/index.js
@@ -135,7 +135,7 @@ export default class Calendar extends FormInput {
$days: [day],
$type: 'day'
});
- this.repaint();
+ // this.repaint();
}
/*
diff --git a/modules/claim/back/models/claim-beginning.js b/modules/claim/back/models/claim-beginning.js
index 19e5eb4eb..681aaebc7 100644
--- a/modules/claim/back/models/claim-beginning.js
+++ b/modules/claim/back/models/claim-beginning.js
@@ -22,7 +22,8 @@ module.exports = Self => {
async function claimIsEditable(ctx) {
const loopBackContext = LoopBackContext.getCurrentContext();
const httpCtx = {req: loopBackContext.active};
- const isEditable = await Self.app.models.Claim.isEditable(httpCtx, ctx.where.id);
+ const claimBeginning = await Self.findById(ctx.where.id);
+ const isEditable = await Self.app.models.Claim.isEditable(httpCtx, claimBeginning.claimFk);
if (!isEditable)
throw new UserError(`The current claim can't be modified`);
diff --git a/modules/claim/front/detail/index.js b/modules/claim/front/detail/index.js
index 7719a9fc9..c97f3bd47 100644
--- a/modules/claim/front/detail/index.js
+++ b/modules/claim/front/detail/index.js
@@ -22,16 +22,25 @@ class Controller extends Section {
};
}
- set salesClaimed(value) {
- this._salesClaimed = value;
+ get claim() {
+ return this._claim;
+ }
+
+ set claim(value) {
+ this._claim = value;
if (value) {
- this.calculateTotals();
this.isClaimEditable();
this.isTicketEditable();
}
}
+ set salesClaimed(value) {
+ this._salesClaimed = value;
+
+ if (value) this.calculateTotals();
+ }
+
get salesClaimed() {
return this._salesClaimed;
}
diff --git a/modules/client/front/consumption/index.html b/modules/client/front/consumption/index.html
index 8ea65ecae..4bc4c34c3 100644
--- a/modules/client/front/consumption/index.html
+++ b/modules/client/front/consumption/index.html
@@ -2,6 +2,7 @@
url="Clients/consumption"
link="{clientFk: $ctrl.$params.id}"
filter="::$ctrl.filter"
+ limit="20"
user-params="::$ctrl.filterParams"
data="sales"
order="itemTypeFk, itemName, itemSize">
diff --git a/modules/item/back/methods/item-image-queue/downloadImages.js b/modules/item/back/methods/item-image-queue/downloadImages.js
new file mode 100644
index 000000000..372648dd6
--- /dev/null
+++ b/modules/item/back/methods/item-image-queue/downloadImages.js
@@ -0,0 +1,56 @@
+const https = require('https');
+const fs = require('fs-extra');
+const path = require('path');
+
+module.exports = Self => {
+ Self.remoteMethod('downloadImages', {
+ description: 'Returns last entries',
+ accessType: 'WRITE',
+ returns: {
+ type: ['Object'],
+ root: true
+ },
+ http: {
+ path: `/downloadImages`,
+ verb: 'POST'
+ }
+ });
+
+ Self.downloadImages = async() => {
+ const models = Self.app.models;
+
+ try {
+ const imageQueue = await Self.find({limit: 25});
+ const rootPath = models.Image.getPath();
+ const tempPath = path.join(rootPath, 'temp');
+
+ // Create temporary path
+ await fs.mkdir(tempPath, {recursive: true});
+
+ for (let image of imageQueue) {
+ const fileName = `${image.itemFk}.png`;
+ const filePath = path.join(tempPath, fileName);
+ const file = fs.createWriteStream(filePath);
+
+ https.get(image.url, async response => {
+ response.pipe(file);
+ });
+
+ file.on('finish', async function() {
+ await models.Image.registerImage('catalog', fileName, filePath);
+ await image.destroy();
+ });
+
+ file.on('error', err => {
+ fs.unlink(filePath);
+
+ throw err;
+ });
+ }
+
+ return imageQueue;
+ } catch (e) {
+ throw e;
+ }
+ };
+};
diff --git a/modules/item/back/model-config.json b/modules/item/back/model-config.json
index c085e075a..c5623dcca 100644
--- a/modules/item/back/model-config.json
+++ b/modules/item/back/model-config.json
@@ -50,6 +50,9 @@
"ItemShelvingSale": {
"dataSource": "vn"
},
+ "ItemImageQueue": {
+ "dataSource": "vn"
+ },
"Origin": {
"dataSource": "vn"
},
diff --git a/modules/item/back/models/item-image-queue.js b/modules/item/back/models/item-image-queue.js
new file mode 100644
index 000000000..e2059ddac
--- /dev/null
+++ b/modules/item/back/models/item-image-queue.js
@@ -0,0 +1,3 @@
+module.exports = Self => {
+ require('../methods/item-image-queue/downloadImages')(Self);
+};
diff --git a/modules/item/back/models/item-image-queue.json b/modules/item/back/models/item-image-queue.json
new file mode 100644
index 000000000..61cb7b018
--- /dev/null
+++ b/modules/item/back/models/item-image-queue.json
@@ -0,0 +1,36 @@
+{
+ "name": "ItemImageQueue",
+ "description": "Image download queue",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "itemImageQueue"
+ }
+ },
+ "properties": {
+ "itemFk": {
+ "type": "Number",
+ "id": true,
+ "description": "Identifier"
+ },
+ "url": {
+ "type": "String",
+ "required": true
+ }
+ },
+ "relations": {
+ "item": {
+ "type": "belongsTo",
+ "model": "Item",
+ "foreignKey": "itemFk"
+ }
+ },
+ "acls": [
+ {
+ "accessType": "*",
+ "principalType": "ROLE",
+ "principalId": "employee",
+ "permission": "ALLOW"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js
new file mode 100644
index 000000000..28a2b9d9d
--- /dev/null
+++ b/modules/worker/back/methods/worker/createAbsence.js
@@ -0,0 +1,58 @@
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('createAbsence', {
+ description: 'Creates a new worker absence',
+ accepts: [{
+ arg: 'id',
+ type: 'Number',
+ description: 'The worker id',
+ http: {source: 'path'}
+ },
+ {
+ arg: 'absenceTypeId',
+ type: 'Number',
+ required: true
+ },
+ {
+ arg: 'dated',
+ type: 'Date',
+ required: false
+ }],
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/:id/createAbsence`,
+ verb: 'POST'
+ }
+ });
+
+ Self.createAbsence = async(ctx, id, absenceTypeId, dated) => {
+ const models = Self.app.models;
+ const userId = ctx.req.accessToken.userId;
+ const isSubordinate = await models.Worker.isSubordinate(ctx, id);
+ const isTeamBoss = await models.Account.hasRole(userId, 'teamBoss');
+
+ if (!isSubordinate || (isSubordinate && userId == id && !isTeamBoss))
+ throw new UserError(`You don't have enough privileges`);
+
+ const labour = await models.WorkerLabour.findOne({
+ where: {
+ and: [
+ {workerFk: id},
+ {or: [{
+ ended: {gte: [dated]}
+ }, {ended: null}]}
+ ]
+ }
+ });
+
+ return models.WorkerCalendar.create({
+ businessFk: labour.businessFk,
+ absenceTypeFk: absenceTypeId,
+ dated: dated
+ });
+ };
+};
diff --git a/modules/worker/back/methods/worker/deleteAbsence.js b/modules/worker/back/methods/worker/deleteAbsence.js
new file mode 100644
index 000000000..ea156d7eb
--- /dev/null
+++ b/modules/worker/back/methods/worker/deleteAbsence.js
@@ -0,0 +1,37 @@
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('deleteAbsence', {
+ description: 'Deletes a worker absence',
+ accepts: [{
+ arg: 'id',
+ type: 'Number',
+ description: 'The worker id',
+ http: {source: 'path'}
+ },
+ {
+ arg: 'absenceId',
+ type: 'Number',
+ required: true
+ }],
+ returns: 'Object',
+ http: {
+ path: `/:id/deleteAbsence`,
+ verb: 'DELETE'
+ }
+ });
+
+ Self.deleteAbsence = async(ctx, id, absenceId) => {
+ const models = Self.app.models;
+ const userId = ctx.req.accessToken.userId;
+ const isSubordinate = await models.Worker.isSubordinate(ctx, id);
+ const isTeamBoss = await models.Account.hasRole(userId, 'teamBoss');
+
+ if (!isSubordinate || (isSubordinate && userId == id && !isTeamBoss))
+ throw new UserError(`You don't have enough privileges`);
+
+ const absence = await models.WorkerCalendar.findById(absenceId);
+
+ return absence.destroy();
+ };
+};
diff --git a/modules/worker/back/methods/worker/specs/createAbsence.spec.js b/modules/worker/back/methods/worker/specs/createAbsence.spec.js
new file mode 100644
index 000000000..33bc2a80e
--- /dev/null
+++ b/modules/worker/back/methods/worker/specs/createAbsence.spec.js
@@ -0,0 +1,39 @@
+const app = require('vn-loopback/server/server');
+
+describe('Worker createAbsence()', () => {
+ const workerId = 106;
+ let createdAbsence;
+
+ afterAll(async() => {
+ const absence = await app.models.WorkerCalendar.findById(createdAbsence.id);
+ await absence.destroy();
+ });
+
+ it('should return an error for a user without enough privileges', async() => {
+ const ctx = {req: {accessToken: {userId: 106}}};
+ const absenceTypeId = 1;
+ const dated = new Date();
+
+ let error;
+ await app.models.Worker.createAbsence(ctx, workerId, absenceTypeId, dated).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`You don't have enough privileges`);
+ });
+
+ expect(error).toBeDefined();
+ });
+
+ it('should create a new absence', async() => {
+ const ctx = {req: {accessToken: {userId: 37}}};
+ const absenceTypeId = 1;
+ const dated = new Date();
+ createdAbsence = await app.models.Worker.createAbsence(ctx, workerId, absenceTypeId, dated);
+
+ const expectedBusinessId = 106;
+ const expectedAbsenceTypeId = 1;
+
+ expect(createdAbsence.businessFk).toEqual(expectedBusinessId);
+ expect(createdAbsence.absenceTypeFk).toEqual(expectedAbsenceTypeId);
+ });
+});
diff --git a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js
new file mode 100644
index 000000000..c506ae86d
--- /dev/null
+++ b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js
@@ -0,0 +1,38 @@
+const app = require('vn-loopback/server/server');
+
+describe('Worker deleteAbsence()', () => {
+ const workerId = 106;
+ let createdAbsence;
+
+ it('should return an error for a user without enough privileges', async() => {
+ const ctx = {req: {accessToken: {userId: 106}}};
+ const businessId = 106;
+ createdAbsence = await app.models.WorkerCalendar.create({
+ businessFk: businessId,
+ absenceTypeFk: 1,
+ dated: new Date()
+ });
+
+ let error;
+ await app.models.Worker.deleteAbsence(ctx, workerId, createdAbsence.id).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`You don't have enough privileges`);
+ });
+
+ expect(error).toBeDefined();
+ });
+
+ it('should create a new absence', async() => {
+ const ctx = {req: {accessToken: {userId: 37}}};
+ const businessId = 106;
+
+ expect(createdAbsence.businessFk).toEqual(businessId);
+
+ await app.models.Worker.deleteAbsence(ctx, workerId, createdAbsence.id);
+
+ const deletedAbsence = await app.models.WorkerCalendar.findById(createdAbsence.id);
+
+ expect(deletedAbsence).toBeNull();
+ });
+});
diff --git a/modules/worker/back/methods/worker/specs/updateAbsence.spec.js b/modules/worker/back/methods/worker/specs/updateAbsence.spec.js
new file mode 100644
index 000000000..689d36136
--- /dev/null
+++ b/modules/worker/back/methods/worker/specs/updateAbsence.spec.js
@@ -0,0 +1,38 @@
+const app = require('vn-loopback/server/server');
+
+describe('Worker updateAbsence()', () => {
+ const workerId = 106;
+ let createdAbsence;
+
+ afterAll(async() => {
+ const absence = await app.models.WorkerCalendar.findById(createdAbsence.id);
+ await absence.destroy();
+ });
+
+ it('should return an error for a user without enough privileges', async() => {
+ const ctx = {req: {accessToken: {userId: 106}}};
+ const expectedAbsenceTypeId = 2;
+ createdAbsence = await app.models.WorkerCalendar.create({
+ businessFk: 106,
+ absenceTypeFk: 1,
+ dated: new Date()
+ });
+
+ let error;
+ await app.models.Worker.updateAbsence(ctx, workerId, createdAbsence.id, expectedAbsenceTypeId).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`You don't have enough privileges`);
+ });
+
+ expect(error).toBeDefined();
+ });
+
+ it('should create a new absence', async() => {
+ const ctx = {req: {accessToken: {userId: 37}}};
+ const expectedAbsenceTypeId = 2;
+ const updatedAbsence = await app.models.Worker.updateAbsence(ctx, workerId, createdAbsence.id, expectedAbsenceTypeId);
+
+ expect(updatedAbsence.absenceTypeFk).toEqual(expectedAbsenceTypeId);
+ });
+});
diff --git a/modules/worker/back/methods/worker/updateAbsence.js b/modules/worker/back/methods/worker/updateAbsence.js
new file mode 100644
index 000000000..719bca7e4
--- /dev/null
+++ b/modules/worker/back/methods/worker/updateAbsence.js
@@ -0,0 +1,42 @@
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('updateAbsence', {
+ description: 'Updates a worker absence',
+ accepts: [{
+ arg: 'id',
+ type: 'Number',
+ description: 'The worker id',
+ http: {source: 'path'}
+ },
+ {
+ arg: 'absenceId',
+ type: 'Number',
+ required: true
+ },
+ {
+ arg: 'absenceTypeId',
+ type: 'Number',
+ required: true
+ }],
+ returns: 'Object',
+ http: {
+ path: `/:id/updateAbsence`,
+ verb: 'PATCH'
+ }
+ });
+
+ Self.updateAbsence = async(ctx, id, absenceId, absenceTypeId) => {
+ const models = Self.app.models;
+ const userId = ctx.req.accessToken.userId;
+ const isSubordinate = await models.Worker.isSubordinate(ctx, id);
+ const isTeamBoss = await models.Account.hasRole(userId, 'teamBoss');
+
+ if (!isSubordinate || (isSubordinate && userId == id && !isTeamBoss))
+ throw new UserError(`You don't have enough privileges`);
+
+ const absence = await models.WorkerCalendar.findById(absenceId);
+
+ return absence.updateAttribute('absenceTypeFk', absenceTypeId);
+ };
+};
diff --git a/modules/worker/back/models/worker-calendar.json b/modules/worker/back/models/worker-calendar.json
index 569d4d1ba..ca802caa7 100644
--- a/modules/worker/back/models/worker-calendar.json
+++ b/modules/worker/back/models/worker-calendar.json
@@ -3,29 +3,22 @@
"base": "VnModel",
"options": {
"mysql": {
- "table": "workerCalendar"
+ "table": "workerCalendar2"
}
},
"properties": {
- "businessFk": {
- "id": 1,
+ "id": {
+ "id": true,
"type": "Number"
},
- "workerFk": {
- "id": 2,
+ "businessFk": {
"type": "Number"
},
"dated": {
- "id": 3,
"type": "Date"
}
},
"relations": {
- "worker": {
- "type": "belongsTo",
- "model": "Worker",
- "foreignKey": "workerFk"
- },
"absenceType": {
"type": "belongsTo",
"model": "AbsenceType",
diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js
index 692c8c735..0d94c788e 100644
--- a/modules/worker/back/models/worker.js
+++ b/modules/worker/back/models/worker.js
@@ -4,4 +4,7 @@ module.exports = Self => {
require('../methods/worker/isSubordinate')(Self);
require('../methods/worker/getWorkedHours')(Self);
require('../methods/worker/uploadFile')(Self);
+ require('../methods/worker/createAbsence')(Self);
+ require('../methods/worker/deleteAbsence')(Self);
+ require('../methods/worker/updateAbsence')(Self);
};
diff --git a/modules/worker/front/calendar/index.html b/modules/worker/front/calendar/index.html
index 08b3f469c..197fb0797 100644
--- a/modules/worker/front/calendar/index.html
+++ b/modules/worker/front/calendar/index.html
@@ -5,15 +5,19 @@
-
-
+
+
+
+
@@ -26,12 +30,19 @@
-
+
+
{{absenceType.name}}
-
\ No newline at end of file
+
+
+
diff --git a/modules/worker/front/calendar/index.js b/modules/worker/front/calendar/index.js
index 6b849d19f..32adc272d 100644
--- a/modules/worker/front/calendar/index.js
+++ b/modules/worker/front/calendar/index.js
@@ -43,15 +43,17 @@ class Controller extends Section {
set worker(value) {
this._worker = value;
- if (!value) return;
- let params = {
- workerFk: this.worker.id,
- started: this.started,
- ended: this.ended
- };
- this.$http.get(`WorkerCalendars/absences`, {params})
- .then(res => this.onData(res.data));
+ if (value) {
+ this.refresh().then(() => this.repaint());
+ this.getIsSubordinate();
+ }
+ }
+
+ getIsSubordinate() {
+ this.$http.get(`Workers/${this.worker.id}/isSubordinate`).then(res =>
+ this.isSubordinate = res.data
+ );
}
onData(data) {
@@ -79,12 +81,12 @@ class Controller extends Section {
let type = absence.absenceType;
addEvent(absence.dated, {
name: type.name,
- color: type.rgb
+ color: type.rgb,
+ type: type.code,
+ absenceId: absence.id
});
});
}
-
- this.repaint();
}
repaint() {
@@ -102,6 +104,105 @@ class Controller extends Section {
dayNumber.style.backgroundColor = event.color;
dayNumber.style.color = 'rgba(0, 0, 0, 0.7)';
}
+
+ pick(absenceType) {
+ if (!this.isSubordinate) return;
+ if (absenceType == this.absenceType)
+ absenceType = null;
+
+ this.absenceType = absenceType;
+ }
+
+ onSelection($event, $days) {
+ if (!this.absenceType)
+ return this.vnApp.showMessage(this.$t('Choose an absence type from the right menu'));
+
+ const day = $days[0];
+ const stamp = day.getTime();
+ const event = this.events[stamp];
+ const calendar = $event.target.closest('vn-calendar').$ctrl;
+
+ if (event) {
+ if (event.type == this.absenceType.code)
+ this.delete(calendar, day, event);
+ else
+ this.edit(calendar, event);
+ } else
+ this.create(calendar, day);
+ }
+
+ create(calendar, dated) {
+ const absenceType = this.absenceType;
+ const params = {
+ dated: dated,
+ absenceTypeId: absenceType.id
+ };
+
+ const path = `Workers/${this.$params.id}/createAbsence`;
+ this.$http.post(path, params).then(res => {
+ const newEvent = res.data;
+ this.events[dated.getTime()] = {
+ name: absenceType.name,
+ color: absenceType.rgb,
+ type: absenceType.code,
+ absenceId: newEvent.id
+ };
+
+ this.repaintCanceller(() =>
+ this.refresh().then(calendar.repaint())
+ );
+ });
+ }
+
+ edit(calendar, event) {
+ const absenceType = this.absenceType;
+ const params = {
+ absenceId: event.absenceId,
+ absenceTypeId: absenceType.id
+ };
+ const path = `Workers/${this.$params.id}/updateAbsence`;
+ this.$http.patch(path, params).then(() => {
+ event.color = absenceType.rgb;
+ event.name = absenceType.name;
+ event.type = absenceType.code;
+
+ this.repaintCanceller(() =>
+ this.refresh().then(calendar.repaint())
+ );
+ });
+ }
+
+ delete(calendar, day, event) {
+ const params = {absenceId: event.absenceId};
+ const path = `Workers/${this.$params.id}/deleteAbsence`;
+ this.$http.delete(path, {params}).then(() => {
+ delete this.events[day.getTime()];
+
+ this.repaintCanceller(() =>
+ this.refresh().then(calendar.repaint())
+ );
+ });
+ }
+
+ repaintCanceller(cb) {
+ if (this.canceller) {
+ clearTimeout(this.canceller);
+ this.canceller = null;
+ }
+
+ this.canceller = setTimeout(
+ () => cb(), 500);
+ }
+
+ refresh() {
+ const params = {
+ workerFk: this.worker.id,
+ started: this.started,
+ ended: this.ended
+ };
+ return this.$http.get(`WorkerCalendars/absences`, {params})
+ .then(res => this.onData(res.data));
+ }
}
ngModule.component('vnWorkerCalendar', {
diff --git a/modules/worker/front/calendar/index.spec.js b/modules/worker/front/calendar/index.spec.js
index da49b8f0f..9d14cca20 100644
--- a/modules/worker/front/calendar/index.spec.js
+++ b/modules/worker/front/calendar/index.spec.js
@@ -3,17 +3,23 @@ import './index';
describe('Worker', () => {
describe('Component vnWorkerCalendar', () => {
let $httpBackend;
+ let $httpParamSerializer;
let $scope;
let controller;
let year = new Date().getFullYear();
beforeEach(ngModule('worker'));
- beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => {
+ beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpParamSerializer_, _$httpBackend_) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
- const $element = angular.element('');
+ $httpParamSerializer = _$httpParamSerializer_;
+ const $element = angular.element('');
controller = $componentController('vnWorkerCalendar', {$element, $scope});
+ controller.isSubordinate = true;
+ controller.absenceType = {id: 1, name: 'Holiday', code: 'holiday', rgb: 'red'};
+ controller.$params.id = 106;
+ controller._worker = {id: 106};
}));
describe('started property', () => {
@@ -44,8 +50,9 @@ describe('Worker', () => {
describe('worker() setter', () => {
it(`should perform a get query and set the reponse data on the model`, () => {
- let today = new Date();
+ jest.spyOn(controller, 'getIsSubordinate').mockReturnValue(true);
+ let today = new Date();
let tomorrow = new Date(today.getTime());
tomorrow.setDate(tomorrow.getDate() + 1);
@@ -64,7 +71,7 @@ describe('Worker', () => {
]
});
- controller.worker = {id: 1};
+ controller.worker = {id: 107};
$httpBackend.flush();
let events = controller.events;
@@ -73,11 +80,14 @@ describe('Worker', () => {
expect(events[tomorrow.getTime()].name).toEqual('Easter');
expect(events[yesterday.getTime()].name).toEqual('Leave');
expect(events[yesterday.getTime()].color).toEqual('#bbb');
+ expect(controller.getIsSubordinate).toHaveBeenCalledWith();
});
});
describe('formatDay()', () => {
it(`should set the day element style`, () => {
+ jest.spyOn(controller, 'getIsSubordinate').mockReturnThis();
+
let today = new Date();
$httpBackend.whenRoute('GET', 'WorkerCalendars/absences')
@@ -99,5 +109,219 @@ describe('Worker', () => {
expect(dayNumber.style.backgroundColor).toEqual('rgb(0, 0, 0)');
});
});
+
+ describe('pick()', () => {
+ it(`should set the absenceType property to null if they match with the current one`, () => {
+ const absenceType = {id: 1, name: 'Holiday'};
+ controller.absenceType = absenceType;
+ controller.pick(absenceType);
+
+ expect(controller.absenceType).toBeNull();
+ });
+
+ it(`should set the absenceType property`, () => {
+ const absenceType = {id: 1, name: 'Holiday'};
+ const expectedAbsence = {id: 2, name: 'Leave of absence'};
+ controller.absenceType = absenceType;
+ controller.pick(expectedAbsence);
+
+ expect(controller.absenceType).toEqual(expectedAbsence);
+ });
+ });
+
+ describe('onSelection()', () => {
+ it(`should show an snackbar message if no absence type is selected`, () => {
+ jest.spyOn(controller.vnApp, 'showMessage').mockReturnThis();
+
+ const $event = {};
+ const $days = [];
+ controller.absenceType = null;
+ controller.onSelection($event, $days);
+
+ expect(controller.vnApp.showMessage).toHaveBeenCalledWith('Choose an absence type from the right menu');
+ });
+
+ it(`should call to the create() method`, () => {
+ jest.spyOn(controller, 'create').mockReturnThis();
+
+ const selectedDay = new Date();
+ const $event = {
+ target: {
+ closest: () => {
+ return {$ctrl: {}};
+ }
+ }
+ };
+ const $days = [selectedDay];
+ controller.absenceType = {id: 1};
+ controller.onSelection($event, $days);
+
+ expect(controller.create).toHaveBeenCalledWith(jasmine.any(Object), selectedDay);
+ });
+
+ it(`should call to the delete() method`, () => {
+ jest.spyOn(controller, 'delete').mockReturnThis();
+
+ const selectedDay = new Date();
+ const expectedEvent = {
+ dated: selectedDay,
+ type: 'holiday'
+ };
+ const $event = {
+ target: {
+ closest: () => {
+ return {$ctrl: {}};
+ }
+ }
+ };
+ const $days = [selectedDay];
+ controller.events[selectedDay.getTime()] = expectedEvent;
+ controller.absenceType = {id: 1, code: 'holiday'};
+ controller.onSelection($event, $days);
+
+ expect(controller.delete).toHaveBeenCalledWith(jasmine.any(Object), selectedDay, expectedEvent);
+ });
+
+ it(`should call to the edit() method`, () => {
+ jest.spyOn(controller, 'edit').mockReturnThis();
+
+ const selectedDay = new Date();
+ const expectedEvent = {
+ dated: selectedDay,
+ type: 'leaveOfAbsence'
+ };
+ const $event = {
+ target: {
+ closest: () => {
+ return {$ctrl: {}};
+ }
+ }
+ };
+ const $days = [selectedDay];
+ controller.events[selectedDay.getTime()] = expectedEvent;
+ controller.absenceType = {id: 1, code: 'holiday'};
+ controller.onSelection($event, $days);
+
+ expect(controller.edit).toHaveBeenCalledWith(jasmine.any(Object), expectedEvent);
+ });
+ });
+
+ describe('create()', () => {
+ it(`should make a HTTP POST query and then call to the repaintCanceller() method`, () => {
+ jest.spyOn(controller, 'repaintCanceller').mockReturnThis();
+
+ const dated = new Date();
+ const calendarElement = {};
+ const expectedResponse = {id: 10};
+
+ $httpBackend.expect('POST', `Workers/106/createAbsence`).respond(200, expectedResponse);
+ controller.create(calendarElement, dated);
+ $httpBackend.flush();
+
+ const createdEvent = controller.events[dated.getTime()];
+ const absenceType = controller.absenceType;
+
+ expect(createdEvent.absenceId).toEqual(expectedResponse.id);
+ expect(createdEvent.color).toEqual(absenceType.rgb);
+ expect(controller.repaintCanceller).toHaveBeenCalled();
+ });
+ });
+
+ describe('edit()', () => {
+ it(`should make a HTTP PATCH query and then call to the repaintCanceller() method`, () => {
+ jest.spyOn(controller, 'repaintCanceller').mockReturnThis();
+
+ const event = {absenceId: 10};
+ const calendarElement = {};
+ const newAbsenceType = {
+ id: 2,
+ name: 'Leave of absence',
+ code: 'leaveOfAbsence',
+ rgb: 'purple'
+ };
+ controller.absenceType = newAbsenceType;
+
+ const expectedParams = {absenceId: 10, absenceTypeId: 2};
+ $httpBackend.expect('PATCH', `Workers/106/updateAbsence`, expectedParams).respond(200);
+ controller.edit(calendarElement, event);
+ $httpBackend.flush();
+
+ expect(event.name).toEqual(newAbsenceType.name);
+ expect(event.color).toEqual(newAbsenceType.rgb);
+ expect(event.type).toEqual(newAbsenceType.code);
+ expect(controller.repaintCanceller).toHaveBeenCalled();
+ });
+ });
+
+ describe('delete()', () => {
+ it(`should make a HTTP DELETE query and then call to the repaintCanceller() method`, () => {
+ jest.spyOn(controller, 'repaintCanceller').mockReturnThis();
+
+ const expectedParams = {absenceId: 10};
+ const calendarElement = {};
+ const selectedDay = new Date();
+ const expectedEvent = {
+ dated: selectedDay,
+ type: 'leaveOfAbsence',
+ absenceId: 10
+ };
+
+ controller.events[selectedDay.getTime()] = expectedEvent;
+
+ const serializedParams = $httpParamSerializer(expectedParams);
+ $httpBackend.expect('DELETE', `Workers/106/deleteAbsence?${serializedParams}`).respond(200);
+ controller.delete(calendarElement, selectedDay, expectedEvent);
+ $httpBackend.flush();
+
+ const event = controller.events[selectedDay.getTime()];
+
+ expect(event).toBeUndefined();
+ expect(controller.repaintCanceller).toHaveBeenCalled();
+ });
+ });
+
+ describe('repaintCanceller()', () => {
+ it(`should cancell the callback execution timer`, () => {
+ jest.spyOn(window, 'clearTimeout');
+ jest.spyOn(window, 'setTimeout');
+
+ const timeoutId = 90;
+ controller.canceller = timeoutId;
+
+ controller.repaintCanceller(() => {
+ return 'My callback';
+ });
+
+ expect(window.clearTimeout).toHaveBeenCalledWith(timeoutId);
+ expect(window.setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 500);
+ });
+ });
+
+ describe('refresh()', () => {
+ it(`should make a HTTP GET query and then call to the onData() method`, () => {
+ jest.spyOn(controller, 'onData').mockReturnThis();
+
+ const dated = controller.date;
+ const started = new Date(dated.getTime());
+ started.setMonth(0);
+ started.setDate(1);
+
+ const ended = new Date(dated.getTime());
+ ended.setMonth(12);
+ ended.setDate(0);
+
+ controller.started = started;
+ controller.ended = ended;
+
+ const expecteResponse = [{id: 1}];
+ const expectedParams = {workerFk: 106, started: started, ended: ended};
+ const serializedParams = $httpParamSerializer(expectedParams);
+ $httpBackend.expect('GET', `WorkerCalendars/absences?${serializedParams}`).respond(200, expecteResponse);
+ controller.refresh();
+ $httpBackend.flush();
+
+ expect(controller.onData).toHaveBeenCalledWith(expecteResponse);
+ });
+ });
});
});
diff --git a/modules/worker/front/calendar/locale/es.yml b/modules/worker/front/calendar/locale/es.yml
index 82939ce91..6681f730f 100644
--- a/modules/worker/front/calendar/locale/es.yml
+++ b/modules/worker/front/calendar/locale/es.yml
@@ -2,4 +2,6 @@ Calendar: Calendario
Holidays: Vacaciones
Used: Utilizados
of: de
-days: días
\ No newline at end of file
+days: días
+Choose an absence type from the right menu: Elige un tipo de ausencia desde el menú de la derecha
+To start adding absences, click an absence type from the right menu and then on the day you want to add an absence: Para empezar a añadir ausencias, haz clic en un tipo de ausencia desde el menu de la derecha y después en el día que quieres añadir la ausencia
\ No newline at end of file
diff --git a/modules/worker/front/calendar/style.scss b/modules/worker/front/calendar/style.scss
index 9b3fc749b..1934a6ca0 100644
--- a/modules/worker/front/calendar/style.scss
+++ b/modules/worker/front/calendar/style.scss
@@ -2,6 +2,7 @@
vn-worker-calendar {
.calendars {
+ position: relative;
display: flex;
flex-wrap: wrap;
justify-content: center;
@@ -16,4 +17,23 @@ vn-worker-calendar {
max-width: 288px;
}
}
+
+ vn-chip.selectable {
+ cursor: pointer
+ }
+
+ vn-chip.selectable:hover {
+ opacity: 0.8
+ }
+
+ vn-chip vn-avatar {
+ text-align: center;
+ color: white
+ }
+
+ vn-icon[icon="info"] {
+ position: absolute;
+ top: 16px;
+ right: 16px
+ }
}
diff --git a/package-lock.json b/package-lock.json
index 5e4ac6c46..92030199a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4690,8 +4690,7 @@
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
- "dev": true
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
},
"ansi-styles": {
"version": "3.2.1",
@@ -4741,8 +4740,7 @@
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
- "dev": true
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"archy": {
"version": "1.0.0",
@@ -4754,7 +4752,6 @@
"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==",
- "dev": true,
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
@@ -6610,8 +6607,7 @@
"chownr": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
- "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==",
- "dev": true
+ "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw=="
},
"chrome-trace-event": {
"version": "1.0.2",
@@ -6820,8 +6816,7 @@
"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=",
- "dev": true
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"collect-v8-coverage": {
"version": "1.0.1",
@@ -6850,11 +6845,19 @@
"object-visit": "^1.0.0"
}
},
+ "color": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
+ "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
+ "requires": {
+ "color-convert": "^1.9.1",
+ "color-string": "^1.5.2"
+ }
+ },
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
"requires": {
"color-name": "1.1.3"
}
@@ -6862,8 +6865,16 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
- "dev": true
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+ },
+ "color-string": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
+ "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
+ "requires": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
},
"color-support": {
"version": "1.1.3",
@@ -7011,8 +7022,7 @@
"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=",
- "dev": true
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"consolidate": {
"version": "0.15.1",
@@ -7563,8 +7573,7 @@
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
- "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
- "dev": true
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"denque": {
"version": "1.4.1",
@@ -7604,6 +7613,11 @@
"integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
"dev": true
},
+ "detect-libc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
+ },
"detect-newline": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
@@ -8369,6 +8383,11 @@
}
}
},
+ "expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
+ },
"expand-tilde": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
@@ -9150,6 +9169,11 @@
"readable-stream": "^2.0.0"
}
},
+ "fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+ },
"fs-extra": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
@@ -9160,6 +9184,14 @@
"universalify": "^0.1.0"
}
},
+ "fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
"fs-mkdirp-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
@@ -9798,7 +9830,6 @@
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
- "dev": true,
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
@@ -9813,14 +9844,12 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"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=",
- "dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -9829,7 +9858,6 @@
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -9840,7 +9868,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -9948,6 +9975,11 @@
"assert-plus": "^1.0.0"
}
},
+ "github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
+ },
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -11293,8 +11325,7 @@
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
- "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
- "dev": true
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"has-value": {
"version": "1.0.0",
@@ -11897,8 +11928,7 @@
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
- "dev": true
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
"inquirer": {
"version": "6.4.1",
@@ -12137,8 +12167,7 @@
"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=",
- "dev": true
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"is-generator-fn": {
"version": "2.1.0",
@@ -18662,6 +18691,37 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
+ "minipass": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+ "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
+ "requires": {
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ }
+ }
+ },
+ "minizlib": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz",
+ "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==",
+ "requires": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ }
+ }
+ },
"mississippi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
@@ -18739,6 +18799,11 @@
"minimist": "^1.2.5"
}
},
+ "mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
+ },
"mktmpdir": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/mktmpdir/-/mktmpdir-0.1.1.tgz",
@@ -19079,6 +19144,11 @@
"to-regex": "^3.0.1"
}
},
+ "napi-build-utils": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
+ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
+ },
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -19121,6 +19191,19 @@
"resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz",
"integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q=="
},
+ "node-abi": {
+ "version": "2.18.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.18.0.tgz",
+ "integrity": "sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw==",
+ "requires": {
+ "semver": "^5.4.1"
+ }
+ },
+ "node-addon-api": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz",
+ "integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg=="
+ },
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
@@ -19507,6 +19590,11 @@
}
}
},
+ "noop-logger": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
+ "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
+ },
"nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -19561,7 +19649,6 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
- "dev": true,
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
@@ -19581,8 +19668,7 @@
"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=",
- "dev": true
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"nwsapi": {
"version": "2.2.0",
@@ -19598,8 +19684,7 @@
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
- "dev": true
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-copy": {
"version": "0.1.0",
@@ -20381,6 +20466,53 @@
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
"dev": true
},
+ "prebuild-install": {
+ "version": "5.3.5",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.5.tgz",
+ "integrity": "sha512-YmMO7dph9CYKi5IR/BzjOJlRzpxGGVo1EsLSUZ0mt/Mq0HWZIHOKHHcHdT69yG54C9m6i45GpItwRHpk0Py7Uw==",
+ "requires": {
+ "detect-libc": "^1.0.3",
+ "expand-template": "^2.0.3",
+ "github-from-package": "0.0.0",
+ "minimist": "^1.2.3",
+ "mkdirp": "^0.5.1",
+ "napi-build-utils": "^1.0.1",
+ "node-abi": "^2.7.0",
+ "noop-logger": "^0.1.1",
+ "npmlog": "^4.0.1",
+ "pump": "^3.0.0",
+ "rc": "^1.2.7",
+ "simple-get": "^3.0.3",
+ "tar-fs": "^2.0.0",
+ "tunnel-agent": "^0.6.0",
+ "which-pm-runs": "^1.0.0"
+ },
+ "dependencies": {
+ "decompress-response": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
+ "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
+ "requires": {
+ "mimic-response": "^2.0.0"
+ }
+ },
+ "mimic-response": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
+ "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
+ },
+ "simple-get": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
+ "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
+ "requires": {
+ "decompress-response": "^4.2.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ }
+ }
+ }
+ },
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@@ -20729,7 +20861,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "dev": true,
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
@@ -20740,8 +20871,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
}
}
},
@@ -21723,8 +21853,7 @@
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
- "dev": true
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"set-value": {
"version": "2.0.1",
@@ -21779,6 +21908,57 @@
"kind-of": "^6.0.2"
}
},
+ "sharp": {
+ "version": "0.25.4",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.25.4.tgz",
+ "integrity": "sha512-umSzJJ1oBwIOfwFFt/fJ7JgCva9FvrEU2cbbm7u/3hSDZhXvkME8WE5qpaJqLIe2Har5msF5UG4CzYlEg5o3BQ==",
+ "requires": {
+ "color": "^3.1.2",
+ "detect-libc": "^1.0.3",
+ "node-addon-api": "^3.0.0",
+ "npmlog": "^4.1.2",
+ "prebuild-install": "^5.3.4",
+ "semver": "^7.3.2",
+ "simple-get": "^4.0.0",
+ "tar": "^6.0.2",
+ "tunnel-agent": "^0.6.0"
+ },
+ "dependencies": {
+ "chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
+ },
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
+ },
+ "semver": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+ "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
+ },
+ "tar": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.2.tgz",
+ "integrity": "sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg==",
+ "requires": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^3.0.0",
+ "minizlib": "^2.1.0",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ }
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ }
+ }
+ },
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@@ -21822,6 +22002,51 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
+ "simple-concat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
+ "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
+ },
+ "simple-get": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.0.tgz",
+ "integrity": "sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ==",
+ "requires": {
+ "decompress-response": "^6.0.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ },
+ "dependencies": {
+ "decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "requires": {
+ "mimic-response": "^3.1.0"
+ }
+ },
+ "mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+ }
+ }
+ },
+ "simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+ "requires": {
+ "is-arrayish": "^0.3.1"
+ },
+ "dependencies": {
+ "is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+ }
+ }
+ },
"sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@@ -22442,7 +22667,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
- "dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
@@ -22460,7 +22684,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
@@ -22496,8 +22719,7 @@
"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=",
- "dev": true
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"strong-error-handler": {
"version": "2.3.2",
@@ -23080,6 +23302,60 @@
"inherits": "2"
}
},
+ "tar-fs": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz",
+ "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==",
+ "requires": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.0.0"
+ }
+ },
+ "tar-stream": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz",
+ "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==",
+ "requires": {
+ "bl": "^4.0.1",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "dependencies": {
+ "bl": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
+ "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
+ "requires": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "buffer": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
+ "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
"teeny-request": {
"version": "3.11.3",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-3.11.3.tgz",
@@ -25048,11 +25324,15 @@
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
"dev": true
},
+ "which-pm-runs": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
+ "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
+ },
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
- "dev": true,
"requires": {
"string-width": "^1.0.2 || 2"
}
diff --git a/package.json b/package.json
index 61dff1678..b7cca0364 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"request": "^2.88.0",
"request-promise-native": "^1.0.8",
"require-yaml": "0.0.1",
+ "sharp": "^0.25.4",
"soap": "^0.26.0",
"strong-error-handler": "^2.3.2",
"uuid": "^3.3.3",