WIP
parent
68c9001270
commit
7344b45be0
@ -1,3 +1,4 @@
|
||||
{
|
||||
"extends": "@joepie91/eslint-config"
|
||||
"extends": "@joepie91/eslint-config",
|
||||
"ignorePatterns": "test/**"
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,55 @@
|
||||
"use strict";
|
||||
|
||||
const utf7 = require('utf7').imap;
|
||||
|
||||
const RE_BACKSLASH = /\\/g;
|
||||
const RE_DBLQUOTE = /"/g;
|
||||
|
||||
function escape(str) {
|
||||
return str.replace(RE_BACKSLASH, '\\\\').replace(RE_DBLQUOTE, '\\"');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
command: function (strings, ... interpolations) {
|
||||
return {
|
||||
toCommandString: function () {
|
||||
let processedInterpolations = interpolations.map((interpolation) => {
|
||||
let isObject = interpolation != null && typeof interpolation === "object";
|
||||
|
||||
if (isObject && interpolation._isRaw) {
|
||||
return interpolation.string;
|
||||
} else if (isObject && interpolation._is7Bit) {
|
||||
return escape(interpolation.string);
|
||||
} else if (typeof interpolation === "string") {
|
||||
return escape(utf7.encode(interpolation));
|
||||
} else {
|
||||
throw new Error(`Invalid input into command string: ${interpolation}`);
|
||||
}
|
||||
});
|
||||
|
||||
let combined = "";
|
||||
|
||||
strings.slice(0, -1).forEach((string, i) => {
|
||||
combined += string;
|
||||
combined += processedInterpolations[i];
|
||||
});
|
||||
|
||||
combined += strings[strings.length - 1];
|
||||
|
||||
return combined;
|
||||
}
|
||||
};
|
||||
},
|
||||
unsafeRaw: function (string) {
|
||||
return {
|
||||
_isRaw: true,
|
||||
string: string
|
||||
};
|
||||
},
|
||||
already7Bit: function (string) {
|
||||
return {
|
||||
_is7Bit: true,
|
||||
string: string
|
||||
};
|
||||
}
|
||||
};
|
@ -0,0 +1,112 @@
|
||||
"use strict";
|
||||
|
||||
const events = require("events");
|
||||
const syncpipe = require("syncpipe");
|
||||
const defaultValue = require("default-value");
|
||||
const splitFilter = require("split-filter");
|
||||
const mapObject = require("map-obj");
|
||||
|
||||
const pickObject = require("./pick-object");
|
||||
|
||||
const RE_BODYPART = /^BODY\[/;
|
||||
const RE_ESCAPE = /\\\\/g;
|
||||
|
||||
const FETCH_ATTR_MAP = {
|
||||
'RFC822.SIZE': 'size',
|
||||
'BODY': 'struct',
|
||||
'BODYSTRUCTURE': 'struct',
|
||||
'ENVELOPE': 'envelope',
|
||||
'INTERNALDATE': 'date'
|
||||
};
|
||||
|
||||
function mapIncomingAttributeKey(key) {
|
||||
return defaultValue(FETCH_ATTR_MAP[key], key.toLowerCase());
|
||||
}
|
||||
|
||||
// FIXME: Get rid of the separate-emitter hackery here, and make the task itself an emitter instead, or find some other better way to wire things up
|
||||
|
||||
module.exports = function createFetchTaskTracker() {
|
||||
let tasks = new Map();
|
||||
|
||||
return {
|
||||
get: function (id) {
|
||||
// TODO: Eventually make this fail hard, after the calling code has been fully refactored
|
||||
return tasks.get(id);
|
||||
},
|
||||
create: function (id, keysToFetch) {
|
||||
let emitter = new events.EventEmitter();
|
||||
let task = createFetchTask({ emitter, keysToFetch });
|
||||
// FIXME: Delete after completion
|
||||
|
||||
tasks.set(id, task);
|
||||
|
||||
return task;
|
||||
},
|
||||
getOrCreate: function (id, keysToFetch) {
|
||||
if (tasks.has(id)) {
|
||||
return this.get(id);
|
||||
} else {
|
||||
return this.create(id, keysToFetch);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function createFetchTask({ emitter, keysToFetch }) {
|
||||
let attributes = {};
|
||||
let endHandled = false;
|
||||
|
||||
return {
|
||||
emitter: emitter,
|
||||
getRemainingKeys: function () {
|
||||
return keysToFetch;
|
||||
},
|
||||
processFetchResponse: function (payload) {
|
||||
if (keysToFetch.length > 0) {
|
||||
let caseMappedPayload = mapObject(payload, (key, value) => {
|
||||
return [ key.toUpperCase(), value ];
|
||||
});
|
||||
|
||||
let [ existingKeys, missingKeys ] = splitFilter(keysToFetch, (key) => caseMappedPayload[key] != null);
|
||||
let relevantKeys = existingKeys.filter((key) => RE_BODYPART.test(key) === false);
|
||||
|
||||
let newAttributes = syncpipe(caseMappedPayload, [
|
||||
(_) => pickObject(_, relevantKeys),
|
||||
(_) => mapObject(_, (key, value) => {
|
||||
return [
|
||||
/* key */ mapIncomingAttributeKey(key),
|
||||
/* value */ (key === 'X-GM-LABELS')
|
||||
// TODO: Why is this special case needed?
|
||||
? value.map((label) => String(label).replace(RE_ESCAPE, '\\'))
|
||||
: value
|
||||
];
|
||||
})
|
||||
]);
|
||||
|
||||
Object.assign(attributes, newAttributes);
|
||||
keysToFetch = missingKeys;
|
||||
|
||||
let isDone = (missingKeys.length === 0);
|
||||
|
||||
if (isDone === true) {
|
||||
endHandled = true;
|
||||
|
||||
// FIXME: Why nextTick?
|
||||
process.nextTick(function() {
|
||||
emitter.emit("attributes", attributes);
|
||||
emitter.emit("end");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (endHandled === false) {
|
||||
endHandled = true;
|
||||
|
||||
// FIXME: Why nextTick?
|
||||
process.nextTick(function() {
|
||||
emitter.emit("end");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
@ -0,0 +1,32 @@
|
||||
"use strict";
|
||||
|
||||
// TODO: Make separate package
|
||||
|
||||
const pTry = require("p-try");
|
||||
|
||||
module.exports = function pInterval(interval, handler) {
|
||||
let timer = setTimeout(doHandler, interval);
|
||||
|
||||
function doHandler() {
|
||||
let startTime = Date.now();
|
||||
|
||||
pTry(() => {
|
||||
return handler();
|
||||
}).then(() => {
|
||||
let timeElapsed = Date.now() - startTime();
|
||||
|
||||
let timeout = (timeElapsed < interval)
|
||||
? interval - timeElapsed
|
||||
: 1; // Be consistently async!
|
||||
|
||||
timer = setTimeout(doHandler, timeout);
|
||||
});
|
||||
}
|
||||
|
||||
return function cancelInterval() {
|
||||
if (timer != null) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = function pickObject(object, keys) {
|
||||
let newObject = {};
|
||||
|
||||
for (let key of keys) {
|
||||
if (key in object) {
|
||||
newObject[key] = object[key];
|
||||
}
|
||||
}
|
||||
|
||||
return newObject;
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
require('fs').readdirSync(__dirname).forEach(function(f) {
|
||||
// if (f === "test-connection-fetch-dup.js")
|
||||
if (f.substr(0, 5).toLowerCase() === 'test-')
|
||||
require('./' + f);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue