Merge pull request #274 from NuSkooler/qwk-support

Qwk support
This commit is contained in:
Bryan Ashby 2020-05-02 18:05:16 -06:00 committed by GitHub
commit 6e0f47b1e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1881 additions and 16 deletions

View File

@ -204,23 +204,37 @@ module.exports = class ArchiveUtil {
}); });
} }
compressTo(archType, archivePath, files, cb) { compressTo(archType, archivePath, files, workDir, cb) {
const archiver = this.getArchiver(archType, paths.extname(archivePath)); const archiver = this.getArchiver(archType, paths.extname(archivePath));
if(!archiver) { if(!archiver) {
return cb(Errors.Invalid(`Unknown archive type: ${archType}`)); return cb(Errors.Invalid(`Unknown archive type: ${archType}`));
} }
if (!cb && _.isFunction(workDir)) {
cb = workDir;
workDir = null;
}
const fmtObj = { const fmtObj = {
archivePath : archivePath, archivePath : archivePath,
fileList : files.join(' '), // :TODO: probably need same hack as extractTo here! fileList : files.join(' '), // :TODO: probably need same hack as extractTo here!
}; };
const args = archiver.compress.args.map( arg => stringFormat(arg, fmtObj) ); // :TODO: DRY with extractTo()
const args = archiver.compress.args.map( arg => {
return '{fileList}' === arg ? arg : stringFormat(arg, fmtObj);
});
const fileListPos = args.indexOf('{fileList}');
if(fileListPos > -1) {
// replace {fileList} with 0:n sep file list arguments
args.splice.apply(args, [fileListPos, 1].concat(files));
}
let proc; let proc;
try { try {
proc = pty.spawn(archiver.compress.cmd, args, this.getPtyOpts()); proc = pty.spawn(archiver.compress.cmd, args, this.getPtyOpts(workDir));
} catch(e) { } catch(e) {
return cb(Errors.ExternalProcess( return cb(Errors.ExternalProcess(
`Error spawning archiver process "${archiver.compress.cmd}" with args "${args.join(' ')}": ${e.message}`) `Error spawning archiver process "${archiver.compress.cmd}" with args "${args.join(' ')}": ${e.message}`)
@ -332,15 +346,15 @@ module.exports = class ArchiveUtil {
}); });
} }
getPtyOpts(extractPath) { getPtyOpts(cwd) {
const opts = { const opts = {
name : 'enigma-archiver', name : 'enigma-archiver',
cols : 80, cols : 80,
rows : 24, rows : 24,
env : process.env, env : process.env,
}; };
if(extractPath) { if(cwd) {
opts.cwd = extractPath; opts.cwd = cwd;
} }
// :TODO: set cwd to supplied temp path if not sepcific extract // :TODO: set cwd to supplied temp path if not sepcific extract
return opts; return opts;

55
core/cp437util.js Normal file
View File

@ -0,0 +1,55 @@
const CP437UnicodeTable = [
'\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006',
'\u0007', '\u0008', '\u0009', '\u000A', '\u000B', '\u000C', '\u000D',
'\u000E', '\u000F', '\u0010', '\u0011', '\u0012', '\u0013', '\u0014',
'\u0015', '\u0016', '\u0017', '\u0018', '\u0019', '\u001A', '\u001B',
'\u001C', '\u001D', '\u001E', '\u001F', '\u0020', '\u0021', '\u0022',
'\u0023', '\u0024', '\u0025', '\u0026', '\u0027', '\u0028', '\u0029',
'\u002A', '\u002B', '\u002C', '\u002D', '\u002E', '\u002F', '\u0030',
'\u0031', '\u0032', '\u0033', '\u0034', '\u0035', '\u0036', '\u0037',
'\u0038', '\u0039', '\u003A', '\u003B', '\u003C', '\u003D', '\u003E',
'\u003F', '\u0040', '\u0041', '\u0042', '\u0043', '\u0044', '\u0045',
'\u0046', '\u0047', '\u0048', '\u0049', '\u004A', '\u004B', '\u004C',
'\u004D', '\u004E', '\u004F', '\u0050', '\u0051', '\u0052', '\u0053',
'\u0054', '\u0055', '\u0056', '\u0057', '\u0058', '\u0059', '\u005A',
'\u005B', '\u005C', '\u005D', '\u005E', '\u005F', '\u0060', '\u0061',
'\u0062', '\u0063', '\u0064', '\u0065', '\u0066', '\u0067', '\u0068',
'\u0069', '\u006A', '\u006B', '\u006C', '\u006D', '\u006E', '\u006F',
'\u0070', '\u0071', '\u0072', '\u0073', '\u0074', '\u0075', '\u0076',
'\u0077', '\u0078', '\u0079', '\u007A', '\u007B', '\u007C', '\u007D',
'\u007E', '\u007F', '\u00C7', '\u00FC', '\u00E9', '\u00E2', '\u00E4',
'\u00E0', '\u00E5', '\u00E7', '\u00EA', '\u00EB', '\u00E8', '\u00EF',
'\u00EE', '\u00EC', '\u00C4', '\u00C5', '\u00C9', '\u00E6', '\u00C6',
'\u00F4', '\u00F6', '\u00F2', '\u00FB', '\u00F9', '\u00FF', '\u00D6',
'\u00DC', '\u00A2', '\u00A3', '\u00A5', '\u20A7', '\u0192', '\u00E1',
'\u00ED', '\u00F3', '\u00FA', '\u00F1', '\u00D1', '\u00AA', '\u00BA',
'\u00BF', '\u2310', '\u00AC', '\u00BD', '\u00BC', '\u00A1', '\u00AB',
'\u00BB', '\u2591', '\u2592', '\u2593', '\u2502', '\u2524', '\u2561',
'\u2562', '\u2556', '\u2555', '\u2563', '\u2551', '\u2557', '\u255D',
'\u255C', '\u255B', '\u2510', '\u2514', '\u2534', '\u252C', '\u251C',
'\u2500', '\u253C', '\u255E', '\u255F', '\u255A', '\u2554', '\u2569',
'\u2566', '\u2560', '\u2550', '\u256C', '\u2567', '\u2568', '\u2564',
'\u2565', '\u2559', '\u2558', '\u2552', '\u2553', '\u256B', '\u256A',
'\u2518', '\u250C', '\u2588', '\u2584', '\u258C', '\u2590', '\u2580',
'\u03B1', '\u00DF', '\u0393', '\u03C0', '\u03A3', '\u03C3', '\u00B5',
'\u03C4', '\u03A6', '\u0398', '\u03A9', '\u03B4', '\u221E', '\u03C6',
'\u03B5', '\u2229', '\u2261', '\u00B1', '\u2265', '\u2264', '\u2320',
'\u2321', '\u00F7', '\u2248', '\u00B0', '\u2219', '\u00B7', '\u221A',
'\u207F', '\u00B2', '\u25A0', '\u00A0'
];
const NonCP437EncodableRegExp = /[^\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u0020\u0021\u0022\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B\u003C\u003D\u003E\u003F\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004A\u004B\u004C\u004D\u004E\u004F\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057\u0058\u0059\u005A\u005B\u005C\u005D\u005E\u005F\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C\u006D\u006E\u006F\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078\u0079\u007A\u007B\u007C\u007D\u007E\u007F\u00C7\u00FC\u00E9\u00E2\u00E4\u00E0\u00E5\u00E7\u00EA\u00EB\u00E8\u00EF\u00EE\u00EC\u00C4\u00C5\u00C9\u00E6\u00C6\u00F4\u00F6\u00F2\u00FB\u00F9\u00FF\u00D6\u00DC\u00A2\u00A3\u00A5\u20A7\u0192\u00E1\u00ED\u00F3\u00FA\u00F1\u00D1\u00AA\u00BA\u00BF\u2310\u00AC\u00BD\u00BC\u00A1\u00AB\u00BB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\u00DF\u0393\u03C0\u03A3\u03C3\u00B5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\u00B1\u2265\u2264\u2320\u2321\u00F7\u2248\u00B0\u2219\u00B7\u221A\u207F\u00B2\u25A0\u00A0]/;
const isCP437Encodable = (s) => {
if (!s.length) {
return true;
}
return !NonCP437EncodableRegExp.test(s);
}
module.exports = {
CP437UnicodeTable,
isCP437Encodable,
}

59
core/mbf.js Normal file
View File

@ -0,0 +1,59 @@
const { Errors } = require('./enig_error');
//
// Utils for dealing with Microsoft Binary Format (MBF) used
// in various BASIC languages, etc.
//
// - https://en.wikipedia.org/wiki/Microsoft_Binary_Format
// - https://stackoverflow.com/questions/2268191/how-to-convert-from-ieee-python-float-to-microsoft-basic-float
//
// Number to 32bit MBF
numToMbf32 = (v) => {
const mbf = Buffer.alloc(4);
if (0 === v) {
return mbf;
}
const ieee = Buffer.alloc(4);
ieee.writeFloatLE(v, 0);
const sign = ieee[3] & 0x80;
let exp = (ieee[3] << 1) | (ieee[2] >> 7);
if (exp === 0xfe) {
throw Errors.Invalid(`${v} cannot be converted to mbf`);
}
exp += 2;
mbf[3] = exp;
mbf[2] = sign | (ieee[2] & 0x7f);
mbf[1] = ieee[1];
mbf[0] = ieee[0];
return mbf;
}
mbf32ToNum = (mbf) => {
if (0 === mbf[3]) {
return 0.0;
}
const ieee = Buffer.alloc(4);
const sign = mbf[2] & 0x80;
const exp = mbf[3] - 2;
ieee[3] = sign | (exp >> 1);
ieee[2] = (exp << 7) | (mbf[2] & 0x7f);
ieee[1] = mbf[1];
ieee[0] = mbf[0];
return ieee.readFloatLE(0);
}
module.exports = {
numToMbf32,
mbf32ToNum,
}

View File

@ -11,6 +11,9 @@ const {
sanitizeString, sanitizeString,
getISOTimestampString } = require('./database.js'); getISOTimestampString } = require('./database.js');
const { isCP437Encodable } = require('./cp437util');
const { containsNonLatinCodepoints } = require('./string_util');
const { const {
isAnsi, isFormattedLine, isAnsi, isFormattedLine,
splitTextAtTerms, splitTextAtTerms,
@ -49,7 +52,8 @@ const SYSTEM_META_NAMES = {
const ADDRESS_FLAVOR = { const ADDRESS_FLAVOR = {
Local : 'local', // local / non-remote addressing Local : 'local', // local / non-remote addressing
FTN : 'ftn', // FTN style FTN : 'ftn', // FTN style
Email : 'email', Email : 'email', // From email
QWK : 'qwk', // QWK packet
}; };
const STATE_FLAGS0 = { const STATE_FLAGS0 = {
@ -87,6 +91,13 @@ const FTN_PROPERTY_NAMES = {
FtnSeenBy : 'ftn_seen_by', // http://ftsc.org/docs/fts-0004.001 FtnSeenBy : 'ftn_seen_by', // http://ftsc.org/docs/fts-0004.001
}; };
const QWKPropertyNames = {
MessageNumber : 'qwk_msg_num',
MessageStatus : 'qwk_msg_status', // See http://wiki.synchro.net/ref:qwk for a decent list
ConferenceNumber : 'qwk_conf_num',
InReplyToNum : 'qwk_in_reply_to_num', // note that we prefer the 'InReplyToMsgId' kludge if available
};
// :TODO: this is a ugly hack due to bad variable names - clean it up & just _.camelCase(k)! // :TODO: this is a ugly hack due to bad variable names - clean it up & just _.camelCase(k)!
const MESSAGE_ROW_MAP = { const MESSAGE_ROW_MAP = {
reply_to_message_id : 'replyToMsgId', reply_to_message_id : 'replyToMsgId',
@ -137,6 +148,20 @@ module.exports = class Message {
return null !== _.get(this, 'meta.System.remote_from_user', null); return null !== _.get(this, 'meta.System.remote_from_user', null);
} }
isCP437Encodable() {
return isCP437Encodable(this.toUserName) &&
isCP437Encodable(this.fromUserName) &&
isCP437Encodable(this.subject) &&
isCP437Encodable(this.message);
}
containsNonLatinCodepoints() {
return containsNonLatinCodepoints(this.toUserName) ||
containsNonLatinCodepoints(this.fromUserName) ||
containsNonLatinCodepoints(this.subject) ||
containsNonLatinCodepoints(this.message);
}
/* /*
:TODO: finish me :TODO: finish me
static checkUserHasDeleteRights(user, messageIdOrUuid, cb) { static checkUserHasDeleteRights(user, messageIdOrUuid, cb) {
@ -183,6 +208,10 @@ module.exports = class Message {
return FTN_PROPERTY_NAMES; return FTN_PROPERTY_NAMES;
} }
static get QWKPropertyNames() {
return QWKPropertyNames;
}
setLocalToUserId(userId) { setLocalToUserId(userId) {
this.meta.System = this.meta.System || {}; this.meta.System = this.meta.System || {};
this.meta.System[Message.SystemMetaNames.LocalToUserID] = userId; this.meta.System[Message.SystemMetaNames.LocalToUserID] = userId;

View File

@ -29,6 +29,7 @@ exports.getDefaultMessageAreaTagByConfTag = getDefaultMessageAreaTagByConfTag;
exports.getSuitableMessageConfAndAreaTags = getSuitableMessageConfAndAreaTags; exports.getSuitableMessageConfAndAreaTags = getSuitableMessageConfAndAreaTags;
exports.getMessageConferenceByTag = getMessageConferenceByTag; exports.getMessageConferenceByTag = getMessageConferenceByTag;
exports.getMessageAreaByTag = getMessageAreaByTag; exports.getMessageAreaByTag = getMessageAreaByTag;
exports.getMessageConfTagByAreaTag = getMessageConfTagByAreaTag;
exports.changeMessageConference = changeMessageConference; exports.changeMessageConference = changeMessageConference;
exports.changeMessageArea = changeMessageArea; exports.changeMessageArea = changeMessageArea;
exports.hasMessageConfAndAreaRead = hasMessageConfAndAreaRead; exports.hasMessageConfAndAreaRead = hasMessageConfAndAreaRead;

View File

@ -170,6 +170,11 @@ Actions:
import-areas PATH Import areas using FidoNet *.NA or AREAS.BBS file import-areas PATH Import areas using FidoNet *.NA or AREAS.BBS file
qwk-dump PATH Dumps a QWK packet to stdout.
qwk-export [AREA_TAGS] PATH Exports one or more configured message area to a QWK
packet in the directory specified by PATH. The QWK
BBS ID will be obtained by the final component of PATH.
import-areas arguments: import-areas arguments:
--conf CONF_TAG Conference tag in which to import areas --conf CONF_TAG Conference tag in which to import areas
--network NETWORK Network name/key to associate FTN areas --network NETWORK Network name/key to associate FTN areas
@ -177,6 +182,11 @@ import-areas arguments:
--type TYPE Area import type --type TYPE Area import type
Valid types are "bbs" and "na". Valid types are "bbs" and "na".
qwk-export arguments:
--user USER User in which to export for. Defaults to the SysOp.
--after TIMESTAMP Export only messages with a timestamp later than
TIMESTAMP.
` `
}; };

View File

@ -11,6 +11,7 @@ const {
getAnswers, getAnswers,
writeConfig, writeConfig,
} = require('./oputil_common.js'); } = require('./oputil_common.js');
const getHelpFor = require('./oputil_help.js').getHelpFor; const getHelpFor = require('./oputil_help.js').getHelpFor;
const Address = require('../ftn_address.js'); const Address = require('../ftn_address.js');
const Errors = require('../enig_error.js').Errors; const Errors = require('../enig_error.js').Errors;
@ -21,6 +22,7 @@ const paths = require('path');
const fs = require('fs'); const fs = require('fs');
const hjson = require('hjson'); const hjson = require('hjson');
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment');
exports.handleMessageBaseCommand = handleMessageBaseCommand; exports.handleMessageBaseCommand = handleMessageBaseCommand;
@ -434,6 +436,197 @@ function getImportEntries(importType, importData) {
return importEntries; return importEntries;
} }
function dumpQWKPacket() {
const packetPath = argv._[argv._.length - 1];
if(argv._.length < 3 || !packetPath || 0 === packetPath.length) {
return printUsageAndSetExitCode(getHelpFor('MessageBase'), ExitCodes.ERROR);
}
async.waterfall(
[
(callback) => {
return initConfigAndDatabases(callback);
},
(callback) => {
const { QWKPacketReader } = require('../qwk_mail_packet');
const reader = new QWKPacketReader(packetPath);
reader.on('error', err => {
console.error(`ERROR: ${err.message}`);
return callback(err);
});
reader.on('done', () => {
return callback(null);
});
reader.on('archive type', archiveType => {
console.info(`-> Archive type: ${archiveType}`);
});
reader.on('creator', creator => {
console.info(`-> Creator: ${creator}`);
});
reader.on('message', message => {
console.info('--- message ---');
console.info(`To: ${message.toUserName}`);
console.info(`From: ${message.fromUserName}`);
console.info(`Subject: ${message.subject}`);
console.info(`Message:\r\n${message.message}`);
});
reader.read();
}
],
err => {
}
)
}
function exportQWKPacket() {
let packetPath = argv._[argv._.length - 1];
if(argv._.length < 3 || !packetPath || 0 === packetPath.length) {
return printUsageAndSetExitCode(getHelpFor('MessageBase'), ExitCodes.ERROR);
}
// oputil mb qwk-export TAGS PATH [--user USER] [--after TIMESTAMP]
// [areaTag1,areaTag2,...] PATH --user USER --after TIMESTAMP
let bbsID = 'ENIGMA';
const filename = paths.basename(packetPath);
if (filename) {
const ext = paths.extname(filename);
bbsID = paths.basename(filename, ext);
}
packetPath = paths.dirname(packetPath);
const posArgLen = argv._.length;
let areaTags;
if (4 === posArgLen) {
areaTags = argv._[posArgLen - 2].split(',');
} else {
areaTags = [];
}
let newerThanTimestamp = null;
if (argv.after) {
const ts = moment(argv.after);
if (ts.isValid()) {
newerThanTimestamp = ts.format();
}
}
const userName = argv.user || '-';
let totalExported = 0;
async.waterfall(
[
(callback) => {
return initConfigAndDatabases(callback);
},
(callback) => {
const User = require('../../core/user.js');
User.getUserIdAndName(userName, (err, userId) => {
if (err) {
if ('-' === userName) {
userId = 1;
} else {
return callback(err);
}
}
return User.getUser(userId, callback);
});
},
(user, callback) => {
// populate area tags with all available to user
// if they were not explicitly supplied
if (!areaTags.length) {
const {
getAvailableMessageConferences,
getAvailableMessageAreasByConfTag
} = require('../../core/message_area');
const confTags = Object.keys(getAvailableMessageConferences(null, { noClient : true }));
confTags.forEach( confTag => {
areaTags = areaTags.concat(Object.keys(getAvailableMessageAreasByConfTag(confTag)));
});
}
return callback(null, user);
},
(user, callback) => {
const Message = require('../message');
const filter = {
resultType : 'id',
areaTag : areaTags,
newerThanTimestamp,
};
// public
Message.findMessages(filter, (err, publicMessageIds) => {
if (err) {
return callback(err);
}
delete filter.areaTag;
filter.privateTagUserId = user.userId;
Message.findMessages(filter, (err, privateMessageIds) => {
return callback(err, user, Message, privateMessageIds.concat(publicMessageIds));
});
});
},
(user, Message, messageIds, callback) => {
const { QWKPacketWriter } = require('../qwk_mail_packet');
const writer = new QWKPacketWriter({
bbsID,
user,
});
writer.on('ready', () => {
async.eachSeries(messageIds, (messageId, nextMessageId) => {
const message = new Message();
message.load( { messageId }, err => {
if (!err) {
writer.appendMessage(message);
++totalExported;
}
return nextMessageId(err);
});
},
(err) => {
writer.finish(packetPath);
if (err) {
console.error(`Failed to write one or more messages: ${err.message}`);
}
});
});
writer.on('warning', err => {
console.warn(`!!! ${err.reason ? err.reason : err.message}`);
});
writer.on('finished', () => {
return callback(null);
});
writer.init();
}
],
err => {
if(err) {
return console.error(err.reason ? err.reason : err.message);
}
console.info(`-> Exported ${totalExported} messages`);
}
);
}
function handleMessageBaseCommand() { function handleMessageBaseCommand() {
function errUsage() { function errUsage() {
@ -452,5 +645,7 @@ function handleMessageBaseCommand() {
return({ return({
areafix : areaFix, areafix : areaFix,
'import-areas' : importAreas, 'import-areas' : importAreas,
'qwk-dump' : dumpQWKPacket,
'qwk-export' : exportQWKPacket,
}[action] || errUsage)(); }[action] || errUsage)();
} }

1467
core/qwk_mail_packet.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ exports.pad = pad;
exports.insert = insert; exports.insert = insert;
exports.replaceAt = replaceAt; exports.replaceAt = replaceAt;
exports.isPrintable = isPrintable; exports.isPrintable = isPrintable;
exports.containsNonLatinCodepoints = containsNonLatinCodepoints;
exports.stripAllLineFeeds = stripAllLineFeeds; exports.stripAllLineFeeds = stripAllLineFeeds;
exports.debugEscapedString = debugEscapedString; exports.debugEscapedString = debugEscapedString;
exports.stringFromNullTermBuffer = stringFromNullTermBuffer; exports.stringFromNullTermBuffer = stringFromNullTermBuffer;
@ -196,6 +197,20 @@ function isPrintable(s) {
return !RE_NON_PRINTABLE.test(s); return !RE_NON_PRINTABLE.test(s);
} }
const NonLatinCodePointsRegExp = /[^\u0000-\u00ff]/;
function containsNonLatinCodepoints(s) {
if (!s.length) {
return false;
}
if (s.charCodeAt(0) > 255) {
return true;
}
return NonLatinCodepointsRegEx.test(s);
}
function stripAllLineFeeds(s) { function stripAllLineFeeds(s) {
return s.replace(/\r?\n|[\r\u2028\u2029]/g, ''); return s.replace(/\r?\n|[\r\u2028\u2029]/g, '');
} }

View File

@ -33,6 +33,7 @@
"hashids": "2.1.0", "hashids": "2.1.0",
"hjson": "^3.2.1", "hjson": "^3.2.1",
"iconv-lite": "0.5.0", "iconv-lite": "0.5.0",
"ini-config-parser": "^1.0.4",
"inquirer": "^7.0.0", "inquirer": "^7.0.0",
"later": "1.2.0", "later": "1.2.0",
"lodash": "^4.17.15", "lodash": "^4.17.15",

View File

@ -312,6 +312,11 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
coffee-script@^1.12.4:
version "1.12.7"
resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.7.tgz#c05dae0cb79591d05b3070a8433a98c9a89ccc53"
integrity sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==
collection-visit@^1.0.0: collection-visit@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@ -408,6 +413,11 @@ decode-uri-component@^0.2.0:
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
deep-extend@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f"
integrity sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==
deep-extend@^0.6.0: deep-extend@^0.6.0:
version "0.6.0" version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
@ -883,6 +893,15 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini-config-parser@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/ini-config-parser/-/ini-config-parser-1.0.4.tgz#0abc75cb68c506204712d2b4861400b6adbfda78"
integrity sha512-5hLh5Cqai67pTrLQ9q/K/3EtSP2Tzu41AZzwPLSegkkMkc42dGweLgkbiocCBiBBEg2fPhs6pKmdFhwj5Ul3Bg==
dependencies:
coffee-script "^1.12.4"
deep-extend "^0.5.1"
rimraf "^2.6.1"
ini@~1.3.0: ini@~1.3.0:
version "1.3.5" version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"