5695 lines
179 KiB
JavaScript
5695 lines
179 KiB
JavaScript
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
if you want to view the source, please visit the github repository of this plugin
|
|
*/
|
|
|
|
var __create = Object.create;
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __commonJS = (cb, mod) => function __require() {
|
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
};
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
var __publicField = (obj, key, value) => {
|
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
|
|
// src/libs/Events.js
|
|
var require_Events = __commonJS({
|
|
"src/libs/Events.js"(exports, module2) {
|
|
module2.exports = class EventEmitter {
|
|
constructor() {
|
|
this.callbacks = {};
|
|
this.callbacks_once = {};
|
|
}
|
|
on(event, cb) {
|
|
if (!this.callbacks[event])
|
|
this.callbacks[event] = [];
|
|
this.callbacks[event].push(cb);
|
|
}
|
|
off(event, cb) {
|
|
if (this.callbacks[event]) {
|
|
this.callbacks[event] = this.callbacks[event].filter((item) => item !== cb);
|
|
}
|
|
}
|
|
removeListener(event, cb) {
|
|
this.off(event, cb);
|
|
}
|
|
removeAllListeners(event) {
|
|
if (event === void 0) {
|
|
this.callbacks = {};
|
|
} else {
|
|
delete this.callbacks[event];
|
|
}
|
|
}
|
|
once(event, cb) {
|
|
if (!this.callbacks_once[event])
|
|
this.callbacks_once[event] = [];
|
|
this.callbacks_once[event].push(cb);
|
|
}
|
|
emit(event, ...args) {
|
|
let cbs = this.callbacks[event];
|
|
if (cbs) {
|
|
cbs.forEach((cb) => cb(...args));
|
|
}
|
|
cbs = this.callbacks_once[event];
|
|
if (cbs) {
|
|
cbs.forEach((cb) => cb(...args));
|
|
delete this.callbacks_once[event];
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/ms/index.js
|
|
var require_ms = __commonJS({
|
|
"node_modules/ms/index.js"(exports, module2) {
|
|
var s = 1e3;
|
|
var m = s * 60;
|
|
var h = m * 60;
|
|
var d = h * 24;
|
|
var w = d * 7;
|
|
var y = d * 365.25;
|
|
module2.exports = function(val, options) {
|
|
options = options || {};
|
|
var type = typeof val;
|
|
if (type === "string" && val.length > 0) {
|
|
return parse(val);
|
|
} else if (type === "number" && isFinite(val)) {
|
|
return options.long ? fmtLong(val) : fmtShort(val);
|
|
}
|
|
throw new Error("val is not a non-empty string or a valid number. val=" + JSON.stringify(val));
|
|
};
|
|
function parse(str) {
|
|
str = String(str);
|
|
if (str.length > 100) {
|
|
return;
|
|
}
|
|
var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(str);
|
|
if (!match) {
|
|
return;
|
|
}
|
|
var n = parseFloat(match[1]);
|
|
var type = (match[2] || "ms").toLowerCase();
|
|
switch (type) {
|
|
case "years":
|
|
case "year":
|
|
case "yrs":
|
|
case "yr":
|
|
case "y":
|
|
return n * y;
|
|
case "weeks":
|
|
case "week":
|
|
case "w":
|
|
return n * w;
|
|
case "days":
|
|
case "day":
|
|
case "d":
|
|
return n * d;
|
|
case "hours":
|
|
case "hour":
|
|
case "hrs":
|
|
case "hr":
|
|
case "h":
|
|
return n * h;
|
|
case "minutes":
|
|
case "minute":
|
|
case "mins":
|
|
case "min":
|
|
case "m":
|
|
return n * m;
|
|
case "seconds":
|
|
case "second":
|
|
case "secs":
|
|
case "sec":
|
|
case "s":
|
|
return n * s;
|
|
case "milliseconds":
|
|
case "millisecond":
|
|
case "msecs":
|
|
case "msec":
|
|
case "ms":
|
|
return n;
|
|
default:
|
|
return void 0;
|
|
}
|
|
}
|
|
function fmtShort(ms) {
|
|
var msAbs = Math.abs(ms);
|
|
if (msAbs >= d) {
|
|
return Math.round(ms / d) + "d";
|
|
}
|
|
if (msAbs >= h) {
|
|
return Math.round(ms / h) + "h";
|
|
}
|
|
if (msAbs >= m) {
|
|
return Math.round(ms / m) + "m";
|
|
}
|
|
if (msAbs >= s) {
|
|
return Math.round(ms / s) + "s";
|
|
}
|
|
return ms + "ms";
|
|
}
|
|
function fmtLong(ms) {
|
|
var msAbs = Math.abs(ms);
|
|
if (msAbs >= d) {
|
|
return plural(ms, msAbs, d, "day");
|
|
}
|
|
if (msAbs >= h) {
|
|
return plural(ms, msAbs, h, "hour");
|
|
}
|
|
if (msAbs >= m) {
|
|
return plural(ms, msAbs, m, "minute");
|
|
}
|
|
if (msAbs >= s) {
|
|
return plural(ms, msAbs, s, "second");
|
|
}
|
|
return ms + " ms";
|
|
}
|
|
function plural(ms, msAbs, n, name) {
|
|
var isPlural = msAbs >= n * 1.5;
|
|
return Math.round(ms / n) + " " + name + (isPlural ? "s" : "");
|
|
}
|
|
}
|
|
});
|
|
|
|
// node_modules/debug/src/common.js
|
|
var require_common = __commonJS({
|
|
"node_modules/debug/src/common.js"(exports, module2) {
|
|
function setup(env) {
|
|
createDebug.debug = createDebug;
|
|
createDebug.default = createDebug;
|
|
createDebug.coerce = coerce;
|
|
createDebug.disable = disable;
|
|
createDebug.enable = enable;
|
|
createDebug.enabled = enabled;
|
|
createDebug.humanize = require_ms();
|
|
createDebug.destroy = destroy;
|
|
Object.keys(env).forEach((key) => {
|
|
createDebug[key] = env[key];
|
|
});
|
|
createDebug.names = [];
|
|
createDebug.skips = [];
|
|
createDebug.formatters = {};
|
|
function selectColor(namespace) {
|
|
let hash = 0;
|
|
for (let i = 0; i < namespace.length; i++) {
|
|
hash = (hash << 5) - hash + namespace.charCodeAt(i);
|
|
hash |= 0;
|
|
}
|
|
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
|
|
}
|
|
createDebug.selectColor = selectColor;
|
|
function createDebug(namespace) {
|
|
let prevTime;
|
|
let enableOverride = null;
|
|
let namespacesCache;
|
|
let enabledCache;
|
|
function debug(...args) {
|
|
if (!debug.enabled) {
|
|
return;
|
|
}
|
|
const self = debug;
|
|
const curr = Number(new Date());
|
|
const ms = curr - (prevTime || curr);
|
|
self.diff = ms;
|
|
self.prev = prevTime;
|
|
self.curr = curr;
|
|
prevTime = curr;
|
|
args[0] = createDebug.coerce(args[0]);
|
|
if (typeof args[0] !== "string") {
|
|
args.unshift("%O");
|
|
}
|
|
let index = 0;
|
|
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
|
|
if (match === "%%") {
|
|
return "%";
|
|
}
|
|
index++;
|
|
const formatter = createDebug.formatters[format];
|
|
if (typeof formatter === "function") {
|
|
const val = args[index];
|
|
match = formatter.call(self, val);
|
|
args.splice(index, 1);
|
|
index--;
|
|
}
|
|
return match;
|
|
});
|
|
createDebug.formatArgs.call(self, args);
|
|
const logFn = self.log || createDebug.log;
|
|
logFn.apply(self, args);
|
|
}
|
|
debug.namespace = namespace;
|
|
debug.useColors = createDebug.useColors();
|
|
debug.color = createDebug.selectColor(namespace);
|
|
debug.extend = extend;
|
|
debug.destroy = createDebug.destroy;
|
|
Object.defineProperty(debug, "enabled", {
|
|
enumerable: true,
|
|
configurable: false,
|
|
get: () => {
|
|
if (enableOverride !== null) {
|
|
return enableOverride;
|
|
}
|
|
if (namespacesCache !== createDebug.namespaces) {
|
|
namespacesCache = createDebug.namespaces;
|
|
enabledCache = createDebug.enabled(namespace);
|
|
}
|
|
return enabledCache;
|
|
},
|
|
set: (v) => {
|
|
enableOverride = v;
|
|
}
|
|
});
|
|
if (typeof createDebug.init === "function") {
|
|
createDebug.init(debug);
|
|
}
|
|
return debug;
|
|
}
|
|
function extend(namespace, delimiter) {
|
|
const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
|
|
newDebug.log = this.log;
|
|
return newDebug;
|
|
}
|
|
function enable(namespaces) {
|
|
createDebug.save(namespaces);
|
|
createDebug.namespaces = namespaces;
|
|
createDebug.names = [];
|
|
createDebug.skips = [];
|
|
let i;
|
|
const split = (typeof namespaces === "string" ? namespaces : "").split(/[\s,]+/);
|
|
const len = split.length;
|
|
for (i = 0; i < len; i++) {
|
|
if (!split[i]) {
|
|
continue;
|
|
}
|
|
namespaces = split[i].replace(/\*/g, ".*?");
|
|
if (namespaces[0] === "-") {
|
|
createDebug.skips.push(new RegExp("^" + namespaces.slice(1) + "$"));
|
|
} else {
|
|
createDebug.names.push(new RegExp("^" + namespaces + "$"));
|
|
}
|
|
}
|
|
}
|
|
function disable() {
|
|
const namespaces = [
|
|
...createDebug.names.map(toNamespace),
|
|
...createDebug.skips.map(toNamespace).map((namespace) => "-" + namespace)
|
|
].join(",");
|
|
createDebug.enable("");
|
|
return namespaces;
|
|
}
|
|
function enabled(name) {
|
|
if (name[name.length - 1] === "*") {
|
|
return true;
|
|
}
|
|
let i;
|
|
let len;
|
|
for (i = 0, len = createDebug.skips.length; i < len; i++) {
|
|
if (createDebug.skips[i].test(name)) {
|
|
return false;
|
|
}
|
|
}
|
|
for (i = 0, len = createDebug.names.length; i < len; i++) {
|
|
if (createDebug.names[i].test(name)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function toNamespace(regexp) {
|
|
return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, "*");
|
|
}
|
|
function coerce(val) {
|
|
if (val instanceof Error) {
|
|
return val.stack || val.message;
|
|
}
|
|
return val;
|
|
}
|
|
function destroy() {
|
|
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
|
|
}
|
|
createDebug.enable(createDebug.load());
|
|
return createDebug;
|
|
}
|
|
module2.exports = setup;
|
|
}
|
|
});
|
|
|
|
// node_modules/debug/src/browser.js
|
|
var require_browser = __commonJS({
|
|
"node_modules/debug/src/browser.js"(exports, module2) {
|
|
exports.formatArgs = formatArgs;
|
|
exports.save = save;
|
|
exports.load = load;
|
|
exports.useColors = useColors;
|
|
exports.storage = localstorage();
|
|
exports.destroy = (() => {
|
|
let warned = false;
|
|
return () => {
|
|
if (!warned) {
|
|
warned = true;
|
|
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
|
|
}
|
|
};
|
|
})();
|
|
exports.colors = [
|
|
"#0000CC",
|
|
"#0000FF",
|
|
"#0033CC",
|
|
"#0033FF",
|
|
"#0066CC",
|
|
"#0066FF",
|
|
"#0099CC",
|
|
"#0099FF",
|
|
"#00CC00",
|
|
"#00CC33",
|
|
"#00CC66",
|
|
"#00CC99",
|
|
"#00CCCC",
|
|
"#00CCFF",
|
|
"#3300CC",
|
|
"#3300FF",
|
|
"#3333CC",
|
|
"#3333FF",
|
|
"#3366CC",
|
|
"#3366FF",
|
|
"#3399CC",
|
|
"#3399FF",
|
|
"#33CC00",
|
|
"#33CC33",
|
|
"#33CC66",
|
|
"#33CC99",
|
|
"#33CCCC",
|
|
"#33CCFF",
|
|
"#6600CC",
|
|
"#6600FF",
|
|
"#6633CC",
|
|
"#6633FF",
|
|
"#66CC00",
|
|
"#66CC33",
|
|
"#9900CC",
|
|
"#9900FF",
|
|
"#9933CC",
|
|
"#9933FF",
|
|
"#99CC00",
|
|
"#99CC33",
|
|
"#CC0000",
|
|
"#CC0033",
|
|
"#CC0066",
|
|
"#CC0099",
|
|
"#CC00CC",
|
|
"#CC00FF",
|
|
"#CC3300",
|
|
"#CC3333",
|
|
"#CC3366",
|
|
"#CC3399",
|
|
"#CC33CC",
|
|
"#CC33FF",
|
|
"#CC6600",
|
|
"#CC6633",
|
|
"#CC9900",
|
|
"#CC9933",
|
|
"#CCCC00",
|
|
"#CCCC33",
|
|
"#FF0000",
|
|
"#FF0033",
|
|
"#FF0066",
|
|
"#FF0099",
|
|
"#FF00CC",
|
|
"#FF00FF",
|
|
"#FF3300",
|
|
"#FF3333",
|
|
"#FF3366",
|
|
"#FF3399",
|
|
"#FF33CC",
|
|
"#FF33FF",
|
|
"#FF6600",
|
|
"#FF6633",
|
|
"#FF9900",
|
|
"#FF9933",
|
|
"#FFCC00",
|
|
"#FFCC33"
|
|
];
|
|
function useColors() {
|
|
if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) {
|
|
return true;
|
|
}
|
|
if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
|
return false;
|
|
}
|
|
return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
|
|
}
|
|
function formatArgs(args) {
|
|
args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module2.exports.humanize(this.diff);
|
|
if (!this.useColors) {
|
|
return;
|
|
}
|
|
const c = "color: " + this.color;
|
|
args.splice(1, 0, c, "color: inherit");
|
|
let index = 0;
|
|
let lastC = 0;
|
|
args[0].replace(/%[a-zA-Z%]/g, (match) => {
|
|
if (match === "%%") {
|
|
return;
|
|
}
|
|
index++;
|
|
if (match === "%c") {
|
|
lastC = index;
|
|
}
|
|
});
|
|
args.splice(lastC, 0, c);
|
|
}
|
|
exports.log = console.debug || console.log || (() => {
|
|
});
|
|
function save(namespaces) {
|
|
try {
|
|
if (namespaces) {
|
|
exports.storage.setItem("debug", namespaces);
|
|
} else {
|
|
exports.storage.removeItem("debug");
|
|
}
|
|
} catch (error) {
|
|
}
|
|
}
|
|
function load() {
|
|
let r;
|
|
try {
|
|
r = exports.storage.getItem("debug");
|
|
} catch (error) {
|
|
}
|
|
if (!r && typeof process !== "undefined" && "env" in process) {
|
|
r = process.env.DEBUG;
|
|
}
|
|
return r;
|
|
}
|
|
function localstorage() {
|
|
try {
|
|
return localStorage;
|
|
} catch (error) {
|
|
}
|
|
}
|
|
module2.exports = require_common()(exports);
|
|
var { formatters } = module2.exports;
|
|
formatters.j = function(v) {
|
|
try {
|
|
return JSON.stringify(v);
|
|
} catch (error) {
|
|
return "[UnexpectedJSONParseError]: " + error.message;
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/browser/events.js
|
|
var require_events = __commonJS({
|
|
"node_modules/anysocket/src/browser/events.js"(exports, module2) {
|
|
module2.exports = class EventEmitter {
|
|
constructor() {
|
|
this.callbacks = {};
|
|
this.callbacks_once = {};
|
|
}
|
|
on(event, cb) {
|
|
if (!this.callbacks[event])
|
|
this.callbacks[event] = [];
|
|
this.callbacks[event].push(cb);
|
|
}
|
|
off(event, cb) {
|
|
if (this.callbacks[event]) {
|
|
this.callbacks[event] = this.callbacks[event].filter((item) => item !== cb);
|
|
}
|
|
}
|
|
removeListener(event, cb) {
|
|
this.off(event, cb);
|
|
}
|
|
removeAllListeners(event) {
|
|
if (event === void 0) {
|
|
this.callbacks = {};
|
|
} else {
|
|
delete this.callbacks[event];
|
|
}
|
|
}
|
|
once(event, cb) {
|
|
if (!this.callbacks_once[event])
|
|
this.callbacks_once[event] = [];
|
|
this.callbacks_once[event].push(cb);
|
|
}
|
|
emit(event, ...args) {
|
|
let cbs = this.callbacks[event];
|
|
if (cbs) {
|
|
cbs.forEach((cb) => cb(...args));
|
|
}
|
|
cbs = this.callbacks_once[event];
|
|
if (cbs) {
|
|
cbs.forEach((cb) => cb(...args));
|
|
delete this.callbacks_once[event];
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/wrappers/events-wrapper.js
|
|
var require_events_wrapper = __commonJS({
|
|
"node_modules/anysocket/src/wrappers/events-wrapper.js"(exports, module2) {
|
|
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
|
|
module2.exports = require_events();
|
|
} else {
|
|
module2.exports = require("events");
|
|
}
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/browser/utils_buffer.js
|
|
var require_utils_buffer = __commonJS({
|
|
"node_modules/anysocket/src/browser/utils_buffer.js"(exports, module2) {
|
|
module2.exports = window._x = {
|
|
stringToBuffer: function(str) {
|
|
return btoa(str);
|
|
},
|
|
bufferFromBase64: function(str) {
|
|
const bytes = atob(str);
|
|
const byteLength = bytes.length;
|
|
const buffer = new Uint8Array(byteLength);
|
|
for (let i = 0; i < byteLength; i++) {
|
|
buffer[i] = bytes.charCodeAt(i);
|
|
}
|
|
return buffer;
|
|
},
|
|
bufferToBase64: function(buf) {
|
|
const uintArray = new Uint8Array(buf);
|
|
const array = [];
|
|
for (let i = 0; i < uintArray.length; i++) {
|
|
array.push(String.fromCharCode(uintArray[i]));
|
|
}
|
|
return window.btoa(array.join(""));
|
|
},
|
|
bufferToHex(buf) {
|
|
return buf.reduce((memo, i) => {
|
|
return memo + this.i2hex(i);
|
|
}, "");
|
|
},
|
|
i2hex(i) {
|
|
return ("0" + i.toString(16)).slice(-2);
|
|
},
|
|
bufferFromHex(buf) {
|
|
let view = new Uint8Array(buf.length / 2);
|
|
for (let i = 0; i < buf.length; i += 2) {
|
|
view[i / 2] = parseInt(buf.substring(i, i + 2), 16);
|
|
}
|
|
return view;
|
|
},
|
|
isBuffer(buf) {
|
|
return !!(buf.buffer instanceof ArrayBuffer && buf.BYTES_PER_ELEMENT);
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/browser/crypto.js
|
|
var require_crypto = __commonJS({
|
|
"node_modules/anysocket/src/browser/crypto.js"(exports, module2) {
|
|
var crypto2 = window.crypto || window.msCrypto;
|
|
var QUOTA = 65536;
|
|
if (!crypto2)
|
|
throw new Error("Crypto is not supported in this browser!");
|
|
var BufferUtils = require_utils_buffer();
|
|
var ECDH_ALGORITHM = "P-521";
|
|
var encoder = new TextEncoder();
|
|
var Crypto = class {
|
|
randomBytes(size) {
|
|
let a = new Uint8Array(size);
|
|
for (let i = 0; i < size; i += QUOTA) {
|
|
crypto2.getRandomValues(a.subarray(i, i + Math.min(size - i, QUOTA)));
|
|
}
|
|
return a;
|
|
}
|
|
createECDH() {
|
|
return new Promise(async (resolve, reject) => {
|
|
window.crypto.subtle.generateKey({
|
|
name: "ECDH",
|
|
namedCurve: ECDH_ALGORITHM
|
|
}, false, ["deriveKey", "deriveBits"]).then(function(key) {
|
|
resolve({
|
|
generateKeys: () => {
|
|
return new Promise((resolve2, reject2) => {
|
|
window.crypto.subtle.exportKey("raw", key.publicKey).then(function(keydata) {
|
|
keydata = new Uint8Array(keydata);
|
|
resolve2(keydata);
|
|
}).catch(function(err) {
|
|
reject2(err);
|
|
});
|
|
});
|
|
},
|
|
computeSecret(buf) {
|
|
return new Promise((resolve2, reject2) => {
|
|
window.crypto.subtle.importKey("raw", buf, {
|
|
name: "ECDH",
|
|
namedCurve: ECDH_ALGORITHM
|
|
}, false, []).then(function(keydata) {
|
|
window.crypto.subtle.deriveBits({
|
|
name: "ECDH",
|
|
namedCurve: ECDH_ALGORITHM,
|
|
public: keydata
|
|
}, key.privateKey, 512).then((keydata2) => {
|
|
keydata2 = new Uint8Array(keydata2);
|
|
resolve2(BufferUtils.bufferToHex(keydata2));
|
|
});
|
|
}).catch(function(err) {
|
|
reject2(err);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}).catch(function(err) {
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
pbkdf2Sync(secret, salt, iterations, length, algorithm) {
|
|
let algo = {
|
|
"sha256": "SHA-256"
|
|
};
|
|
if (!algo[algorithm])
|
|
throw new Error("Invalid algorithm " + algorithm);
|
|
algorithm = algo[algorithm];
|
|
return new Promise(async (resolve, reject) => {
|
|
if (!(secret instanceof CryptoKey)) {
|
|
secret = await window.crypto.subtle.importKey("raw", encoder.encode(secret), {
|
|
name: "PBKDF2"
|
|
}, false, ["deriveKey", "deriveBits"]);
|
|
}
|
|
window.crypto.subtle.deriveBits({
|
|
name: "PBKDF2",
|
|
salt: encoder.encode(salt),
|
|
iterations,
|
|
hash: {
|
|
name: algorithm
|
|
}
|
|
}, secret, length * 8).then(function(bits) {
|
|
resolve(new Uint8Array(bits));
|
|
}).catch(function(err) {
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
};
|
|
module2.exports = new Crypto();
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/browser/utils.js
|
|
var require_utils = __commonJS({
|
|
"node_modules/anysocket/src/browser/utils.js"(exports, module2) {
|
|
var crypto2 = require_crypto();
|
|
var BufferUtils = require_utils_buffer();
|
|
var encoder = new TextEncoder();
|
|
var decoder = new TextDecoder();
|
|
module2.exports = new class Utils {
|
|
uuidv4() {
|
|
return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
const r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8;
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
generateAESKey() {
|
|
return new Promise(async (resolve, reject) => {
|
|
let ecdh = await crypto2.createECDH("secp521r1");
|
|
let publicKey = await ecdh.generateKeys();
|
|
resolve({
|
|
private: ecdh,
|
|
public: BufferUtils.bufferToBase64(publicKey),
|
|
nonce: BufferUtils.bufferToHex(crypto2.randomBytes(32))
|
|
});
|
|
});
|
|
}
|
|
computeAESsecret(privateECDHKey, publicECDHKey) {
|
|
return new Promise(async (resolve, reject) => {
|
|
let result = await privateECDHKey.computeSecret(BufferUtils.bufferFromBase64(publicECDHKey), null, "hex");
|
|
result = result.substr(0, 128);
|
|
resolve(result);
|
|
});
|
|
}
|
|
getAESSessionKey(secret, nonce, seq) {
|
|
return new Promise(async (resolve, reject) => {
|
|
nonce = nonce + "_" + seq;
|
|
secret = await crypto2.pbkdf2Sync(secret, nonce, 1, 32, "sha256");
|
|
secret = BufferUtils.bufferToHex(secret);
|
|
resolve(secret);
|
|
});
|
|
}
|
|
encryptAES(secret, data) {
|
|
return new Promise((resolve, reject) => {
|
|
window.crypto.subtle.importKey("raw", BufferUtils.bufferFromHex(secret), {
|
|
name: "AES-CBC",
|
|
length: 256
|
|
}, false, ["encrypt"]).then((key) => {
|
|
let iv = window.crypto.getRandomValues(new Uint8Array(16));
|
|
window.crypto.subtle.encrypt({
|
|
name: "AES-CBC",
|
|
iv
|
|
}, key, encoder.encode(data)).then(function(encrypted) {
|
|
resolve(BufferUtils.bufferToHex(iv) + BufferUtils.bufferToHex(new Uint8Array(encrypted)));
|
|
}).catch(function(err) {
|
|
reject(err);
|
|
});
|
|
}).catch(reject);
|
|
});
|
|
}
|
|
decryptAES(secret, data) {
|
|
return new Promise((resolve, reject) => {
|
|
window.crypto.subtle.importKey("raw", BufferUtils.bufferFromHex(secret), {
|
|
name: "AES-CBC",
|
|
length: 256
|
|
}, false, ["decrypt"]).then((key) => {
|
|
window.crypto.subtle.decrypt({
|
|
name: "AES-CBC",
|
|
iv: BufferUtils.bufferFromHex(data.substr(0, 32))
|
|
}, key, BufferUtils.bufferFromHex(data.substr(32))).then(function(encrypted) {
|
|
resolve(decoder.decode(encrypted));
|
|
}).catch((e) => {
|
|
reject(e);
|
|
});
|
|
}).catch((e) => {
|
|
reject(e);
|
|
});
|
|
});
|
|
}
|
|
}();
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/utils_buffer.js
|
|
var require_utils_buffer2 = __commonJS({
|
|
"node_modules/anysocket/src/libs/utils_buffer.js"(exports, module2) {
|
|
module2.exports = {
|
|
bufferFromBase64: (str) => {
|
|
return Buffer.from(str, "base64");
|
|
},
|
|
bufferToBase64: (buf) => {
|
|
return buf.toString("base64");
|
|
},
|
|
bufferToHex(buf) {
|
|
return buf.toString("hex");
|
|
},
|
|
bufferFromHex(buf) {
|
|
return Buffer.from(buf, "hex");
|
|
},
|
|
isBuffer(buf) {
|
|
return Buffer.isBuffer(buf);
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/wrappers/utils_buffer.js
|
|
var require_utils_buffer3 = __commonJS({
|
|
"node_modules/anysocket/src/wrappers/utils_buffer.js"(exports, module2) {
|
|
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
|
|
module2.exports = require_utils_buffer();
|
|
} else {
|
|
module2.exports = require_utils_buffer2();
|
|
}
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/utils.js
|
|
var require_utils2 = __commonJS({
|
|
"node_modules/anysocket/src/libs/utils.js"(exports, module2) {
|
|
var crypto2 = require("crypto");
|
|
var BufferUtils = require_utils_buffer3();
|
|
module2.exports = new class Utils {
|
|
uuidv4() {
|
|
return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
const r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8;
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
generateAESKey() {
|
|
return new Promise(async (resolve, reject) => {
|
|
let ecdh = await crypto2.createECDH("secp521r1");
|
|
let publicKey = await ecdh.generateKeys();
|
|
resolve({
|
|
private: ecdh,
|
|
public: BufferUtils.bufferToBase64(publicKey),
|
|
nonce: BufferUtils.bufferToHex(crypto2.randomBytes(32))
|
|
});
|
|
});
|
|
}
|
|
computeAESsecret(privateECDHKey, publicECDHKey) {
|
|
return new Promise(async (resolve, reject) => {
|
|
let result = await privateECDHKey.computeSecret(BufferUtils.bufferFromBase64(publicECDHKey), null, "hex");
|
|
result = result.substr(0, 128);
|
|
resolve(result);
|
|
});
|
|
}
|
|
getAESSessionKey(secret, nonce, seq) {
|
|
return new Promise(async (resolve, reject) => {
|
|
nonce = nonce + "_" + seq;
|
|
secret = await crypto2.pbkdf2Sync(secret, nonce, 1, 32, "sha256");
|
|
secret = BufferUtils.bufferToHex(secret);
|
|
resolve(secret);
|
|
});
|
|
}
|
|
encryptAES(secret, data) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
let iv = crypto2.randomBytes(16);
|
|
let cipher = crypto2.createCipheriv("aes-256-cbc", BufferUtils.bufferFromHex(secret), iv);
|
|
let encrypted = cipher.update(data);
|
|
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
|
let msg = iv.toString("hex") + encrypted.toString("hex");
|
|
this.decryptAES(secret, msg);
|
|
resolve(msg);
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
}
|
|
decryptAES(secret, data) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
let iv = Buffer.from(data.substr(0, 32), "hex");
|
|
let encryptedText = Buffer.from(data.substr(32), "hex");
|
|
let decipher = crypto2.createDecipheriv("aes-256-cbc", BufferUtils.bufferFromHex(secret), iv);
|
|
let decrypted = decipher.update(encryptedText);
|
|
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
|
resolve(decrypted.toString());
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
}
|
|
}();
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/wrappers/utils.js
|
|
var require_utils3 = __commonJS({
|
|
"node_modules/anysocket/src/wrappers/utils.js"(exports, module2) {
|
|
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
|
|
module2.exports = require_utils();
|
|
} else {
|
|
module2.exports = require_utils2();
|
|
}
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/_constants.js
|
|
var require_constants = __commonJS({
|
|
"node_modules/anysocket/src/libs/_constants.js"(exports, module2) {
|
|
var constants = {
|
|
PACKET_TYPE: {
|
|
AUTH: 1,
|
|
INTERNAL: 2,
|
|
LINK: 3,
|
|
SWITCH: 4,
|
|
HEARTBEAT: 5,
|
|
FORWARD: 6
|
|
},
|
|
PACKET_LENGTH: {
|
|
FULL: 1,
|
|
PARTIAL: 2
|
|
},
|
|
INTERNAL_PACKET_TYPE: {
|
|
NETWORK: 1,
|
|
PROXY: 2,
|
|
RPC: 3,
|
|
RPC_NOTIFY: 4,
|
|
SYNCED_TIME: 5
|
|
},
|
|
PROTOCOL_STATES: {
|
|
ESTABLISHED: 0,
|
|
AUTHING: 1,
|
|
CONNECTED: 2,
|
|
SWITCHING_PROTOCOL: 3,
|
|
DISCONNECTED: 4
|
|
},
|
|
PROTOCOL_ENCRYPTION: {
|
|
PLAIN: 1,
|
|
E2EE: 2
|
|
},
|
|
MAX_PACKET_SIZE: 1024 * 1024,
|
|
HTTP_CONTENT_TYPES: {
|
|
txt: "text/plain;charset=utf-8",
|
|
html: "text/html;charset=utf-8",
|
|
htm: "text/html;charset=utf-8",
|
|
css: "text/css;charset=utf-8",
|
|
js: "text/javascript;charset=utf-8",
|
|
md: "text/markdown;charset=utf-8",
|
|
sh: "application/x-shellscript;charset=utf-8",
|
|
svg: "image/svg+xml;charset=utf-8",
|
|
xml: "text/xml;charset=utf-8",
|
|
png: "image/png",
|
|
jpeg: "image/jpeg",
|
|
jpg: "image/jpeg",
|
|
jpe: "image/jpeg",
|
|
gif: "image/gif",
|
|
ttf: "font/ttf",
|
|
woff: "font/woff",
|
|
woff2: "font/woff2",
|
|
eot: "application/vnd.ms-fontobject",
|
|
gz: "application/gzip",
|
|
bz: "application/x-bzip",
|
|
bz2: "application/x-bzip2",
|
|
xz: "application/x-xz",
|
|
zst: "application/zst"
|
|
}
|
|
};
|
|
for (let item in constants) {
|
|
constants[item]._string = (number) => {
|
|
for (let key in constants[item]) {
|
|
if (constants[item][key] == number) {
|
|
return key;
|
|
}
|
|
}
|
|
return number;
|
|
};
|
|
}
|
|
module2.exports = constants;
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/AnyHTTPPeer.js
|
|
var require_AnyHTTPPeer = __commonJS({
|
|
"node_modules/anysocket/src/libs/AnyHTTPPeer.js"(exports, module2) {
|
|
var constants = require_constants();
|
|
var httpResult = Symbol("httpResult");
|
|
var reqSymbol = Symbol("req");
|
|
var resSymbol = Symbol("res");
|
|
var endSymbol = Symbol("ended");
|
|
var parseCookies = Symbol("parseCookies");
|
|
var debug = require_browser()("AnyHTTPPeer");
|
|
var url = require("url");
|
|
var fs = require("fs");
|
|
module2.exports = class AnyHTTPPeer {
|
|
constructor(req, res) {
|
|
let self = this;
|
|
let qs = url.parse(req.url, true);
|
|
self[reqSymbol] = req;
|
|
self[resSymbol] = res;
|
|
self[httpResult] = {
|
|
_headers: {},
|
|
_body: [],
|
|
_cookies: [],
|
|
_url: qs.pathname,
|
|
_query: {
|
|
headers: req.headers,
|
|
cookies: req.headers.cookie,
|
|
method: req.method.toLowerCase(),
|
|
body: req.body,
|
|
qs: qs.query,
|
|
upgrade: req.upgrade
|
|
},
|
|
_status: 200
|
|
};
|
|
self[endSymbol] = false;
|
|
self[parseCookies] = (cookies) => {
|
|
const list = {};
|
|
cookies && cookies.split(";").forEach(function(cookie) {
|
|
const parts = cookie.split("=");
|
|
list[parts.shift().trim()] = decodeURI(parts.join("="));
|
|
});
|
|
self[httpResult]._cookies = list;
|
|
return list;
|
|
};
|
|
}
|
|
get url() {
|
|
return this[httpResult]._url;
|
|
}
|
|
get query() {
|
|
return this[httpResult]._query;
|
|
}
|
|
get upgrade() {
|
|
return this[httpResult]._query.upgrade;
|
|
}
|
|
get cookies() {
|
|
return this[parseCookies](this.query.cookies);
|
|
}
|
|
serveFile(path, contentType) {
|
|
fs.readFile(path, "utf8", (err, data) => {
|
|
if (err) {
|
|
this.status(404).end();
|
|
return false;
|
|
}
|
|
if (!contentType) {
|
|
contentType = constants.HTTP_CONTENT_TYPES[path.split(".").pop().toLowerCase()] || "application/octet-stream";
|
|
}
|
|
this.status(200).header("Content-Type", contentType).body(data).end();
|
|
});
|
|
}
|
|
status(code) {
|
|
if (this.isClosed()) {
|
|
debug("Connection already ended!");
|
|
return this;
|
|
}
|
|
this[httpResult]._status = code;
|
|
return this;
|
|
}
|
|
header(name, value) {
|
|
if (this.isClosed()) {
|
|
debug("Connection already ended!");
|
|
return this;
|
|
}
|
|
this[httpResult]._headers[name] = value;
|
|
return this;
|
|
}
|
|
body(chunk) {
|
|
if (this.isClosed()) {
|
|
debug("Connection already ended!");
|
|
return this;
|
|
}
|
|
this[httpResult]._body.push(chunk);
|
|
return this;
|
|
}
|
|
setCookie(key, value, expires) {
|
|
this[httpResult]._cookies[key] = {
|
|
value,
|
|
expires
|
|
};
|
|
return this;
|
|
}
|
|
deleteCookie(key) {
|
|
this[httpResult]._cookies[key] = {
|
|
value: "",
|
|
expires: 1
|
|
};
|
|
return this;
|
|
}
|
|
end() {
|
|
if (this.isClosed()) {
|
|
debug("Connection already ended!");
|
|
return this;
|
|
}
|
|
if (Object.keys(this[httpResult]._cookies).length > 0) {
|
|
let cookie = [];
|
|
for (let key in this[httpResult]._cookies) {
|
|
if (!this[httpResult]._cookies.hasOwnProperty(key))
|
|
continue;
|
|
let c = this[httpResult]._cookies[key];
|
|
cookie.push(key + "=" + c.value + (c.expires ? ";Expires=" + new Date(c.expires).toUTCString() : "") + ";Path=/");
|
|
}
|
|
if (cookie.length > 0) {
|
|
this.header("Set-Cookie", cookie);
|
|
}
|
|
}
|
|
if (this[resSymbol].writeHead) {
|
|
this[resSymbol].writeHead(this[httpResult]._status, this[httpResult]._headers);
|
|
}
|
|
this[endSymbol] = true;
|
|
if (this[httpResult]._body.length > 0)
|
|
for (let i in this[httpResult]._body) {
|
|
if (!this[httpResult]._body.hasOwnProperty(i))
|
|
continue;
|
|
this[resSymbol].write(this[httpResult]._body[i]);
|
|
}
|
|
this[resSymbol].end();
|
|
}
|
|
isClosed() {
|
|
return this[endSymbol];
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/AnyHTTPRouter.js
|
|
var require_AnyHTTPRouter = __commonJS({
|
|
"node_modules/anysocket/src/libs/AnyHTTPRouter.js"(exports, module2) {
|
|
module2.exports = class AnyHTTPRouter {
|
|
constructor() {
|
|
this.routes = {
|
|
_: []
|
|
};
|
|
this.routesRegexp = {
|
|
_: []
|
|
};
|
|
this._upgradeCallback = null;
|
|
this._process = this._process.bind(this);
|
|
this._processUpgrade = this._processUpgrade.bind(this);
|
|
}
|
|
on(method, path, callback) {
|
|
if (path instanceof RegExp) {
|
|
if (!this.routesRegexp[method])
|
|
this.routesRegexp[method] = [];
|
|
this.routesRegexp[method].push({
|
|
path,
|
|
cb: callback
|
|
});
|
|
} else {
|
|
if (!this.routes[method])
|
|
this.routes[method] = {};
|
|
this.routes[method][path] = callback;
|
|
}
|
|
return this;
|
|
}
|
|
upgrade(callback) {
|
|
this._upgradeCallback = callback;
|
|
return this;
|
|
}
|
|
static(url, directory) {
|
|
if (url[0] != "/") {
|
|
url = "/" + url;
|
|
}
|
|
if (!directory) {
|
|
directory = url;
|
|
if (directory[0] == "/") {
|
|
directory = "." + directory;
|
|
}
|
|
}
|
|
return this.on("get", new RegExp("^" + url + "/(.*)$"), (peer) => {
|
|
peer.serveFile(directory + peer.url.split(url).splice(1).join(url));
|
|
});
|
|
}
|
|
any(path, callback) {
|
|
return this.on("_", path, callback);
|
|
}
|
|
get(path, callback) {
|
|
return this.on("get", path, callback);
|
|
}
|
|
post(path, callback) {
|
|
return this.on("post", path, callback);
|
|
}
|
|
delete(path, callback) {
|
|
return this.on("delete", path, callback);
|
|
}
|
|
error(callback) {
|
|
this.onError = callback;
|
|
}
|
|
_processUpgrade(peer) {
|
|
try {
|
|
if (!this._upgradeCallback)
|
|
return;
|
|
this._upgradeCallback(peer);
|
|
} catch (e) {
|
|
return this._finish(peer, e);
|
|
}
|
|
}
|
|
_process(peer) {
|
|
try {
|
|
if (this.routes._[peer.url]) {
|
|
this.routes._[peer.url](peer);
|
|
return true;
|
|
}
|
|
if (this.routes[peer.query.method] && this.routes[peer.query.method][peer.url]) {
|
|
this.routes[peer.query.method][peer.url](peer);
|
|
return true;
|
|
}
|
|
for (let item of this.routesRegexp._) {
|
|
if (item.path.test(peer.url)) {
|
|
item.cb(peer);
|
|
return true;
|
|
}
|
|
}
|
|
if (this.routesRegexp[peer.query.method]) {
|
|
for (let item of this.routesRegexp[peer.query.method]) {
|
|
if (item.path.test(peer.url)) {
|
|
item.cb(peer);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
return this._finish(peer, e);
|
|
}
|
|
this._finish(peer, new Error("No route for path: '" + peer.url + "'"));
|
|
}
|
|
_finish(peer, error) {
|
|
if (this.onError) {
|
|
this.onError(peer, error);
|
|
}
|
|
if (!peer.isClosed()) {
|
|
peer.status(404).end();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/AnyPacker.js
|
|
var require_AnyPacker = __commonJS({
|
|
"node_modules/anysocket/src/libs/AnyPacker.js"(exports, module2) {
|
|
var BufferUtils = require_utils_buffer3();
|
|
var AnyPacker = class {
|
|
packInt32(int) {
|
|
const arr = new ArrayBuffer(4);
|
|
const view = new DataView(arr);
|
|
view.setInt32(0, int, false);
|
|
return String.fromCharCode.apply(String, new Uint8Array(arr));
|
|
}
|
|
unpackInt32(bytes) {
|
|
const arr = new ArrayBuffer(4);
|
|
const bufView = new Uint8Array(arr);
|
|
for (let i in bytes) {
|
|
bufView[i] = bytes.charCodeAt(i);
|
|
}
|
|
const view = new DataView(arr);
|
|
return view.getInt32(0);
|
|
}
|
|
packHex(hex) {
|
|
let str = "";
|
|
for (let n = 0; n < hex.length; n += 2) {
|
|
str += String.fromCharCode(parseInt(hex.substr(n, 2), 16));
|
|
}
|
|
return str;
|
|
}
|
|
unpackHex(bytes) {
|
|
let str = "";
|
|
for (let n = 0; n < bytes.length; n++) {
|
|
let hex = Number(bytes.charCodeAt(n)).toString(16);
|
|
str += hex.length === 1 ? "0" + hex : hex;
|
|
}
|
|
return str;
|
|
}
|
|
packBytes(bytes) {
|
|
if (bytes == null) {
|
|
return null;
|
|
}
|
|
if (!(bytes instanceof ArrayBuffer || bytes instanceof Uint8Array))
|
|
throw new Error("packBytes requires ArrayBuffer or UInt8Array");
|
|
return BufferUtils.bufferToBase64(bytes);
|
|
}
|
|
unpackBytes(bytes) {
|
|
if (bytes == null) {
|
|
return null;
|
|
}
|
|
return BufferUtils.bufferFromBase64(bytes);
|
|
}
|
|
};
|
|
module2.exports = new AnyPacker();
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/Packet.js
|
|
var require_Packet = __commonJS({
|
|
"node_modules/anysocket/src/libs/Packet.js"(exports, module2) {
|
|
var AnyPacker = require_AnyPacker();
|
|
var constants = require_constants();
|
|
var getSeq = (buf) => {
|
|
return AnyPacker.unpackInt32(buf.substr(2, 4));
|
|
};
|
|
var getType = (buf) => {
|
|
return parseInt(buf.substr(1, 1));
|
|
};
|
|
var Packet = class {
|
|
constructor(data) {
|
|
this.seq = 0;
|
|
this.type = 0;
|
|
this.buffer = [];
|
|
this.data = null;
|
|
if (data)
|
|
this.data = data;
|
|
}
|
|
setType(type) {
|
|
this.type = type;
|
|
return this;
|
|
}
|
|
setSeq(seq) {
|
|
this.seq = seq;
|
|
return this;
|
|
}
|
|
setReplyTo(replyTo) {
|
|
if (replyTo)
|
|
this.seq = -replyTo;
|
|
return this;
|
|
}
|
|
async serialize(max_packet_size, encryptFnc) {
|
|
max_packet_size = max_packet_size || Number.MAX_SAFE_INTEGER;
|
|
let packet = JSON.stringify(this.data);
|
|
const count = Math.ceil(packet.length / max_packet_size);
|
|
const chunks = new Array(count);
|
|
for (let i = 0, o = 0; i < count; ++i, o += max_packet_size) {
|
|
chunks[i] = await this.encode(i == count - 1 ? constants.PACKET_LENGTH.FULL.toString() : constants.PACKET_LENGTH.PARTIAL.toString(), packet.substr(o, max_packet_size), encryptFnc);
|
|
}
|
|
return chunks;
|
|
}
|
|
async encode(eol, packet, encryptFnc) {
|
|
return eol + this.type.toString() + AnyPacker.packInt32(this.seq) + await encryptFnc(packet, Math.abs(this.seq));
|
|
}
|
|
async deserialize(buf, encryptionState, decryptFnc) {
|
|
decryptFnc = decryptFnc || ((packet) => Promise.resolve(packet));
|
|
const eol = buf.substr(0, 1) == constants.PACKET_LENGTH.FULL;
|
|
this.type = getType(buf);
|
|
this.seq = getSeq(buf);
|
|
this.buffer.push(await decryptFnc(encryptionState, buf.substr(6), Math.abs(this.seq)));
|
|
if (eol) {
|
|
try {
|
|
this.buffer = this.buffer.join("");
|
|
let packet = JSON.parse(this.buffer);
|
|
this.buffer = [];
|
|
this.data = packet;
|
|
} catch (e) {
|
|
console.log("packet error", e);
|
|
this.data = null;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
module2.exports = {
|
|
data: (data) => {
|
|
data = data || {};
|
|
return new Packet(data);
|
|
},
|
|
buffer: () => {
|
|
return new Packet();
|
|
},
|
|
getSeq: (buf) => {
|
|
return getSeq(buf);
|
|
},
|
|
getType: (buf) => {
|
|
return getType(buf);
|
|
},
|
|
isForwardPacket(buf) {
|
|
return buf.substr(0, 1) == constants.PACKET_TYPE.FORWARD;
|
|
},
|
|
TYPE: constants.PACKET_TYPE
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/AnyPacket.js
|
|
var require_AnyPacket = __commonJS({
|
|
"node_modules/anysocket/src/libs/AnyPacket.js"(exports, module2) {
|
|
var debug = require_browser()("AnyPacket");
|
|
var _send = Symbol("send function");
|
|
module2.exports = class AnyPacket {
|
|
constructor(peer, message, sendFnc) {
|
|
this.peer = peer;
|
|
this.seq = message.seq;
|
|
this.msg = message.data;
|
|
this[_send] = sendFnc;
|
|
}
|
|
reply(message) {
|
|
this[_send](message, this.seq);
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/AnyPeer.js
|
|
var require_AnyPeer = __commonJS({
|
|
"node_modules/anysocket/src/libs/AnyPeer.js"(exports, module2) {
|
|
var debug = require_browser()("AnyPeer");
|
|
var constants = require_constants();
|
|
var EventEmitter2 = require_events_wrapper();
|
|
var Packet = require_Packet();
|
|
var AnyPacket = require_AnyPacket();
|
|
var AnyPacker = require_AnyPacker();
|
|
var _protocol = Symbol("private protocol");
|
|
var _packets = Symbol("packets");
|
|
var _links = Symbol("links");
|
|
var BufferUtils = require_utils_buffer3();
|
|
var isBoolean = function(obj) {
|
|
return obj === true || obj === false || toString.call(obj) === "[object Boolean]";
|
|
};
|
|
module2.exports = class AnyPeer extends EventEmitter2 {
|
|
constructor(protocol) {
|
|
super();
|
|
this[_links] = {};
|
|
this[_protocol] = protocol;
|
|
this[_packets] = {};
|
|
this.id = protocol.peerID;
|
|
this.connectionID = protocol.connectionID;
|
|
this.syncedTime = null;
|
|
this.options = protocol.options;
|
|
const handlers = {
|
|
get: (target, name) => {
|
|
const prop = target[name];
|
|
if (prop != null) {
|
|
return prop;
|
|
}
|
|
if (!target.path)
|
|
target.path = [];
|
|
target.path.push(name);
|
|
return new Proxy(target, {
|
|
get: handlers.get,
|
|
apply: (target2, name2, args) => {
|
|
let path = target2.path;
|
|
target2.path = [];
|
|
return new Promise((resolve, reject) => {
|
|
let binary = [];
|
|
for (let item in args) {
|
|
if (BufferUtils.isBuffer(args[item])) {
|
|
args[item] = AnyPacker.packBytes(args[item]);
|
|
binary.push(item);
|
|
}
|
|
}
|
|
const packet = Packet.data({
|
|
type: constants.INTERNAL_PACKET_TYPE.RPC,
|
|
method: path,
|
|
params: args || null,
|
|
bin: binary
|
|
}).setType(constants.PACKET_TYPE.INTERNAL);
|
|
this._send(packet, true).then((packet2) => {
|
|
if (packet2.msg.error) {
|
|
reject(packet2.msg);
|
|
} else {
|
|
let result = packet2.msg.result;
|
|
if (packet2.msg.bin)
|
|
result = AnyPacker.unpackBytes(result);
|
|
resolve(result);
|
|
}
|
|
}).catch((e) => {
|
|
reject(packet.msg);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
};
|
|
this.rpc = new Proxy(() => {
|
|
}, handlers);
|
|
protocol.on("internal", this.onInternalComs.bind(this));
|
|
protocol.on("message", this.onMessage.bind(this));
|
|
protocol.on("e2e", () => {
|
|
this.onE2E();
|
|
});
|
|
protocol.on("disconnected", (peer, reason) => {
|
|
this.emit("disconnected", peer, reason);
|
|
});
|
|
}
|
|
isProxy() {
|
|
return this[_protocol].isProxy();
|
|
}
|
|
addLink(peer) {
|
|
this[_links][peer.id] = peer;
|
|
}
|
|
removeLink(peer) {
|
|
delete this[_links][peer.id];
|
|
}
|
|
getLinks() {
|
|
return this[_links];
|
|
}
|
|
getSyncedTime(refresh) {
|
|
refresh = refresh || false;
|
|
return new Promise((resolve, reject) => {
|
|
if (!refresh && this.syncedTime) {
|
|
resolve(Object.assign({
|
|
time: Date.now() + this.syncedTime.offset
|
|
}, this.syncedTime));
|
|
} else {
|
|
let clientTimestamp = Date.now();
|
|
this.sendInternal({
|
|
type: constants.INTERNAL_PACKET_TYPE.SYNCED_TIME,
|
|
time: clientTimestamp
|
|
}, true).then((packet) => {
|
|
const T1 = packet.msg.o;
|
|
const T2 = packet.msg.t;
|
|
const T3 = packet.msg.t;
|
|
const T4 = Date.now();
|
|
this.syncedTime = {
|
|
rtt: T4 - T1 - (T3 - T2),
|
|
offset: (T2 - T1 - (T4 - T3)) / 2
|
|
};
|
|
resolve(Object.assign({
|
|
time: Date.now() + this.syncedTime.offset
|
|
}, this.syncedTime));
|
|
}).catch(reject);
|
|
}
|
|
});
|
|
}
|
|
e2e() {
|
|
this[_protocol].e2e();
|
|
}
|
|
isE2EEnabled() {
|
|
return this[_protocol].hasE2EEnabled();
|
|
}
|
|
send(message, awaitReply, timeout) {
|
|
const packet = Packet.data(message).setType(constants.PACKET_TYPE.LINK);
|
|
return this._send(packet, awaitReply, timeout);
|
|
}
|
|
forward(packet) {
|
|
this[_protocol].forward(packet);
|
|
}
|
|
sendInternal(message, awaitReply, timeout) {
|
|
const packet = Packet.data(message).setType(constants.PACKET_TYPE.INTERNAL);
|
|
return this._send(packet, awaitReply, timeout);
|
|
}
|
|
onMessage(peer, message) {
|
|
if (message.seq < 0) {
|
|
if (!this._resolveReply(message)) {
|
|
debug("Dropped reply " + message.seq + ". Delivered after Timeout");
|
|
}
|
|
return;
|
|
}
|
|
this.emit("message", new AnyPacket(this, message, this.send.bind(this)));
|
|
}
|
|
onE2E() {
|
|
this.emit("e2e", this);
|
|
}
|
|
onInternalComs(peer, message) {
|
|
if (message.seq < 0) {
|
|
if (!this._resolveReply(message)) {
|
|
debug("Dropped reply " + message.seq + ". Delivered after Timeout");
|
|
}
|
|
return;
|
|
}
|
|
if (message.type == constants.PACKET_TYPE.INTERNAL) {
|
|
this.emit("internal", new AnyPacket(this, message, this.sendInternal.bind(this)));
|
|
} else {
|
|
debug("Dropped internal packet!", message);
|
|
}
|
|
}
|
|
disconnect(reason) {
|
|
for (let seq in this[_packets]) {
|
|
clearTimeout(this[_packets][seq].timeout);
|
|
this[_packets][seq].reject("Peer disconnected!");
|
|
}
|
|
this[_packets] = {};
|
|
this[_protocol].disconnect(reason);
|
|
}
|
|
_send(packet, awaitReply, timeout) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!this[_protocol].isConnected()) {
|
|
reject("Cannot send message. Peer is disconnected");
|
|
return;
|
|
}
|
|
if (!isBoolean(awaitReply) && awaitReply && awaitReply > 0) {
|
|
packet.setReplyTo(awaitReply);
|
|
}
|
|
this[_protocol].send(packet);
|
|
if (isBoolean(awaitReply) && awaitReply === true) {
|
|
this[_packets][packet.seq] = {
|
|
time: new Date().getTime(),
|
|
resolve,
|
|
reject,
|
|
timeout: setTimeout(() => {
|
|
if (this[_packets][packet.seq]) {
|
|
let msg = this[_packets][packet.seq];
|
|
delete this[_packets][packet.seq];
|
|
this.disconnect("Missed reply timeout! Packet Type: " + Packet.TYPE._string(packet.type) + " - " + packet.seq);
|
|
msg.reject("Timeout!");
|
|
}
|
|
}, timeout || this[_protocol].options.replyTimeout)
|
|
};
|
|
}
|
|
});
|
|
}
|
|
_recvForward(packet) {
|
|
this[_protocol]._recvPacketQueue.push({
|
|
peer: this[_protocol].peer,
|
|
recv: packet.msg,
|
|
state: this[_protocol].ENCRYPTION_STATE
|
|
});
|
|
}
|
|
_resolveReply(message) {
|
|
message.seq *= -1;
|
|
if (this[_packets][message.seq]) {
|
|
const tmp = this[_packets][message.seq];
|
|
delete this[_packets][message.seq];
|
|
clearTimeout(tmp.timeout);
|
|
tmp.resolve(new AnyPacket(this, message, () => {
|
|
debug("Cannot reply to a reply packet!");
|
|
}));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/AnyMesh.js
|
|
var require_AnyMesh = __commonJS({
|
|
"node_modules/anysocket/src/libs/AnyMesh.js"(exports, module2) {
|
|
var AnyMesh = class {
|
|
constructor(anysocket) {
|
|
this.anysocket = anysocket;
|
|
}
|
|
};
|
|
module2.exports = AnyMesh;
|
|
}
|
|
});
|
|
|
|
// node_modules/reusify/reusify.js
|
|
var require_reusify = __commonJS({
|
|
"node_modules/reusify/reusify.js"(exports, module2) {
|
|
"use strict";
|
|
function reusify(Constructor) {
|
|
var head = new Constructor();
|
|
var tail = head;
|
|
function get() {
|
|
var current = head;
|
|
if (current.next) {
|
|
head = current.next;
|
|
} else {
|
|
head = new Constructor();
|
|
tail = head;
|
|
}
|
|
current.next = null;
|
|
return current;
|
|
}
|
|
function release(obj) {
|
|
tail.next = obj;
|
|
tail = obj;
|
|
}
|
|
return {
|
|
get,
|
|
release
|
|
};
|
|
}
|
|
module2.exports = reusify;
|
|
}
|
|
});
|
|
|
|
// node_modules/fastq/queue.js
|
|
var require_queue = __commonJS({
|
|
"node_modules/fastq/queue.js"(exports, module2) {
|
|
"use strict";
|
|
var reusify = require_reusify();
|
|
function fastqueue(context, worker, concurrency) {
|
|
if (typeof context === "function") {
|
|
concurrency = worker;
|
|
worker = context;
|
|
context = null;
|
|
}
|
|
if (concurrency < 1) {
|
|
throw new Error("fastqueue concurrency must be greater than 1");
|
|
}
|
|
var cache = reusify(Task);
|
|
var queueHead = null;
|
|
var queueTail = null;
|
|
var _running = 0;
|
|
var errorHandler = null;
|
|
var self = {
|
|
push,
|
|
drain: noop,
|
|
saturated: noop,
|
|
pause,
|
|
paused: false,
|
|
concurrency,
|
|
running,
|
|
resume,
|
|
idle,
|
|
length,
|
|
getQueue,
|
|
unshift,
|
|
empty: noop,
|
|
kill,
|
|
killAndDrain,
|
|
error
|
|
};
|
|
return self;
|
|
function running() {
|
|
return _running;
|
|
}
|
|
function pause() {
|
|
self.paused = true;
|
|
}
|
|
function length() {
|
|
var current = queueHead;
|
|
var counter = 0;
|
|
while (current) {
|
|
current = current.next;
|
|
counter++;
|
|
}
|
|
return counter;
|
|
}
|
|
function getQueue() {
|
|
var current = queueHead;
|
|
var tasks = [];
|
|
while (current) {
|
|
tasks.push(current.value);
|
|
current = current.next;
|
|
}
|
|
return tasks;
|
|
}
|
|
function resume() {
|
|
if (!self.paused)
|
|
return;
|
|
self.paused = false;
|
|
for (var i = 0; i < self.concurrency; i++) {
|
|
_running++;
|
|
release();
|
|
}
|
|
}
|
|
function idle() {
|
|
return _running === 0 && self.length() === 0;
|
|
}
|
|
function push(value, done) {
|
|
var current = cache.get();
|
|
current.context = context;
|
|
current.release = release;
|
|
current.value = value;
|
|
current.callback = done || noop;
|
|
current.errorHandler = errorHandler;
|
|
if (_running === self.concurrency || self.paused) {
|
|
if (queueTail) {
|
|
queueTail.next = current;
|
|
queueTail = current;
|
|
} else {
|
|
queueHead = current;
|
|
queueTail = current;
|
|
self.saturated();
|
|
}
|
|
} else {
|
|
_running++;
|
|
worker.call(context, current.value, current.worked);
|
|
}
|
|
}
|
|
function unshift(value, done) {
|
|
var current = cache.get();
|
|
current.context = context;
|
|
current.release = release;
|
|
current.value = value;
|
|
current.callback = done || noop;
|
|
if (_running === self.concurrency || self.paused) {
|
|
if (queueHead) {
|
|
current.next = queueHead;
|
|
queueHead = current;
|
|
} else {
|
|
queueHead = current;
|
|
queueTail = current;
|
|
self.saturated();
|
|
}
|
|
} else {
|
|
_running++;
|
|
worker.call(context, current.value, current.worked);
|
|
}
|
|
}
|
|
function release(holder) {
|
|
if (holder) {
|
|
cache.release(holder);
|
|
}
|
|
var next = queueHead;
|
|
if (next) {
|
|
if (!self.paused) {
|
|
if (queueTail === queueHead) {
|
|
queueTail = null;
|
|
}
|
|
queueHead = next.next;
|
|
next.next = null;
|
|
worker.call(context, next.value, next.worked);
|
|
if (queueTail === null) {
|
|
self.empty();
|
|
}
|
|
} else {
|
|
_running--;
|
|
}
|
|
} else if (--_running === 0) {
|
|
self.drain();
|
|
}
|
|
}
|
|
function kill() {
|
|
queueHead = null;
|
|
queueTail = null;
|
|
self.drain = noop;
|
|
}
|
|
function killAndDrain() {
|
|
queueHead = null;
|
|
queueTail = null;
|
|
self.drain();
|
|
self.drain = noop;
|
|
}
|
|
function error(handler) {
|
|
errorHandler = handler;
|
|
}
|
|
}
|
|
function noop() {
|
|
}
|
|
function Task() {
|
|
this.value = null;
|
|
this.callback = noop;
|
|
this.next = null;
|
|
this.release = noop;
|
|
this.context = null;
|
|
this.errorHandler = null;
|
|
var self = this;
|
|
this.worked = function worked(err, result) {
|
|
var callback = self.callback;
|
|
var errorHandler = self.errorHandler;
|
|
var val = self.value;
|
|
self.value = null;
|
|
self.callback = noop;
|
|
if (self.errorHandler) {
|
|
errorHandler(err, val);
|
|
}
|
|
callback.call(self.context, err, result);
|
|
self.release(self);
|
|
};
|
|
}
|
|
function queueAsPromised(context, worker, concurrency) {
|
|
if (typeof context === "function") {
|
|
concurrency = worker;
|
|
worker = context;
|
|
context = null;
|
|
}
|
|
function asyncWrapper(arg, cb) {
|
|
worker.call(this, arg).then(function(res) {
|
|
cb(null, res);
|
|
}, cb);
|
|
}
|
|
var queue = fastqueue(context, asyncWrapper, concurrency);
|
|
var pushCb = queue.push;
|
|
var unshiftCb = queue.unshift;
|
|
queue.push = push;
|
|
queue.unshift = unshift;
|
|
queue.drained = drained;
|
|
return queue;
|
|
function push(value) {
|
|
var p = new Promise(function(resolve, reject) {
|
|
pushCb(value, function(err, result) {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
resolve(result);
|
|
});
|
|
});
|
|
p.catch(noop);
|
|
return p;
|
|
}
|
|
function unshift(value) {
|
|
var p = new Promise(function(resolve, reject) {
|
|
unshiftCb(value, function(err, result) {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
resolve(result);
|
|
});
|
|
});
|
|
p.catch(noop);
|
|
return p;
|
|
}
|
|
function drained() {
|
|
if (queue.idle()) {
|
|
return new Promise(function(resolve) {
|
|
resolve();
|
|
});
|
|
}
|
|
var previousDrain = queue.drain;
|
|
var p = new Promise(function(resolve) {
|
|
queue.drain = function() {
|
|
previousDrain();
|
|
resolve();
|
|
};
|
|
});
|
|
return p;
|
|
}
|
|
}
|
|
module2.exports = fastqueue;
|
|
module2.exports.promise = queueAsPromised;
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/AnyProtocol.js
|
|
var require_AnyProtocol = __commonJS({
|
|
"node_modules/anysocket/src/libs/AnyProtocol.js"(exports, module2) {
|
|
var debug = require_browser()("AnyProtocol");
|
|
var EventEmitter2 = require_events_wrapper();
|
|
var FastQ = require_queue();
|
|
var Packet = require_Packet();
|
|
var Utils2 = require_utils3();
|
|
var AnyPacker = require_AnyPacker();
|
|
var constants = require_constants();
|
|
var ENCRYPTION_SECRET = Symbol("secret key");
|
|
var ENCRYPTION_PRIVATE = Symbol("private key");
|
|
var ENCRYPTION_NONCE = Symbol("nonce");
|
|
var heartbeatTimer = Symbol("heartbeat timer");
|
|
var heartbeatsMissed = Symbol("heartbeats missed");
|
|
var heartbeatPonged = Symbol("heartbeat ponged");
|
|
var authTimeout = Symbol("authTimeout");
|
|
var e2eTimeout = Symbol("e2eTimeout");
|
|
module2.exports = class AnyProtocol extends EventEmitter2 {
|
|
constructor(anysocket, peer, options) {
|
|
super();
|
|
this._seq = 0;
|
|
this[ENCRYPTION_SECRET] = null;
|
|
this[ENCRYPTION_PRIVATE] = null;
|
|
this[ENCRYPTION_NONCE] = null;
|
|
this[heartbeatTimer] = 0;
|
|
this[heartbeatsMissed] = 0;
|
|
this[heartbeatPonged] = true;
|
|
this[authTimeout] = false;
|
|
this[e2eTimeout] = false;
|
|
this.peerID = peer.id;
|
|
this.peer = peer;
|
|
this.options = Object.assign({
|
|
authTimeout: 5 * 1e3,
|
|
e2eTimeout: 5 * 1e3,
|
|
replyTimeout: 30 * 1e3,
|
|
heartbeatInterval: 5 * 1e3
|
|
}, options);
|
|
this.connectionID = this.peer.connectionID;
|
|
this.anysocket = anysocket;
|
|
this._packetQueue = FastQ(this, this.processPacketQueue.bind(this), 1);
|
|
this._linkPacketQueue = FastQ(this, this.processLinkPacketQueue.bind(this), 1);
|
|
this._recvPacketQueue = FastQ(this, this.processRecvPacketQueue.bind(this), 1);
|
|
this._recvLinkPacketQueue = FastQ(this, this.processRecvLinkPacketQueue.bind(this), 1);
|
|
this._packets = {};
|
|
this.changeState(constants.PROTOCOL_STATES.ESTABLISHED);
|
|
this.ENCRYPTION_STATE = constants.PROTOCOL_ENCRYPTION.PLAIN;
|
|
this.peer.on("message", (peer2, recv) => {
|
|
this._heartbeat(true);
|
|
this._recvPacketQueue.push({
|
|
peer: peer2,
|
|
recv,
|
|
state: this.ENCRYPTION_STATE
|
|
});
|
|
});
|
|
if (this.peer.isClient() && !this.peerID) {
|
|
this.changeState(constants.PROTOCOL_STATES.AUTHING);
|
|
this.send(Packet.data({
|
|
id: this.anysocket.id,
|
|
auth: this.anysocket.authPacket()
|
|
}).setType(Packet.TYPE.AUTH));
|
|
}
|
|
if (this.peerID) {
|
|
this.changeState(constants.PROTOCOL_STATES.CONNECTED);
|
|
}
|
|
}
|
|
isProxy() {
|
|
return !!this.peer.isProxy;
|
|
}
|
|
isConnected() {
|
|
return this.state != constants.PROTOCOL_STATES.DISCONNECTED;
|
|
}
|
|
send(packet) {
|
|
if (packet.seq == 0)
|
|
packet.setSeq(this._getSeq());
|
|
if (packet.type != Packet.TYPE.HEARTBEAT) {
|
|
this._heartbeat();
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
const rejectFnc = (e) => {
|
|
this.disconnect(e);
|
|
reject(e);
|
|
};
|
|
if (this.isLINKMessage(packet.type)) {
|
|
this._linkPacketQueue.push({
|
|
packet,
|
|
resolve,
|
|
reject: rejectFnc
|
|
});
|
|
} else {
|
|
this._send(packet, resolve, rejectFnc);
|
|
}
|
|
});
|
|
}
|
|
_send(packet, resolve, reject) {
|
|
debug(this.peerID, ">>>>", Packet.TYPE._string(packet.type), packet.seq);
|
|
packet.serialize(constants.MAX_PACKET_SIZE, this._encrypt.bind(this)).then((packet2) => {
|
|
for (let i = 0; i < packet2.length; i++) {
|
|
const item = {
|
|
packet: packet2[i],
|
|
reject
|
|
};
|
|
if (i == packet2.length - 1) {
|
|
item.resolve = resolve;
|
|
}
|
|
this._packetQueue.push(item);
|
|
}
|
|
}).catch(reject);
|
|
}
|
|
forward(packet) {
|
|
return new Promise((resolve, reject) => {
|
|
this._packetQueue.push({
|
|
packet: this._encodeForwardPacket(packet.to, packet.from, packet.msg),
|
|
resolve,
|
|
reject
|
|
});
|
|
});
|
|
}
|
|
hasE2EEnabled() {
|
|
return !!this[ENCRYPTION_PRIVATE];
|
|
}
|
|
e2e() {
|
|
Utils2.generateAESKey().then((result) => {
|
|
this[ENCRYPTION_PRIVATE] = result.private;
|
|
this[ENCRYPTION_NONCE] = result.nonce;
|
|
this.changeState(constants.PROTOCOL_STATES.SWITCHING_PROTOCOL);
|
|
this.send(Packet.data({
|
|
type: constants.PROTOCOL_ENCRYPTION.E2EE,
|
|
key: result.public,
|
|
nonce: result.nonce
|
|
}).setType(Packet.TYPE.SWITCH));
|
|
}).catch((e) => {
|
|
this.disconnect(e);
|
|
});
|
|
}
|
|
onPacket(peer, recv, encryptionState) {
|
|
return new Promise((resolve, reject) => {
|
|
let invalidPacket = true;
|
|
if (Packet.isForwardPacket(recv)) {
|
|
this.emit("forward", this.peerID, this._decodeForwardPacket(recv));
|
|
resolve();
|
|
} else {
|
|
let seq = Packet.getSeq(recv);
|
|
if (!this._packets[seq]) {
|
|
this._packets[seq] = Packet.buffer();
|
|
}
|
|
let packet = this._packets[seq];
|
|
packet.deserialize(recv, encryptionState, this._decrypt.bind(this)).then((result) => {
|
|
debug(this.peerID, "<<<<", Packet.TYPE._string(packet.type), packet.seq);
|
|
if (result) {
|
|
delete this._packets[seq];
|
|
switch (this.state) {
|
|
case constants.PROTOCOL_STATES.ESTABLISHED:
|
|
if (packet.type == Packet.TYPE.AUTH) {
|
|
invalidPacket = false;
|
|
if (!packet.data.id || !this.anysocket.onAuth(packet.data)) {
|
|
return this.disconnect("Invalid Auth Packet!");
|
|
}
|
|
this.peerID = packet.data.id;
|
|
this.send(Packet.data({
|
|
id: this.anysocket.id,
|
|
auth: this.anysocket.authPacket()
|
|
}).setType(Packet.TYPE.AUTH)).then(() => {
|
|
this.changeState(constants.PROTOCOL_STATES.CONNECTED);
|
|
this.emit("ready", this);
|
|
});
|
|
resolve();
|
|
}
|
|
break;
|
|
case constants.PROTOCOL_STATES.AUTHING:
|
|
if (packet.type == Packet.TYPE.AUTH) {
|
|
invalidPacket = false;
|
|
this.changeState(constants.PROTOCOL_STATES.CONNECTED);
|
|
if (!packet.data.id || !this.anysocket.onAuth(packet.data)) {
|
|
return this.disconnect("Invalid Auth Packet!");
|
|
}
|
|
this.peerID = packet.data.id;
|
|
this.emit("ready", this);
|
|
resolve();
|
|
}
|
|
break;
|
|
case constants.PROTOCOL_STATES.CONNECTED:
|
|
if (packet.type == Packet.TYPE.LINK) {
|
|
invalidPacket = false;
|
|
this.emit("message", this, {
|
|
seq: packet.seq,
|
|
data: packet.data
|
|
});
|
|
resolve();
|
|
} else if (packet.type == Packet.TYPE.INTERNAL) {
|
|
invalidPacket = false;
|
|
this.emit("internal", this, {
|
|
seq: packet.seq,
|
|
type: packet.type,
|
|
data: packet.data
|
|
});
|
|
resolve();
|
|
} else if (packet.type == Packet.TYPE.SWITCH) {
|
|
invalidPacket = false;
|
|
Utils2.generateAESKey().then((result2) => {
|
|
this[ENCRYPTION_PRIVATE] = result2.private;
|
|
this[ENCRYPTION_NONCE] = packet.data.nonce + result2.nonce;
|
|
return Utils2.getAESSessionKey(this[ENCRYPTION_NONCE], this.peerID, 0).then((nonce) => {
|
|
this[ENCRYPTION_NONCE] = nonce;
|
|
return Utils2.computeAESsecret(this[ENCRYPTION_PRIVATE], packet.data.key).then((secret) => {
|
|
this[ENCRYPTION_SECRET] = secret;
|
|
this.send(Packet.data({
|
|
type: constants.PROTOCOL_ENCRYPTION.E2EE,
|
|
key: result2.public,
|
|
nonce: result2.nonce
|
|
}).setType(Packet.TYPE.SWITCH)).then(() => {
|
|
this.ENCRYPTION_STATE = constants.PROTOCOL_ENCRYPTION.E2EE;
|
|
this.changeState(constants.PROTOCOL_STATES.CONNECTED);
|
|
this.emit("e2e", this);
|
|
resolve();
|
|
});
|
|
});
|
|
});
|
|
}).catch((e) => {
|
|
this.disconnect(e);
|
|
});
|
|
} else if (packet.type == Packet.TYPE.HEARTBEAT) {
|
|
invalidPacket = false;
|
|
this._heartbeatPong(packet.data);
|
|
resolve();
|
|
}
|
|
break;
|
|
case constants.PROTOCOL_STATES.SWITCHING_PROTOCOL:
|
|
if (packet.type == Packet.TYPE.SWITCH) {
|
|
invalidPacket = false;
|
|
this[ENCRYPTION_NONCE] = this[ENCRYPTION_NONCE] + packet.data.nonce;
|
|
Utils2.getAESSessionKey(this[ENCRYPTION_NONCE], this.anysocket.id, 0).then((nonce) => {
|
|
this[ENCRYPTION_NONCE] = nonce;
|
|
return Utils2.computeAESsecret(this[ENCRYPTION_PRIVATE], packet.data.key).then((secret) => {
|
|
this[ENCRYPTION_SECRET] = secret;
|
|
this.ENCRYPTION_STATE = constants.PROTOCOL_ENCRYPTION.E2EE;
|
|
this.changeState(constants.PROTOCOL_STATES.CONNECTED);
|
|
this.emit("e2e", this);
|
|
resolve();
|
|
});
|
|
}).catch((e) => {
|
|
this.disconnect(e);
|
|
});
|
|
}
|
|
break;
|
|
case constants.PROTOCOL_STATES.DISCONNECTED:
|
|
invalidPacket = false;
|
|
resolve();
|
|
break;
|
|
}
|
|
if (invalidPacket) {
|
|
console.log("Invalid packet received! RECV:", packet);
|
|
return this.disconnect("Invalid Packet!");
|
|
}
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
changeState(state) {
|
|
this.state = state;
|
|
switch (this.state) {
|
|
case constants.PROTOCOL_STATES.ESTABLISHED:
|
|
this[authTimeout] = setTimeout(() => {
|
|
this.disconnect("auth timed out");
|
|
}, this.options.authTimeout);
|
|
this._linkPacketQueue.pause();
|
|
this._recvLinkPacketQueue.pause();
|
|
break;
|
|
case constants.PROTOCOL_STATES.AUTHING:
|
|
clearTimeout(this[authTimeout]);
|
|
this[authTimeout] = false;
|
|
this._linkPacketQueue.pause();
|
|
this._recvLinkPacketQueue.pause();
|
|
break;
|
|
case constants.PROTOCOL_STATES.CONNECTED:
|
|
clearTimeout(this[authTimeout]);
|
|
this[authTimeout] = false;
|
|
clearTimeout(this[e2eTimeout]);
|
|
this[e2eTimeout] = false;
|
|
this._linkPacketQueue.resume();
|
|
this._recvLinkPacketQueue.resume();
|
|
break;
|
|
case constants.PROTOCOL_STATES.SWITCHING_PROTOCOL:
|
|
this[e2eTimeout] = setTimeout(() => {
|
|
this.disconnect("e2e timed out");
|
|
}, this.options.e2eTimeout);
|
|
this._linkPacketQueue.pause();
|
|
this._recvLinkPacketQueue.pause();
|
|
break;
|
|
case constants.PROTOCOL_STATES.DISCONNECTED:
|
|
this._packetQueue.pause();
|
|
this._packetQueue.kill();
|
|
this._linkPacketQueue.pause();
|
|
this._linkPacketQueue.kill();
|
|
this._recvPacketQueue.pause();
|
|
this._recvPacketQueue.kill();
|
|
this._recvLinkPacketQueue.pause();
|
|
this._recvLinkPacketQueue.kill();
|
|
break;
|
|
}
|
|
}
|
|
disconnect(reason) {
|
|
this.changeState(constants.PROTOCOL_STATES.DISCONNECTED);
|
|
this._heartbeat();
|
|
if (this.isProxy()) {
|
|
this.anysocket.unproxy(this.peer.id, this.peer.socket.id, reason);
|
|
} else {
|
|
this.peer.disconnect(reason);
|
|
}
|
|
}
|
|
processPacketQueue(item, cb) {
|
|
this.peer.send(item.packet).then(() => {
|
|
if (item.resolve)
|
|
item.resolve();
|
|
cb(null, null);
|
|
}).catch((e) => {
|
|
item.reject(e);
|
|
this._packetQueue.kill();
|
|
cb(null, null);
|
|
});
|
|
}
|
|
processLinkPacketQueue(item, cb) {
|
|
this._send(item.packet, item.resolve, item.reject);
|
|
cb(null, null);
|
|
}
|
|
processRecvPacketQueue(item, cb) {
|
|
if (Packet.isForwardPacket(item.recv)) {
|
|
this.emit("forward", this.peerID, this._decodeForwardPacket(item.recv));
|
|
cb(null, null);
|
|
} else {
|
|
if (this.isLINKMessage(Packet.getType(item.recv))) {
|
|
item.state = this.ENCRYPTION_STATE;
|
|
this._recvLinkPacketQueue.push(item);
|
|
cb(null, null);
|
|
} else {
|
|
this.onPacket(item.peer, item.recv, item.state).then(() => {
|
|
cb(null, null);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
processRecvLinkPacketQueue(item, cb) {
|
|
this.onPacket(item.peer, item.recv, item.state).then(() => {
|
|
cb(null, null);
|
|
});
|
|
}
|
|
_encrypt(packet, seq) {
|
|
return new Promise((resolve) => {
|
|
switch (this.ENCRYPTION_STATE) {
|
|
case constants.PROTOCOL_ENCRYPTION.PLAIN:
|
|
resolve(packet);
|
|
break;
|
|
case constants.PROTOCOL_ENCRYPTION.E2EE:
|
|
Utils2.getAESSessionKey(this[ENCRYPTION_SECRET], this[ENCRYPTION_NONCE], seq).then((secretKey) => {
|
|
return Utils2.encryptAES(secretKey, packet).then(resolve);
|
|
}).catch((e) => {
|
|
this.disconnect(e);
|
|
});
|
|
break;
|
|
default:
|
|
throw new Error("[encrypt] Encryption state '" + this.ENCRYPTION_STATE + "' not implemented!");
|
|
}
|
|
});
|
|
}
|
|
_decrypt(encryptionState, packet, seq) {
|
|
return new Promise((resolve) => {
|
|
switch (encryptionState) {
|
|
case constants.PROTOCOL_ENCRYPTION.PLAIN:
|
|
resolve(packet);
|
|
break;
|
|
case constants.PROTOCOL_ENCRYPTION.E2EE:
|
|
Utils2.getAESSessionKey(this[ENCRYPTION_SECRET], this[ENCRYPTION_NONCE], seq).then((secretKey) => {
|
|
return Utils2.decryptAES(secretKey, packet).then(resolve);
|
|
}).catch((e) => {
|
|
this.disconnect(e);
|
|
});
|
|
break;
|
|
default:
|
|
throw new Error("[decrypt] Encryption state '" + encryptionState + "' not implemented!");
|
|
}
|
|
});
|
|
}
|
|
_encodeForwardPacket(to, from, msg) {
|
|
return Packet.TYPE.FORWARD + AnyPacker.packHex(to) + AnyPacker.packHex(from) + msg;
|
|
}
|
|
_decodeForwardPacket(recv) {
|
|
recv = {
|
|
to: AnyPacker.unpackHex(recv.substr(1, 16)),
|
|
from: AnyPacker.unpackHex(recv.substr(17, 16)),
|
|
msg: recv.substr(33)
|
|
};
|
|
return recv;
|
|
}
|
|
_getSeq() {
|
|
if (this._seq >= 2147483647) {
|
|
this._seq = 0;
|
|
}
|
|
this._seq++;
|
|
return this._seq;
|
|
}
|
|
_heartbeat(ponged) {
|
|
ponged = ponged || false;
|
|
if (this.isProxy())
|
|
return;
|
|
clearTimeout(this[heartbeatTimer]);
|
|
if (ponged) {
|
|
this._heartbeatPong();
|
|
}
|
|
if (this.state == constants.PROTOCOL_STATES.AUTHING || this.state == constants.PROTOCOL_STATES.DISCONNECTED)
|
|
return;
|
|
this[heartbeatTimer] = setTimeout(() => {
|
|
if (!this[heartbeatPonged]) {
|
|
this[heartbeatsMissed]++;
|
|
if (this[heartbeatsMissed] >= 2) {
|
|
this.disconnect("Missed Heartbeats");
|
|
return;
|
|
}
|
|
this._heartbeat();
|
|
return;
|
|
}
|
|
this[heartbeatsMissed] = 0;
|
|
this[heartbeatPonged] = false;
|
|
const packet = Packet.data(1).setType(Packet.TYPE.HEARTBEAT);
|
|
this.send(packet).catch((e) => {
|
|
debug("Heartbeat Error:", e);
|
|
this.disconnect(e);
|
|
});
|
|
}, this.options.heartbeatInterval);
|
|
}
|
|
_heartbeatPong(data) {
|
|
if (data == 1) {
|
|
const packet = Packet.data(2).setType(Packet.TYPE.HEARTBEAT);
|
|
this.send(packet).catch((e) => {
|
|
debug("Heartbeat Error:", e);
|
|
this.disconnect(e);
|
|
});
|
|
} else {
|
|
this[heartbeatPonged] = true;
|
|
this[heartbeatsMissed] = 0;
|
|
}
|
|
}
|
|
isLINKMessage(type) {
|
|
return [Packet.TYPE.INTERNAL, Packet.TYPE.LINK].indexOf(type) != -1;
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/modules/transports/abstract/AbstractTransport.js
|
|
var require_AbstractTransport = __commonJS({
|
|
"node_modules/anysocket/src/modules/transports/abstract/AbstractTransport.js"(exports, module2) {
|
|
var EventEmitter2 = require_events_wrapper();
|
|
var Utils2 = require_utils3();
|
|
var AbstractTransport = class extends EventEmitter2 {
|
|
constructor(type, options) {
|
|
super();
|
|
this.id = Utils2.uuidv4();
|
|
this.options = Object.assign({}, options);
|
|
this.type = type;
|
|
this.peers = /* @__PURE__ */ new Map();
|
|
this.started = false;
|
|
}
|
|
listen() {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.started) {
|
|
resolve();
|
|
return;
|
|
}
|
|
this.onListen().then(() => {
|
|
this.started = true;
|
|
resolve();
|
|
}).catch((err) => {
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
connect() {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.started) {
|
|
resolve();
|
|
return;
|
|
}
|
|
this.onConnect().then(() => {
|
|
this.started = true;
|
|
resolve();
|
|
}).catch((err) => {
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
stop() {
|
|
return new Promise((resolve, reject) => {
|
|
if (!this.started) {
|
|
resolve();
|
|
return;
|
|
}
|
|
this.started = false;
|
|
for (const peer of this.peers.values()) {
|
|
peer.disconnect("Local Connection Closed");
|
|
}
|
|
this.onStop().then(() => {
|
|
resolve();
|
|
}).catch((err) => {
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
addPeer(peer) {
|
|
peer.type = this.type;
|
|
peer.on("connected", () => {
|
|
this.peers.set(peer.connectionID, peer);
|
|
this.emit("connected", peer);
|
|
});
|
|
peer.on("disconnected", (peer2, reason) => {
|
|
this.peers.delete(peer2.connectionID);
|
|
this.emit("disconnected", peer2, reason);
|
|
});
|
|
peer.init();
|
|
}
|
|
onConnect() {
|
|
throw new Error("onConnect() must be implemented");
|
|
}
|
|
onListen() {
|
|
throw new Error("onListen() must be implemented");
|
|
}
|
|
onStop() {
|
|
throw new Error("onStop() must be implemented");
|
|
}
|
|
};
|
|
__publicField(AbstractTransport, "scheme", () => {
|
|
throw new Error("static scheme() must be implemented");
|
|
});
|
|
module2.exports = AbstractTransport;
|
|
AbstractTransport.TYPE = {
|
|
CLIENT: "client",
|
|
SERVER: "server",
|
|
HTTP: "http"
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/modules/transports/abstract/AbstractPeer.js
|
|
var require_AbstractPeer = __commonJS({
|
|
"node_modules/anysocket/src/modules/transports/abstract/AbstractPeer.js"(exports, module2) {
|
|
var EventEmitter2 = require_events_wrapper();
|
|
var Utils2 = require_utils3();
|
|
var AbstractTransport = require_AbstractTransport();
|
|
var AbstractPeer = class extends EventEmitter2 {
|
|
constructor(socket) {
|
|
super();
|
|
this.connectionID = Utils2.uuidv4();
|
|
this.connected = true;
|
|
this.socket = socket;
|
|
this.type = AbstractTransport.TYPE.NONE;
|
|
this.inited = false;
|
|
}
|
|
init() {
|
|
if (this.inited)
|
|
return;
|
|
this.inited = true;
|
|
this.onConnect();
|
|
this.emit("connected", this);
|
|
}
|
|
isClient() {
|
|
if (this.type == AbstractTransport.TYPE.NONE)
|
|
throw new Error("Invalid transport type!!!");
|
|
return this.type == AbstractTransport.TYPE.CLIENT;
|
|
}
|
|
disconnect(reason) {
|
|
if (this.connected) {
|
|
this.connected = false;
|
|
this.onDisconnect();
|
|
this.emit("disconnected", this, reason);
|
|
}
|
|
}
|
|
send(message) {
|
|
throw new Error("send() must be implemented");
|
|
}
|
|
onConnect() {
|
|
throw new Error("onConnect() must be implemented");
|
|
}
|
|
onDisconnect() {
|
|
throw new Error("onDisconnect() must be implemented");
|
|
}
|
|
};
|
|
module2.exports = AbstractPeer;
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/ProxyPeer.js
|
|
var require_ProxyPeer = __commonJS({
|
|
"node_modules/anysocket/src/libs/ProxyPeer.js"(exports, module2) {
|
|
var AbstractPeer = require_AbstractPeer();
|
|
var AbstractTransport = require_AbstractTransport();
|
|
module2.exports = class ProxyPeer extends AbstractPeer {
|
|
constructor(isClient, anysocketID, peerID, socket) {
|
|
super(socket);
|
|
this.id = peerID;
|
|
this.anysocketID = anysocketID;
|
|
this.type = isClient ? AbstractTransport.TYPE.CLIENT : AbstractTransport.TYPE.SERVER;
|
|
this.isProxy = true;
|
|
this.init();
|
|
}
|
|
onConnect() {
|
|
}
|
|
send(message) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
this.socket.forward({
|
|
to: this.id,
|
|
from: this.anysocketID,
|
|
msg: message
|
|
});
|
|
resolve();
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
}
|
|
onDisconnect() {
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/libs/AnySocket.js
|
|
var require_AnySocket = __commonJS({
|
|
"node_modules/anysocket/src/libs/AnySocket.js"(exports, module2) {
|
|
var debug = require_browser()("AnySocket");
|
|
var fs = require("fs");
|
|
var EventEmitter2 = require_events_wrapper();
|
|
var Utils2 = require_utils3();
|
|
var BufferUtils = require_utils_buffer3();
|
|
var constants = require_constants();
|
|
var AnyHTTPPeer = require_AnyHTTPPeer();
|
|
var AnyRouter = require_AnyHTTPRouter();
|
|
var _private = {
|
|
peersConnected: Symbol("peers connected"),
|
|
peers: Symbol("ready peers"),
|
|
transports: Symbol("transports"),
|
|
onForward: Symbol("onForward"),
|
|
onPeerConnected: Symbol("onPeerConnected"),
|
|
onProtocolReady: Symbol("onPeerReady"),
|
|
onPeerDisconnected: Symbol("onPeerDisconnected"),
|
|
onPeerInternalMessage: Symbol("onPeerInternalMessage"),
|
|
findTransport: Symbol("findTransport"),
|
|
httpBundle: Symbol("http bundle js"),
|
|
anymesh: Symbol("AnyMesh"),
|
|
httpServer: Symbol("HTTPServer")
|
|
};
|
|
var AnyPeer = require_AnyPeer();
|
|
var AnyMesh = require_AnyMesh();
|
|
var AnyProtocol = require_AnyProtocol();
|
|
var ProxyPeer = require_ProxyPeer();
|
|
var AnySocket4 = class extends EventEmitter2 {
|
|
constructor() {
|
|
super();
|
|
this._started = false;
|
|
this.id = Utils2.uuidv4();
|
|
this.http = new AnyRouter();
|
|
debug("AnySocketID:", this.id);
|
|
this.rpc = {};
|
|
this[_private.peersConnected] = {};
|
|
this[_private.peers] = {};
|
|
this[_private.transports] = {};
|
|
this[_private.httpServer] = null;
|
|
this[_private.anymesh] = null;
|
|
if (typeof window === "undefined") {
|
|
this[_private.httpBundle] = fs.readFileSync(__dirname + "/../../dist/anysocket.browser.js");
|
|
}
|
|
return this;
|
|
}
|
|
filter(options) {
|
|
}
|
|
broadcast(message, awaitReply) {
|
|
awaitReply = awaitReply || false;
|
|
return new Promise((resolve, reject) => {
|
|
const promises = [];
|
|
for (let p in this[_private.peers]) {
|
|
p = this[_private.peers][p];
|
|
promises.push(p.send(message, awaitReply));
|
|
Promise.all(promises).then(resolve).catch(reject);
|
|
}
|
|
});
|
|
}
|
|
mesh() {
|
|
if (this._started)
|
|
throw new Error("Cannot enable Mesh while AnySocket is running. You must first stop AnySocket!");
|
|
this[_private.anymesh] = new AnyMesh(this);
|
|
}
|
|
setRPC(rpc) {
|
|
this.rpc = rpc;
|
|
}
|
|
canProxy(peerID, otherPeerID) {
|
|
return false;
|
|
}
|
|
proxy(peerID, throughPeerID) {
|
|
return new Promise((resolve, reject) => {
|
|
if (peerID == throughPeerID || peerID == this.id) {
|
|
reject("Cannot proxy loopback!");
|
|
return;
|
|
}
|
|
if (this[_private.peers][throughPeerID].isProxy()) {
|
|
reject("Cannot proxy via a proxy! atm... :)");
|
|
return;
|
|
}
|
|
this[_private.peers][throughPeerID].sendInternal({
|
|
type: constants.INTERNAL_PACKET_TYPE.PROXY,
|
|
action: "proxy",
|
|
id: peerID
|
|
}, true).then((packet) => {
|
|
if (packet.msg.ok && !this[_private.peers][peerID]) {
|
|
let protocol = new AnyProtocol(this, new ProxyPeer(true, this.id, peerID, this[_private.peers][throughPeerID]), this[_private.peers][throughPeerID].options);
|
|
this[_private.onProtocolReady](protocol);
|
|
resolve(this[_private.peers][peerID]);
|
|
} else {
|
|
reject("Cannot proxy!");
|
|
}
|
|
}).catch(reject);
|
|
});
|
|
}
|
|
unproxy(peerID, throughPeerID, reason) {
|
|
reason = reason || "Proxy Connection Closed";
|
|
if (this[_private.peers][peerID] && this[_private.peers][peerID].isProxy()) {
|
|
this[_private.peers][throughPeerID].sendInternal({
|
|
type: constants.INTERNAL_PACKET_TYPE.PROXY,
|
|
action: "unproxy",
|
|
id: peerID
|
|
});
|
|
this[_private.onPeerDisconnected](this[_private.peers][peerID], reason);
|
|
}
|
|
}
|
|
hasPeer(id) {
|
|
return !!this[_private.peers][id];
|
|
}
|
|
hasDirectPeer(id) {
|
|
return !!(this[_private.peers][id] && !this[_private.peers][id].isProxy());
|
|
}
|
|
server(scheme, options) {
|
|
return this.listen(scheme, options);
|
|
}
|
|
listen(scheme, options) {
|
|
this._started = true;
|
|
options = options || {};
|
|
if (typeof options == "number") {
|
|
options = { port: options };
|
|
}
|
|
options.ip = options.ip || "0.0.0.0";
|
|
if (["http", "ws"].indexOf(scheme.toLowerCase()) == -1 && !options.port)
|
|
throw new Error("Invalid port!");
|
|
if (["ws"].indexOf(scheme.toLowerCase()) != -1) {
|
|
if (!this[_private.httpServer]) {
|
|
this.listen("http", options);
|
|
}
|
|
options = {
|
|
server: this[_private.httpServer]
|
|
};
|
|
}
|
|
let transport = this[_private.findTransport](scheme);
|
|
transport = new transport("server", options);
|
|
this[_private.transports][transport.id] = transport;
|
|
transport.on("connected", (peer) => {
|
|
this[_private.onPeerConnected](peer, transport.options);
|
|
});
|
|
transport.on("disconnected", (peer, reason) => {
|
|
this[_private.onPeerDisconnected](peer, reason);
|
|
});
|
|
let result = transport.listen();
|
|
if (scheme == "http") {
|
|
this[_private.httpServer] = transport.server;
|
|
}
|
|
return result;
|
|
}
|
|
connect(scheme, ip, port, options) {
|
|
return new Promise((resolve, reject) => {
|
|
this._started = true;
|
|
options = Object.assign(options || {}, {
|
|
ip,
|
|
port
|
|
});
|
|
let transport = this[_private.findTransport](scheme);
|
|
transport = new transport("client", options);
|
|
transport.on("connected", (peer) => {
|
|
this[_private.transports][transport.id] = transport;
|
|
this[_private.onPeerConnected](peer, transport.options, resolve);
|
|
debug("Transports Added", transport.id, Object.keys(this[_private.transports]).length);
|
|
});
|
|
transport.on("disconnected", (peer, reason) => {
|
|
this[_private.transports][transport.id].stop();
|
|
delete this[_private.transports][transport.id];
|
|
this[_private.onPeerDisconnected](peer, reason);
|
|
debug("Transports left", transport.id, Object.keys(this[_private.transports]).length);
|
|
if (!this[_private.peers][peer.id]) {
|
|
reject(reason);
|
|
}
|
|
});
|
|
transport.connect().catch(reject);
|
|
});
|
|
}
|
|
stop() {
|
|
this._started = false;
|
|
return new Promise((resolve, reject) => {
|
|
const promises = [];
|
|
for (let id in this[_private.transports]) {
|
|
promises.push(this[_private.transports][id].stop());
|
|
}
|
|
Promise.all(promises).then(() => {
|
|
this[_private.peersConnected] = {};
|
|
this[_private.peers] = {};
|
|
this[_private.transports] = {};
|
|
resolve();
|
|
}).catch((err) => {
|
|
throw err;
|
|
});
|
|
});
|
|
}
|
|
onAuth(packet) {
|
|
return true;
|
|
}
|
|
authPacket() {
|
|
return void 0;
|
|
}
|
|
[_private.findTransport](scheme) {
|
|
for (let name in AnySocket4.Transport) {
|
|
if (!AnySocket4.Transport.hasOwnProperty(name))
|
|
continue;
|
|
if (AnySocket4.Transport[name].scheme() == scheme) {
|
|
return AnySocket4.Transport[name];
|
|
}
|
|
}
|
|
throw new Error("Invalid scheme '" + scheme + "'");
|
|
}
|
|
[_private.onPeerConnected](peer, options, resolve) {
|
|
debug("Peer connected");
|
|
if (peer.type == "http") {
|
|
peer.on("upgrade", (req, socket) => {
|
|
let httpPeer = new AnyHTTPPeer(req, socket);
|
|
httpPeer.header("ANYSOCKET-ID", this.id);
|
|
this.http._processUpgrade(httpPeer);
|
|
this.emit("http_upgrade", httpPeer, req, socket);
|
|
});
|
|
peer.on("message", (req, res) => {
|
|
if (req.url == "/@anysocket") {
|
|
let httpPeer = new AnyHTTPPeer(req, res);
|
|
httpPeer.body(this[_private.httpBundle]);
|
|
httpPeer.end();
|
|
return;
|
|
}
|
|
req.body = "";
|
|
req.on("error", (err) => {
|
|
console.log("Err", err);
|
|
}).on("data", (chunk) => {
|
|
req.body += chunk;
|
|
if (req.body.length > 1e7)
|
|
req.connection.destroy();
|
|
}).on("end", () => {
|
|
req.body = req.body.toString();
|
|
let httpPeer = new AnyHTTPPeer(req, res);
|
|
httpPeer.header("ANYSOCKET-ID", this.id);
|
|
this.http._process(httpPeer);
|
|
this.emit("http", httpPeer, req, res);
|
|
});
|
|
});
|
|
return;
|
|
}
|
|
const anyprotocol = new AnyProtocol(this, peer, options);
|
|
this[_private.peersConnected][peer.connectionID] = anyprotocol;
|
|
anyprotocol.on("forward", this[_private.onForward].bind(this));
|
|
anyprotocol.once("ready", (protocol) => {
|
|
this[_private.onProtocolReady](protocol, resolve);
|
|
});
|
|
}
|
|
[_private.onForward](peerID, packet) {
|
|
if (this.id == packet.to) {
|
|
if (!this[_private.peers][packet.from]) {
|
|
this[_private.peers][peerID].disconnect("Invalid forward packet! Client doesn't exist!");
|
|
return;
|
|
}
|
|
this[_private.peers][packet.from]._recvForward(packet);
|
|
} else if (this.hasDirectPeer(packet.to)) {
|
|
this[_private.peers][packet.to].forward(packet);
|
|
} else {
|
|
console.error("FORWARD ERROR! We do not have the peer", packet.to);
|
|
}
|
|
}
|
|
[_private.onProtocolReady](protocol, resolve) {
|
|
if (this[_private.peers][protocol.peerID]) {
|
|
protocol.peerID = null;
|
|
protocol.disconnect("Duplicated AnySocket ID found!");
|
|
return;
|
|
}
|
|
debug("Peer ready");
|
|
const anypeer = new AnyPeer(protocol);
|
|
this[_private.peers][protocol.peerID] = anypeer;
|
|
anypeer.on("message", (packet) => {
|
|
this.emit("message", packet);
|
|
});
|
|
anypeer.on("e2e", (peer) => {
|
|
this.emit("e2e", peer);
|
|
});
|
|
anypeer.on("internal", this[_private.onPeerInternalMessage].bind(this));
|
|
if (resolve) {
|
|
resolve(anypeer);
|
|
}
|
|
setTimeout(() => {
|
|
this.emit("connected", anypeer);
|
|
}, 0);
|
|
return anypeer;
|
|
}
|
|
[_private.onPeerDisconnected](peer, reason) {
|
|
debug("Peer disconnected", reason, peer.id);
|
|
let anypeerID = null;
|
|
if (this[_private.peersConnected][peer.connectionID]) {
|
|
anypeerID = this[_private.peersConnected][peer.connectionID].peerID;
|
|
delete this[_private.peersConnected][peer.connectionID];
|
|
}
|
|
if (this[_private.peers][peer.id]) {
|
|
anypeerID = peer.id;
|
|
}
|
|
if (anypeerID) {
|
|
const anypeer = this[_private.peers][anypeerID];
|
|
delete this[_private.peers][anypeerID];
|
|
const links = anypeer.getLinks();
|
|
for (let peerID in links) {
|
|
links[peerID].sendInternal({
|
|
type: constants.INTERNAL_PACKET_TYPE.NETWORK,
|
|
action: "disconnected",
|
|
id: anypeer.id
|
|
}).catch(() => {
|
|
});
|
|
anypeer.removeLink(links[peerID]);
|
|
if (this[_private.peers][peerID]) {
|
|
this[_private.peers][peerID].removeLink(anypeer);
|
|
}
|
|
}
|
|
anypeer.disconnect();
|
|
this.emit("disconnected", anypeer, reason);
|
|
} else {
|
|
peer.disconnect();
|
|
}
|
|
}
|
|
[_private.onPeerInternalMessage](packet) {
|
|
if (packet.msg.type == constants.INTERNAL_PACKET_TYPE.NETWORK) {
|
|
if (packet.msg.action == "connected") {
|
|
if (!this[_private.peers][packet.msg.id]) {
|
|
let protocol = new AnyProtocol(this, new ProxyPeer(false, this.id, packet.msg.id, this[_private.peers][packet.peer.id]));
|
|
this[_private.onProtocolReady](protocol);
|
|
}
|
|
} else if (packet.msg.action == "disconnected") {
|
|
if (!this[_private.peers][packet.msg.id]) {
|
|
packet.peer.disconnect("Invalid proxy request!");
|
|
return;
|
|
}
|
|
this[_private.onPeerDisconnected](this[_private.peers][packet.msg.id], "Proxy Connection Closed");
|
|
}
|
|
} else if (packet.msg.type == constants.INTERNAL_PACKET_TYPE.PROXY) {
|
|
if (packet.msg.action == "proxy") {
|
|
if (!this.canProxy(packet.peer.id, packet.msg.id) || !this[_private.peers][packet.msg.id]) {
|
|
packet.peer.disconnect("Invalid proxy request!");
|
|
return;
|
|
}
|
|
if (this[_private.peers][packet.msg.id].isProxy()) {
|
|
packet.reply({
|
|
ok: false
|
|
});
|
|
return;
|
|
}
|
|
this[_private.peers][packet.msg.id].addLink(this[_private.peers][packet.peer.id]);
|
|
this[_private.peers][packet.peer.id].addLink(this[_private.peers][packet.msg.id]);
|
|
this[_private.peers][packet.msg.id].sendInternal({
|
|
type: constants.INTERNAL_PACKET_TYPE.NETWORK,
|
|
action: "connected",
|
|
id: packet.peer.id
|
|
});
|
|
packet.reply({
|
|
ok: true
|
|
});
|
|
} else if (packet.msg.action == "unproxy") {
|
|
if (!this.canProxy(packet.peer.id, packet.msg.id) || !this[_private.peers][packet.msg.id]) {
|
|
packet.peer.disconnect("Invalid proxy request!");
|
|
return;
|
|
}
|
|
this[_private.peers][packet.msg.id].removeLink(this[_private.peers][packet.peer.id]);
|
|
this[_private.peers][packet.peer.id].removeLink(this[_private.peers][packet.msg.id]);
|
|
this[_private.peers][packet.msg.id].sendInternal({
|
|
type: constants.INTERNAL_PACKET_TYPE.NETWORK,
|
|
action: "disconnected",
|
|
id: packet.peer.id
|
|
});
|
|
}
|
|
} else if (packet.msg.type == constants.INTERNAL_PACKET_TYPE.RPC) {
|
|
let parent = false;
|
|
let tmp = this.rpc;
|
|
for (let key in packet.msg.method) {
|
|
parent = tmp;
|
|
tmp = tmp[packet.msg.method[key]];
|
|
if (!tmp)
|
|
break;
|
|
}
|
|
if (!parent || !tmp || typeof tmp != "function") {
|
|
packet.reply({
|
|
error: "Method not found!",
|
|
details: "method: '" + packet.msg.method + "'",
|
|
code: 404
|
|
});
|
|
} else {
|
|
try {
|
|
for (let item of packet.msg.bin) {
|
|
packet.msg.params[item] = AnySocket4.Packer.unpack(packet.msg.params[item]);
|
|
}
|
|
Promise.resolve(tmp.apply(parent, [...packet.msg.params, packet.peer])).then((result) => {
|
|
let binary = false;
|
|
if (BufferUtils.isBuffer(result)) {
|
|
result = AnySocket4.Packer.pack(result);
|
|
binary = true;
|
|
}
|
|
packet.reply({
|
|
result,
|
|
bin: binary
|
|
});
|
|
}).catch((e) => {
|
|
packet.reply({
|
|
error: e,
|
|
details: "method: '" + packet.msg.method + "'",
|
|
code: 500
|
|
});
|
|
});
|
|
} catch (e) {
|
|
packet.reply({
|
|
error: e.message,
|
|
details: "method: '" + packet.msg.method + "'",
|
|
code: 500
|
|
});
|
|
}
|
|
}
|
|
} else if (packet.msg.type == constants.INTERNAL_PACKET_TYPE.RPC_NOTIFY) {
|
|
console.log("RPC_NOTIFY", packet.msg);
|
|
} else if (packet.msg.type == constants.INTERNAL_PACKET_TYPE.SYNCED_TIME) {
|
|
packet.reply({
|
|
o: packet.msg.time,
|
|
t: Date.now()
|
|
});
|
|
} else {
|
|
packet.peer.disconnect("Invalid internal message");
|
|
}
|
|
}
|
|
};
|
|
module2.exports = AnySocket4;
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/browser/ws.js
|
|
var require_ws = __commonJS({
|
|
"node_modules/anysocket/src/browser/ws.js"(exports, module2) {
|
|
module2.exports = class BrowserWS {
|
|
constructor(...args) {
|
|
this.ws = new WebSocket(...args);
|
|
}
|
|
on(event, fnc) {
|
|
switch (event) {
|
|
case "open":
|
|
this.ws.onopen = fnc;
|
|
break;
|
|
case "error":
|
|
this.ws.onerror = fnc;
|
|
break;
|
|
case "message":
|
|
this.ws.onmessage = (packet) => {
|
|
fnc(packet.data);
|
|
};
|
|
break;
|
|
case "close":
|
|
this.ws.onclose = fnc;
|
|
break;
|
|
default:
|
|
throw new Error("Not implemented in browser! (" + event + ")");
|
|
}
|
|
}
|
|
send(...args) {
|
|
this.ws.send(args);
|
|
}
|
|
close() {
|
|
this.ws.close();
|
|
}
|
|
terminate() {
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/ws/browser.js
|
|
var require_browser2 = __commonJS({
|
|
"node_modules/ws/browser.js"(exports, module2) {
|
|
"use strict";
|
|
module2.exports = function() {
|
|
throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object");
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/modules/transports/ws/peer.js
|
|
var require_peer = __commonJS({
|
|
"node_modules/anysocket/src/modules/transports/ws/peer.js"(exports, module2) {
|
|
var AbstractPeer = require_AbstractPeer();
|
|
module2.exports = class WSPeer extends AbstractPeer {
|
|
onConnect() {
|
|
this.socket.on("close", () => {
|
|
this.disconnect("Remote Connection Closed");
|
|
});
|
|
this.socket.on("error", (err) => {
|
|
this.emit("error", this, err);
|
|
});
|
|
this.socket.on("message", (message) => {
|
|
this.emit("message", this, message);
|
|
});
|
|
}
|
|
send(message) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
this.socket.send(message);
|
|
resolve();
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
}
|
|
onDisconnect() {
|
|
if (this.socket) {
|
|
this.socket.close();
|
|
this.socket.terminate();
|
|
this.socket = null;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/modules/transports/ws/transport.js
|
|
var require_transport = __commonJS({
|
|
"node_modules/anysocket/src/modules/transports/ws/transport.js"(exports, module2) {
|
|
var WebSocket2;
|
|
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
|
|
WebSocket2 = require_ws();
|
|
} else {
|
|
WebSocket2 = require_browser2();
|
|
}
|
|
var AbstractTransport = require_AbstractTransport();
|
|
var Peer = require_peer();
|
|
var WS = class extends AbstractTransport {
|
|
constructor(type, options) {
|
|
super(type, options);
|
|
}
|
|
static scheme() {
|
|
return "ws";
|
|
}
|
|
onListen() {
|
|
return new Promise((resolve, reject) => {
|
|
this.ws = new WebSocket2.Server({
|
|
server: this.options.server
|
|
});
|
|
this.ws.on("connection", (socket) => {
|
|
this.addPeer(new Peer(socket));
|
|
});
|
|
this.ws.on("error", (err) => {
|
|
reject(err);
|
|
});
|
|
this.ws.on("listening", () => {
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
onConnect(plain) {
|
|
return new Promise((resolve, reject) => {
|
|
let connected = false;
|
|
let opts = null;
|
|
if (this.options.cookies) {
|
|
opts = {
|
|
headers: {
|
|
Cookie: this._formatCookies(this.options.cookies)
|
|
}
|
|
};
|
|
}
|
|
let ws = new WebSocket2((plain ? "ws" : "wss") + "://" + this.options.ip + ":" + this.options.port + "/", opts);
|
|
ws.on("open", (socket) => {
|
|
connected = true;
|
|
this.addPeer(new Peer(ws));
|
|
resolve();
|
|
});
|
|
ws.on("error", (err) => {
|
|
if (!plain && !connected) {
|
|
this.onConnect(true).then(resolve).catch(reject);
|
|
} else {
|
|
reject(err);
|
|
}
|
|
connected = false;
|
|
});
|
|
});
|
|
}
|
|
onStop() {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.ws) {
|
|
this.ws.close();
|
|
this.ws = null;
|
|
}
|
|
resolve();
|
|
});
|
|
}
|
|
_formatCookies(cookies) {
|
|
let cookieString = [];
|
|
for (let key in cookies) {
|
|
cookieString.push(key + "=" + cookies[key]);
|
|
}
|
|
return cookieString.join("; ");
|
|
}
|
|
};
|
|
module2.exports = WS;
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/modules/transports/http/peer.js
|
|
var require_peer2 = __commonJS({
|
|
"node_modules/anysocket/src/modules/transports/http/peer.js"(exports, module2) {
|
|
var AbstractPeer = require_AbstractPeer();
|
|
module2.exports = class HTTPPeer extends AbstractPeer {
|
|
onConnect() {
|
|
this.socket.connectionID = this.connectionID;
|
|
this.socket.on("close", () => {
|
|
this.disconnect("Remote Connection Closed");
|
|
});
|
|
this.socket.on("error", (err) => {
|
|
this.disconnect(err);
|
|
});
|
|
}
|
|
send(message) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
this.socket.send(message);
|
|
resolve();
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
}
|
|
onDisconnect() {
|
|
if (this.socket) {
|
|
this.socket = null;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/modules/transports/http/transport.js
|
|
var require_transport2 = __commonJS({
|
|
"node_modules/anysocket/src/modules/transports/http/transport.js"(exports, module2) {
|
|
var AbstractTransport = require_AbstractTransport();
|
|
var Peer = require_peer2();
|
|
var http = require("http");
|
|
var https = require("https");
|
|
var fs = require("fs");
|
|
var HTTP = class extends AbstractTransport {
|
|
constructor(type, options) {
|
|
super(type, options);
|
|
this.type = AbstractTransport.TYPE.HTTP;
|
|
this.server = null;
|
|
this.isSecure = false;
|
|
}
|
|
static scheme() {
|
|
return "http";
|
|
}
|
|
_getSocket(req) {
|
|
let socket = req.socket._parent;
|
|
if (!socket) {
|
|
socket = req.socket;
|
|
}
|
|
return socket;
|
|
}
|
|
_handler(req, res) {
|
|
this.peers.get(this._getSocket(req).connectionID).emit("message", req, res);
|
|
}
|
|
onListen() {
|
|
return new Promise((resolve, reject) => {
|
|
if (this.options.cert && this.options.key && fs.existsSync(this.options.cert) && fs.existsSync(this.options.key)) {
|
|
this.server = https.createServer({
|
|
key: fs.readFileSync(this.options.key).toString(),
|
|
cert: fs.readFileSync(this.options.cert).toString()
|
|
}, this._handler.bind(this));
|
|
this.isSecure = true;
|
|
this.server.listen(this.options.port || 443, this.options.host || "0.0.0.0", () => {
|
|
resolve();
|
|
});
|
|
} else {
|
|
this.server = http.createServer(this._handler.bind(this));
|
|
this.server.listen(this.options.port || 80, this.options.host || "0.0.0.0", () => {
|
|
resolve();
|
|
});
|
|
}
|
|
this.server.on("connection", (socket) => {
|
|
this.addPeer(new Peer(socket));
|
|
});
|
|
this.server.on("upgrade", (req, socket) => {
|
|
this.peers.get(this._getSocket(req).connectionID).emit("upgrade", req, socket);
|
|
});
|
|
this.server.on("error", (err) => {
|
|
console.log("http err", err);
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
onConnect() {
|
|
throw new Error("not implemented!");
|
|
}
|
|
onStop() {
|
|
return new Promise((resolve) => {
|
|
if (this.server) {
|
|
this.server.close();
|
|
this.server = null;
|
|
}
|
|
resolve();
|
|
});
|
|
}
|
|
};
|
|
module2.exports = HTTP;
|
|
}
|
|
});
|
|
|
|
// node_modules/anysocket/src/index.js
|
|
var require_src = __commonJS({
|
|
"node_modules/anysocket/src/index.js"(exports, module2) {
|
|
var AnySocket4 = require_AnySocket();
|
|
var AnyPacker = require_AnyPacker();
|
|
AnySocket4.Transport = {
|
|
"WS": require_transport(),
|
|
"HTTP": require_transport2()
|
|
};
|
|
AnySocket4.Packer = {
|
|
pack: AnyPacker.packBytes.bind(AnyPacker),
|
|
unpack: AnyPacker.unpackBytes.bind(AnyPacker)
|
|
};
|
|
module2.exports = AnySocket4;
|
|
}
|
|
});
|
|
|
|
// src/libs/XTimeouts.js
|
|
var require_XTimeouts = __commonJS({
|
|
"src/libs/XTimeouts.js"(exports, module2) {
|
|
module2.exports = class XTimeouts {
|
|
constructor() {
|
|
this.timeouts = {};
|
|
this.callbacks = {};
|
|
}
|
|
execute(key) {
|
|
if (this.callbacks[key]) {
|
|
this.callbacks[key]();
|
|
}
|
|
}
|
|
executeAll() {
|
|
for (let key in this.timeouts) {
|
|
this.execute(key);
|
|
}
|
|
}
|
|
set(key, timeout, callback) {
|
|
this.clear(key);
|
|
this.callbacks[key] = () => {
|
|
this.clear(key);
|
|
callback();
|
|
};
|
|
this.timeouts[key] = setTimeout(this.callbacks[key].bind(this), timeout);
|
|
}
|
|
clear(key) {
|
|
clearTimeout(this.timeouts[key]);
|
|
delete this.timeouts[key];
|
|
delete this.callbacks[key];
|
|
}
|
|
clearAll() {
|
|
for (let key in this.timeouts) {
|
|
this.clear(key);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
// node_modules/ua-parser-js/src/ua-parser.js
|
|
var require_ua_parser = __commonJS({
|
|
"node_modules/ua-parser-js/src/ua-parser.js"(exports, module2) {
|
|
(function(window2, undefined2) {
|
|
"use strict";
|
|
var LIBVERSION = "1.0.38", EMPTY = "", UNKNOWN = "?", FUNC_TYPE = "function", UNDEF_TYPE = "undefined", OBJ_TYPE = "object", STR_TYPE = "string", MAJOR = "major", MODEL = "model", NAME = "name", TYPE = "type", VENDOR = "vendor", VERSION = "version", ARCHITECTURE = "architecture", CONSOLE = "console", MOBILE = "mobile", TABLET = "tablet", SMARTTV = "smarttv", WEARABLE = "wearable", EMBEDDED = "embedded", UA_MAX_LENGTH = 500;
|
|
var AMAZON = "Amazon", APPLE = "Apple", ASUS = "ASUS", BLACKBERRY = "BlackBerry", BROWSER = "Browser", CHROME = "Chrome", EDGE = "Edge", FIREFOX = "Firefox", GOOGLE = "Google", HUAWEI = "Huawei", LG = "LG", MICROSOFT = "Microsoft", MOTOROLA = "Motorola", OPERA = "Opera", SAMSUNG = "Samsung", SHARP = "Sharp", SONY = "Sony", XIAOMI = "Xiaomi", ZEBRA = "Zebra", FACEBOOK = "Facebook", CHROMIUM_OS = "Chromium OS", MAC_OS = "Mac OS";
|
|
var extend = function(regexes2, extensions) {
|
|
var mergedRegexes = {};
|
|
for (var i in regexes2) {
|
|
if (extensions[i] && extensions[i].length % 2 === 0) {
|
|
mergedRegexes[i] = extensions[i].concat(regexes2[i]);
|
|
} else {
|
|
mergedRegexes[i] = regexes2[i];
|
|
}
|
|
}
|
|
return mergedRegexes;
|
|
}, enumerize = function(arr) {
|
|
var enums = {};
|
|
for (var i = 0; i < arr.length; i++) {
|
|
enums[arr[i].toUpperCase()] = arr[i];
|
|
}
|
|
return enums;
|
|
}, has = function(str1, str2) {
|
|
return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
|
|
}, lowerize = function(str) {
|
|
return str.toLowerCase();
|
|
}, majorize = function(version) {
|
|
return typeof version === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split(".")[0] : undefined2;
|
|
}, trim = function(str, len) {
|
|
if (typeof str === STR_TYPE) {
|
|
str = str.replace(/^\s\s*/, EMPTY);
|
|
return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
|
|
}
|
|
};
|
|
var rgxMapper = function(ua, arrays) {
|
|
var i = 0, j, k, p, q, matches, match;
|
|
while (i < arrays.length && !matches) {
|
|
var regex = arrays[i], props = arrays[i + 1];
|
|
j = k = 0;
|
|
while (j < regex.length && !matches) {
|
|
if (!regex[j]) {
|
|
break;
|
|
}
|
|
matches = regex[j++].exec(ua);
|
|
if (!!matches) {
|
|
for (p = 0; p < props.length; p++) {
|
|
match = matches[++k];
|
|
q = props[p];
|
|
if (typeof q === OBJ_TYPE && q.length > 0) {
|
|
if (q.length === 2) {
|
|
if (typeof q[1] == FUNC_TYPE) {
|
|
this[q[0]] = q[1].call(this, match);
|
|
} else {
|
|
this[q[0]] = q[1];
|
|
}
|
|
} else if (q.length === 3) {
|
|
if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
|
|
this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined2;
|
|
} else {
|
|
this[q[0]] = match ? match.replace(q[1], q[2]) : undefined2;
|
|
}
|
|
} else if (q.length === 4) {
|
|
this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined2;
|
|
}
|
|
} else {
|
|
this[q] = match ? match : undefined2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
i += 2;
|
|
}
|
|
}, strMapper = function(str, map) {
|
|
for (var i in map) {
|
|
if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
|
|
for (var j = 0; j < map[i].length; j++) {
|
|
if (has(map[i][j], str)) {
|
|
return i === UNKNOWN ? undefined2 : i;
|
|
}
|
|
}
|
|
} else if (has(map[i], str)) {
|
|
return i === UNKNOWN ? undefined2 : i;
|
|
}
|
|
}
|
|
return str;
|
|
};
|
|
var oldSafariMap = {
|
|
"1.0": "/8",
|
|
"1.2": "/1",
|
|
"1.3": "/3",
|
|
"2.0": "/412",
|
|
"2.0.2": "/416",
|
|
"2.0.3": "/417",
|
|
"2.0.4": "/419",
|
|
"?": "/"
|
|
}, windowsVersionMap = {
|
|
"ME": "4.90",
|
|
"NT 3.11": "NT3.51",
|
|
"NT 4.0": "NT4.0",
|
|
"2000": "NT 5.0",
|
|
"XP": ["NT 5.1", "NT 5.2"],
|
|
"Vista": "NT 6.0",
|
|
"7": "NT 6.1",
|
|
"8": "NT 6.2",
|
|
"8.1": "NT 6.3",
|
|
"10": ["NT 6.4", "NT 10.0"],
|
|
"RT": "ARM"
|
|
};
|
|
var regexes = {
|
|
browser: [
|
|
[
|
|
/\b(?:crmo|crios)\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Chrome"]],
|
|
[
|
|
/edg(?:e|ios|a)?\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Edge"]],
|
|
[
|
|
/(opera mini)\/([-\w\.]+)/i,
|
|
/(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i,
|
|
/(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/opios[\/ ]+([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, OPERA + " Mini"]],
|
|
[
|
|
/\bop(?:rg)?x\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, OPERA + " GX"]],
|
|
[
|
|
/\bopr\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, OPERA]],
|
|
[
|
|
/\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Baidu"]],
|
|
[
|
|
/(kindle)\/([\w\.]+)/i,
|
|
/(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i,
|
|
/(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i,
|
|
/(?:ms|\()(ie) ([\w\.]+)/i,
|
|
/(flock|rockmelt|midori|epiphany|silk|skyfire|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i,
|
|
/(heytap|ovi)browser\/([\d\.]+)/i,
|
|
/(weibo)__([\d\.]+)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/\bddg\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "DuckDuckGo"]],
|
|
[
|
|
/(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "UC" + BROWSER]],
|
|
[
|
|
/microm.+\bqbcore\/([\w\.]+)/i,
|
|
/\bqbcore\/([\w\.]+).+microm/i,
|
|
/micromessenger\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "WeChat"]],
|
|
[
|
|
/konqueror\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Konqueror"]],
|
|
[
|
|
/trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i
|
|
],
|
|
[VERSION, [NAME, "IE"]],
|
|
[
|
|
/ya(?:search)?browser\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Yandex"]],
|
|
[
|
|
/slbrowser\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Smart Lenovo " + BROWSER]],
|
|
[
|
|
/(avast|avg)\/([\w\.]+)/i
|
|
],
|
|
[[NAME, /(.+)/, "$1 Secure " + BROWSER], VERSION],
|
|
[
|
|
/\bfocus\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, FIREFOX + " Focus"]],
|
|
[
|
|
/\bopt\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, OPERA + " Touch"]],
|
|
[
|
|
/coc_coc\w+\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Coc Coc"]],
|
|
[
|
|
/dolfin\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Dolphin"]],
|
|
[
|
|
/coast\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, OPERA + " Coast"]],
|
|
[
|
|
/miuibrowser\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "MIUI " + BROWSER]],
|
|
[
|
|
/fxios\/([-\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, FIREFOX]],
|
|
[
|
|
/\bqihu|(qi?ho?o?|360)browser/i
|
|
],
|
|
[[NAME, "360 " + BROWSER]],
|
|
[
|
|
/(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i
|
|
],
|
|
[[NAME, /(.+)/, "$1 " + BROWSER], VERSION],
|
|
[
|
|
/samsungbrowser\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, SAMSUNG + " Internet"]],
|
|
[
|
|
/(comodo_dragon)\/([\w\.]+)/i
|
|
],
|
|
[[NAME, /_/g, " "], VERSION],
|
|
[
|
|
/metasr[\/ ]?([\d\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Sogou Explorer"]],
|
|
[
|
|
/(sogou)mo\w+\/([\d\.]+)/i
|
|
],
|
|
[[NAME, "Sogou Mobile"], VERSION],
|
|
[
|
|
/(electron)\/([\w\.]+) safari/i,
|
|
/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i,
|
|
/m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/(lbbrowser)/i,
|
|
/\[(linkedin)app\]/i
|
|
],
|
|
[NAME],
|
|
[
|
|
/((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i
|
|
],
|
|
[[NAME, FACEBOOK], VERSION],
|
|
[
|
|
/(Klarna)\/([\w\.]+)/i,
|
|
/(kakao(?:talk|story))[\/ ]([\w\.]+)/i,
|
|
/(naver)\(.*?(\d+\.[\w\.]+).*\)/i,
|
|
/safari (line)\/([\w\.]+)/i,
|
|
/\b(line)\/([\w\.]+)\/iab/i,
|
|
/(alipay)client\/([\w\.]+)/i,
|
|
/(twitter)(?:and| f.+e\/([\w\.]+))/i,
|
|
/(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/\bgsa\/([\w\.]+) .*safari\//i
|
|
],
|
|
[VERSION, [NAME, "GSA"]],
|
|
[
|
|
/musical_ly(?:.+app_?version\/|_)([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "TikTok"]],
|
|
[
|
|
/headlesschrome(?:\/([\w\.]+)| )/i
|
|
],
|
|
[VERSION, [NAME, CHROME + " Headless"]],
|
|
[
|
|
/ wv\).+(chrome)\/([\w\.]+)/i
|
|
],
|
|
[[NAME, CHROME + " WebView"], VERSION],
|
|
[
|
|
/droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i
|
|
],
|
|
[VERSION, [NAME, "Android " + BROWSER]],
|
|
[
|
|
/(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i
|
|
],
|
|
[VERSION, [NAME, "Mobile Safari"]],
|
|
[
|
|
/version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i
|
|
],
|
|
[VERSION, NAME],
|
|
[
|
|
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i
|
|
],
|
|
[NAME, [VERSION, strMapper, oldSafariMap]],
|
|
[
|
|
/(webkit|khtml)\/([\w\.]+)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/(navigator|netscape\d?)\/([-\w\.]+)/i
|
|
],
|
|
[[NAME, "Netscape"], VERSION],
|
|
[
|
|
/mobile vr; rv:([\w\.]+)\).+firefox/i
|
|
],
|
|
[VERSION, [NAME, FIREFOX + " Reality"]],
|
|
[
|
|
/ekiohf.+(flow)\/([\w\.]+)/i,
|
|
/(swiftfox)/i,
|
|
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i,
|
|
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
|
|
/(firefox)\/([\w\.]+)/i,
|
|
/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i,
|
|
/(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
|
|
/(links) \(([\w\.]+)/i,
|
|
/panasonic;(viera)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/(cobalt)\/([\w\.]+)/i
|
|
],
|
|
[NAME, [VERSION, /master.|lts./, ""]]
|
|
],
|
|
cpu: [
|
|
[
|
|
/(?:(amd|x(?:(?:86|64)[-_])?|wow|win)64)[;\)]/i
|
|
],
|
|
[[ARCHITECTURE, "amd64"]],
|
|
[
|
|
/(ia32(?=;))/i
|
|
],
|
|
[[ARCHITECTURE, lowerize]],
|
|
[
|
|
/((?:i[346]|x)86)[;\)]/i
|
|
],
|
|
[[ARCHITECTURE, "ia32"]],
|
|
[
|
|
/\b(aarch64|arm(v?8e?l?|_?64))\b/i
|
|
],
|
|
[[ARCHITECTURE, "arm64"]],
|
|
[
|
|
/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i
|
|
],
|
|
[[ARCHITECTURE, "armhf"]],
|
|
[
|
|
/windows (ce|mobile); ppc;/i
|
|
],
|
|
[[ARCHITECTURE, "arm"]],
|
|
[
|
|
/((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i
|
|
],
|
|
[[ARCHITECTURE, /ower/, EMPTY, lowerize]],
|
|
[
|
|
/(sun4\w)[;\)]/i
|
|
],
|
|
[[ARCHITECTURE, "sparc"]],
|
|
[
|
|
/((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i
|
|
],
|
|
[[ARCHITECTURE, lowerize]]
|
|
],
|
|
device: [
|
|
[
|
|
/\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i
|
|
],
|
|
[MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]],
|
|
[
|
|
/\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i,
|
|
/samsung[- ]([-\w]+)/i,
|
|
/sec-(sgh\w+)/i
|
|
],
|
|
[MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]],
|
|
[
|
|
/(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i
|
|
],
|
|
[MODEL, [VENDOR, APPLE], [TYPE, MOBILE]],
|
|
[
|
|
/\((ipad);[-\w\),; ]+apple/i,
|
|
/applecoremedia\/[\w\.]+ \((ipad)/i,
|
|
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i
|
|
],
|
|
[MODEL, [VENDOR, APPLE], [TYPE, TABLET]],
|
|
[
|
|
/(macintosh);/i
|
|
],
|
|
[MODEL, [VENDOR, APPLE]],
|
|
[
|
|
/\b(sh-?[altvz]?\d\d[a-ekm]?)/i
|
|
],
|
|
[MODEL, [VENDOR, SHARP], [TYPE, MOBILE]],
|
|
[
|
|
/\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i
|
|
],
|
|
[MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]],
|
|
[
|
|
/(?:huawei|honor)([-\w ]+)[;\)]/i,
|
|
/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i
|
|
],
|
|
[MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]],
|
|
[
|
|
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i,
|
|
/\b; (\w+) build\/hm\1/i,
|
|
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i,
|
|
/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i,
|
|
/oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i,
|
|
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i
|
|
],
|
|
[[MODEL, /_/g, " "], [VENDOR, XIAOMI], [TYPE, MOBILE]],
|
|
[
|
|
/oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i,
|
|
/\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i
|
|
],
|
|
[[MODEL, /_/g, " "], [VENDOR, XIAOMI], [TYPE, TABLET]],
|
|
[
|
|
/; (\w+) bui.+ oppo/i,
|
|
/\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i
|
|
],
|
|
[MODEL, [VENDOR, "OPPO"], [TYPE, MOBILE]],
|
|
[
|
|
/\b(opd2\d{3}a?) bui/i
|
|
],
|
|
[MODEL, [VENDOR, "OPPO"], [TYPE, TABLET]],
|
|
[
|
|
/vivo (\w+)(?: bui|\))/i,
|
|
/\b(v[12]\d{3}\w?[at])(?: bui|;)/i
|
|
],
|
|
[MODEL, [VENDOR, "Vivo"], [TYPE, MOBILE]],
|
|
[
|
|
/\b(rmx[1-3]\d{3})(?: bui|;|\))/i
|
|
],
|
|
[MODEL, [VENDOR, "Realme"], [TYPE, MOBILE]],
|
|
[
|
|
/\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i,
|
|
/\bmot(?:orola)?[- ](\w*)/i,
|
|
/((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i
|
|
],
|
|
[MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]],
|
|
[
|
|
/\b(mz60\d|xoom[2 ]{0,2}) build\//i
|
|
],
|
|
[MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]],
|
|
[
|
|
/((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
|
|
],
|
|
[MODEL, [VENDOR, LG], [TYPE, TABLET]],
|
|
[
|
|
/(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
|
|
/\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i,
|
|
/\blg-?([\d\w]+) bui/i
|
|
],
|
|
[MODEL, [VENDOR, LG], [TYPE, MOBILE]],
|
|
[
|
|
/(ideatab[-\w ]+)/i,
|
|
/lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i
|
|
],
|
|
[MODEL, [VENDOR, "Lenovo"], [TYPE, TABLET]],
|
|
[
|
|
/(?:maemo|nokia).*(n900|lumia \d+)/i,
|
|
/nokia[-_ ]?([-\w\.]*)/i
|
|
],
|
|
[[MODEL, /_/g, " "], [VENDOR, "Nokia"], [TYPE, MOBILE]],
|
|
[
|
|
/(pixel c)\b/i
|
|
],
|
|
[MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]],
|
|
[
|
|
/droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i
|
|
],
|
|
[MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]],
|
|
[
|
|
/droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i
|
|
],
|
|
[MODEL, [VENDOR, SONY], [TYPE, MOBILE]],
|
|
[
|
|
/sony tablet [ps]/i,
|
|
/\b(?:sony)?sgp\w+(?: bui|\))/i
|
|
],
|
|
[[MODEL, "Xperia Tablet"], [VENDOR, SONY], [TYPE, TABLET]],
|
|
[
|
|
/ (kb2005|in20[12]5|be20[12][59])\b/i,
|
|
/(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
|
|
],
|
|
[MODEL, [VENDOR, "OnePlus"], [TYPE, MOBILE]],
|
|
[
|
|
/(alexa)webm/i,
|
|
/(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i,
|
|
/(kf[a-z]+)( bui|\)).+silk\//i
|
|
],
|
|
[MODEL, [VENDOR, AMAZON], [TYPE, TABLET]],
|
|
[
|
|
/((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i
|
|
],
|
|
[[MODEL, /(.+)/g, "Fire Phone $1"], [VENDOR, AMAZON], [TYPE, MOBILE]],
|
|
[
|
|
/(playbook);[-\w\),; ]+(rim)/i
|
|
],
|
|
[MODEL, VENDOR, [TYPE, TABLET]],
|
|
[
|
|
/\b((?:bb[a-f]|st[hv])100-\d)/i,
|
|
/\(bb10; (\w+)/i
|
|
],
|
|
[MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]],
|
|
[
|
|
/(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i
|
|
],
|
|
[MODEL, [VENDOR, ASUS], [TYPE, TABLET]],
|
|
[
|
|
/ (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i
|
|
],
|
|
[MODEL, [VENDOR, ASUS], [TYPE, MOBILE]],
|
|
[
|
|
/(nexus 9)/i
|
|
],
|
|
[MODEL, [VENDOR, "HTC"], [TYPE, TABLET]],
|
|
[
|
|
/(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i,
|
|
/(zte)[- ]([\w ]+?)(?: bui|\/|\))/i,
|
|
/(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i
|
|
],
|
|
[VENDOR, [MODEL, /_/g, " "], [TYPE, MOBILE]],
|
|
[
|
|
/droid.+; ([ab][1-7]-?[0178a]\d\d?)/i
|
|
],
|
|
[MODEL, [VENDOR, "Acer"], [TYPE, TABLET]],
|
|
[
|
|
/droid.+; (m[1-5] note) bui/i,
|
|
/\bmz-([-\w]{2,})/i
|
|
],
|
|
[MODEL, [VENDOR, "Meizu"], [TYPE, MOBILE]],
|
|
[
|
|
/; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i
|
|
],
|
|
[MODEL, [VENDOR, "Ulefone"], [TYPE, MOBILE]],
|
|
[
|
|
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
|
|
/(hp) ([\w ]+\w)/i,
|
|
/(asus)-?(\w+)/i,
|
|
/(microsoft); (lumia[\w ]+)/i,
|
|
/(lenovo)[-_ ]?([-\w]+)/i,
|
|
/(jolla)/i,
|
|
/(oppo) ?([\w ]+) bui/i
|
|
],
|
|
[VENDOR, MODEL, [TYPE, MOBILE]],
|
|
[
|
|
/(kobo)\s(ereader|touch)/i,
|
|
/(archos) (gamepad2?)/i,
|
|
/(hp).+(touchpad(?!.+tablet)|tablet)/i,
|
|
/(kindle)\/([\w\.]+)/i,
|
|
/(nook)[\w ]+build\/(\w+)/i,
|
|
/(dell) (strea[kpr\d ]*[\dko])/i,
|
|
/(le[- ]+pan)[- ]+(\w{1,9}) bui/i,
|
|
/(trinity)[- ]*(t\d{3}) bui/i,
|
|
/(gigaset)[- ]+(q\w{1,9}) bui/i,
|
|
/(vodafone) ([\w ]+)(?:\)| bui)/i
|
|
],
|
|
[VENDOR, MODEL, [TYPE, TABLET]],
|
|
[
|
|
/(surface duo)/i
|
|
],
|
|
[MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]],
|
|
[
|
|
/droid [\d\.]+; (fp\du?)(?: b|\))/i
|
|
],
|
|
[MODEL, [VENDOR, "Fairphone"], [TYPE, MOBILE]],
|
|
[
|
|
/(u304aa)/i
|
|
],
|
|
[MODEL, [VENDOR, "AT&T"], [TYPE, MOBILE]],
|
|
[
|
|
/\bsie-(\w*)/i
|
|
],
|
|
[MODEL, [VENDOR, "Siemens"], [TYPE, MOBILE]],
|
|
[
|
|
/\b(rct\w+) b/i
|
|
],
|
|
[MODEL, [VENDOR, "RCA"], [TYPE, TABLET]],
|
|
[
|
|
/\b(venue[\d ]{2,7}) b/i
|
|
],
|
|
[MODEL, [VENDOR, "Dell"], [TYPE, TABLET]],
|
|
[
|
|
/\b(q(?:mv|ta)\w+) b/i
|
|
],
|
|
[MODEL, [VENDOR, "Verizon"], [TYPE, TABLET]],
|
|
[
|
|
/\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i
|
|
],
|
|
[MODEL, [VENDOR, "Barnes & Noble"], [TYPE, TABLET]],
|
|
[
|
|
/\b(tm\d{3}\w+) b/i
|
|
],
|
|
[MODEL, [VENDOR, "NuVision"], [TYPE, TABLET]],
|
|
[
|
|
/\b(k88) b/i
|
|
],
|
|
[MODEL, [VENDOR, "ZTE"], [TYPE, TABLET]],
|
|
[
|
|
/\b(nx\d{3}j) b/i
|
|
],
|
|
[MODEL, [VENDOR, "ZTE"], [TYPE, MOBILE]],
|
|
[
|
|
/\b(gen\d{3}) b.+49h/i
|
|
],
|
|
[MODEL, [VENDOR, "Swiss"], [TYPE, MOBILE]],
|
|
[
|
|
/\b(zur\d{3}) b/i
|
|
],
|
|
[MODEL, [VENDOR, "Swiss"], [TYPE, TABLET]],
|
|
[
|
|
/\b((zeki)?tb.*\b) b/i
|
|
],
|
|
[MODEL, [VENDOR, "Zeki"], [TYPE, TABLET]],
|
|
[
|
|
/\b([yr]\d{2}) b/i,
|
|
/\b(dragon[- ]+touch |dt)(\w{5}) b/i
|
|
],
|
|
[[VENDOR, "Dragon Touch"], MODEL, [TYPE, TABLET]],
|
|
[
|
|
/\b(ns-?\w{0,9}) b/i
|
|
],
|
|
[MODEL, [VENDOR, "Insignia"], [TYPE, TABLET]],
|
|
[
|
|
/\b((nxa|next)-?\w{0,9}) b/i
|
|
],
|
|
[MODEL, [VENDOR, "NextBook"], [TYPE, TABLET]],
|
|
[
|
|
/\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i
|
|
],
|
|
[[VENDOR, "Voice"], MODEL, [TYPE, MOBILE]],
|
|
[
|
|
/\b(lvtel\-)?(v1[12]) b/i
|
|
],
|
|
[[VENDOR, "LvTel"], MODEL, [TYPE, MOBILE]],
|
|
[
|
|
/\b(ph-1) /i
|
|
],
|
|
[MODEL, [VENDOR, "Essential"], [TYPE, MOBILE]],
|
|
[
|
|
/\b(v(100md|700na|7011|917g).*\b) b/i
|
|
],
|
|
[MODEL, [VENDOR, "Envizen"], [TYPE, TABLET]],
|
|
[
|
|
/\b(trio[-\w\. ]+) b/i
|
|
],
|
|
[MODEL, [VENDOR, "MachSpeed"], [TYPE, TABLET]],
|
|
[
|
|
/\btu_(1491) b/i
|
|
],
|
|
[MODEL, [VENDOR, "Rotor"], [TYPE, TABLET]],
|
|
[
|
|
/(shield[\w ]+) b/i
|
|
],
|
|
[MODEL, [VENDOR, "Nvidia"], [TYPE, TABLET]],
|
|
[
|
|
/(sprint) (\w+)/i
|
|
],
|
|
[VENDOR, MODEL, [TYPE, MOBILE]],
|
|
[
|
|
/(kin\.[onetw]{3})/i
|
|
],
|
|
[[MODEL, /\./g, " "], [VENDOR, MICROSOFT], [TYPE, MOBILE]],
|
|
[
|
|
/droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i
|
|
],
|
|
[MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]],
|
|
[
|
|
/droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i
|
|
],
|
|
[MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]],
|
|
[
|
|
/smart-tv.+(samsung)/i
|
|
],
|
|
[VENDOR, [TYPE, SMARTTV]],
|
|
[
|
|
/hbbtv.+maple;(\d+)/i
|
|
],
|
|
[[MODEL, /^/, "SmartTV"], [VENDOR, SAMSUNG], [TYPE, SMARTTV]],
|
|
[
|
|
/(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i
|
|
],
|
|
[[VENDOR, LG], [TYPE, SMARTTV]],
|
|
[
|
|
/(apple) ?tv/i
|
|
],
|
|
[VENDOR, [MODEL, APPLE + " TV"], [TYPE, SMARTTV]],
|
|
[
|
|
/crkey/i
|
|
],
|
|
[[MODEL, CHROME + "cast"], [VENDOR, GOOGLE], [TYPE, SMARTTV]],
|
|
[
|
|
/droid.+aft(\w+)( bui|\))/i
|
|
],
|
|
[MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]],
|
|
[
|
|
/\(dtv[\);].+(aquos)/i,
|
|
/(aquos-tv[\w ]+)\)/i
|
|
],
|
|
[MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],
|
|
[
|
|
/(bravia[\w ]+)( bui|\))/i
|
|
],
|
|
[MODEL, [VENDOR, SONY], [TYPE, SMARTTV]],
|
|
[
|
|
/(mitv-\w{5}) bui/i
|
|
],
|
|
[MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]],
|
|
[
|
|
/Hbbtv.*(technisat) (.*);/i
|
|
],
|
|
[VENDOR, MODEL, [TYPE, SMARTTV]],
|
|
[
|
|
/\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i,
|
|
/hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i
|
|
],
|
|
[[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]],
|
|
[
|
|
/\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i
|
|
],
|
|
[[TYPE, SMARTTV]],
|
|
[
|
|
/(ouya)/i,
|
|
/(nintendo) ([wids3utch]+)/i
|
|
],
|
|
[VENDOR, MODEL, [TYPE, CONSOLE]],
|
|
[
|
|
/droid.+; (shield) bui/i
|
|
],
|
|
[MODEL, [VENDOR, "Nvidia"], [TYPE, CONSOLE]],
|
|
[
|
|
/(playstation [345portablevi]+)/i
|
|
],
|
|
[MODEL, [VENDOR, SONY], [TYPE, CONSOLE]],
|
|
[
|
|
/\b(xbox(?: one)?(?!; xbox))[\); ]/i
|
|
],
|
|
[MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]],
|
|
[
|
|
/((pebble))app/i
|
|
],
|
|
[VENDOR, MODEL, [TYPE, WEARABLE]],
|
|
[
|
|
/(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i
|
|
],
|
|
[MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]],
|
|
[
|
|
/droid.+; (glass) \d/i
|
|
],
|
|
[MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]],
|
|
[
|
|
/droid.+; (wt63?0{2,3})\)/i
|
|
],
|
|
[MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]],
|
|
[
|
|
/(quest( \d| pro)?)/i
|
|
],
|
|
[MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]],
|
|
[
|
|
/(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i
|
|
],
|
|
[VENDOR, [TYPE, EMBEDDED]],
|
|
[
|
|
/(aeobc)\b/i
|
|
],
|
|
[MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]],
|
|
[
|
|
/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i
|
|
],
|
|
[MODEL, [TYPE, MOBILE]],
|
|
[
|
|
/droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i
|
|
],
|
|
[MODEL, [TYPE, TABLET]],
|
|
[
|
|
/\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i
|
|
],
|
|
[[TYPE, TABLET]],
|
|
[
|
|
/(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i
|
|
],
|
|
[[TYPE, MOBILE]],
|
|
[
|
|
/(android[-\w\. ]{0,9});.+buil/i
|
|
],
|
|
[MODEL, [VENDOR, "Generic"]]
|
|
],
|
|
engine: [
|
|
[
|
|
/windows.+ edge\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, EDGE + "HTML"]],
|
|
[
|
|
/webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "Blink"]],
|
|
[
|
|
/(presto)\/([\w\.]+)/i,
|
|
/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i,
|
|
/ekioh(flow)\/([\w\.]+)/i,
|
|
/(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i,
|
|
/(icab)[\/ ]([23]\.[\d\.]+)/i,
|
|
/\b(libweb)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/rv\:([\w\.]{1,9})\b.+(gecko)/i
|
|
],
|
|
[VERSION, NAME]
|
|
],
|
|
os: [
|
|
[
|
|
/microsoft (windows) (vista|xp)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i
|
|
],
|
|
[NAME, [VERSION, strMapper, windowsVersionMap]],
|
|
[
|
|
/windows nt 6\.2; (arm)/i,
|
|
/windows[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i,
|
|
/(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i
|
|
],
|
|
[[VERSION, strMapper, windowsVersionMap], [NAME, "Windows"]],
|
|
[
|
|
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i,
|
|
/(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
|
|
/cfnetwork\/.+darwin/i
|
|
],
|
|
[[VERSION, /_/g, "."], [NAME, "iOS"]],
|
|
[
|
|
/(mac os x) ?([\w\. ]*)/i,
|
|
/(macintosh|mac_powerpc\b)(?!.+haiku)/i
|
|
],
|
|
[[NAME, MAC_OS], [VERSION, /_/g, "."]],
|
|
[
|
|
/droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i
|
|
],
|
|
[VERSION, NAME],
|
|
[
|
|
/(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i,
|
|
/(blackberry)\w*\/([\w\.]*)/i,
|
|
/(tizen|kaios)[\/ ]([\w\.]+)/i,
|
|
/\((series40);/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/\(bb(10);/i
|
|
],
|
|
[VERSION, [NAME, BLACKBERRY]],
|
|
[
|
|
/(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i
|
|
],
|
|
[VERSION, [NAME, "Symbian"]],
|
|
[
|
|
/mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, FIREFOX + " OS"]],
|
|
[
|
|
/web0s;.+rt(tv)/i,
|
|
/\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "webOS"]],
|
|
[
|
|
/watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i
|
|
],
|
|
[VERSION, [NAME, "watchOS"]],
|
|
[
|
|
/crkey\/([\d\.]+)/i
|
|
],
|
|
[VERSION, [NAME, CHROME + "cast"]],
|
|
[
|
|
/(cros) [\w]+(?:\)| ([\w\.]+)\b)/i
|
|
],
|
|
[[NAME, CHROMIUM_OS], VERSION],
|
|
[
|
|
/panasonic;(viera)/i,
|
|
/(netrange)mmh/i,
|
|
/(nettv)\/(\d+\.[\w\.]+)/i,
|
|
/(nintendo|playstation) ([wids345portablevuch]+)/i,
|
|
/(xbox); +xbox ([^\);]+)/i,
|
|
/\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i,
|
|
/(mint)[\/\(\) ]?(\w*)/i,
|
|
/(mageia|vectorlinux)[; ]/i,
|
|
/([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i,
|
|
/(hurd|linux) ?([\w\.]*)/i,
|
|
/(gnu) ?([\w\.]*)/i,
|
|
/\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i,
|
|
/(haiku) (\w+)/i
|
|
],
|
|
[NAME, VERSION],
|
|
[
|
|
/(sunos) ?([\w\.\d]*)/i
|
|
],
|
|
[[NAME, "Solaris"], VERSION],
|
|
[
|
|
/((?:open)?solaris)[-\/ ]?([\w\.]*)/i,
|
|
/(aix) ((\d)(?=\.|\)| )[\w\.])*/i,
|
|
/\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i,
|
|
/(unix) ?([\w\.]*)/i
|
|
],
|
|
[NAME, VERSION]
|
|
]
|
|
};
|
|
var UAParser2 = function(ua, extensions) {
|
|
if (typeof ua === OBJ_TYPE) {
|
|
extensions = ua;
|
|
ua = undefined2;
|
|
}
|
|
if (!(this instanceof UAParser2)) {
|
|
return new UAParser2(ua, extensions).getResult();
|
|
}
|
|
var _navigator = typeof window2 !== UNDEF_TYPE && window2.navigator ? window2.navigator : undefined2;
|
|
var _ua = ua || (_navigator && _navigator.userAgent ? _navigator.userAgent : EMPTY);
|
|
var _uach = _navigator && _navigator.userAgentData ? _navigator.userAgentData : undefined2;
|
|
var _rgxmap = extensions ? extend(regexes, extensions) : regexes;
|
|
var _isSelfNav = _navigator && _navigator.userAgent == _ua;
|
|
this.getBrowser = function() {
|
|
var _browser = {};
|
|
_browser[NAME] = undefined2;
|
|
_browser[VERSION] = undefined2;
|
|
rgxMapper.call(_browser, _ua, _rgxmap.browser);
|
|
_browser[MAJOR] = majorize(_browser[VERSION]);
|
|
if (_isSelfNav && _navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) {
|
|
_browser[NAME] = "Brave";
|
|
}
|
|
return _browser;
|
|
};
|
|
this.getCPU = function() {
|
|
var _cpu = {};
|
|
_cpu[ARCHITECTURE] = undefined2;
|
|
rgxMapper.call(_cpu, _ua, _rgxmap.cpu);
|
|
return _cpu;
|
|
};
|
|
this.getDevice = function() {
|
|
var _device = {};
|
|
_device[VENDOR] = undefined2;
|
|
_device[MODEL] = undefined2;
|
|
_device[TYPE] = undefined2;
|
|
rgxMapper.call(_device, _ua, _rgxmap.device);
|
|
if (_isSelfNav && !_device[TYPE] && _uach && _uach.mobile) {
|
|
_device[TYPE] = MOBILE;
|
|
}
|
|
if (_isSelfNav && _device[MODEL] == "Macintosh" && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) {
|
|
_device[MODEL] = "iPad";
|
|
_device[TYPE] = TABLET;
|
|
}
|
|
return _device;
|
|
};
|
|
this.getEngine = function() {
|
|
var _engine = {};
|
|
_engine[NAME] = undefined2;
|
|
_engine[VERSION] = undefined2;
|
|
rgxMapper.call(_engine, _ua, _rgxmap.engine);
|
|
return _engine;
|
|
};
|
|
this.getOS = function() {
|
|
var _os = {};
|
|
_os[NAME] = undefined2;
|
|
_os[VERSION] = undefined2;
|
|
rgxMapper.call(_os, _ua, _rgxmap.os);
|
|
if (_isSelfNav && !_os[NAME] && _uach && _uach.platform && _uach.platform != "Unknown") {
|
|
_os[NAME] = _uach.platform.replace(/chrome os/i, CHROMIUM_OS).replace(/macos/i, MAC_OS);
|
|
}
|
|
return _os;
|
|
};
|
|
this.getResult = function() {
|
|
return {
|
|
ua: this.getUA(),
|
|
browser: this.getBrowser(),
|
|
engine: this.getEngine(),
|
|
os: this.getOS(),
|
|
device: this.getDevice(),
|
|
cpu: this.getCPU()
|
|
};
|
|
};
|
|
this.getUA = function() {
|
|
return _ua;
|
|
};
|
|
this.setUA = function(ua2) {
|
|
_ua = typeof ua2 === STR_TYPE && ua2.length > UA_MAX_LENGTH ? trim(ua2, UA_MAX_LENGTH) : ua2;
|
|
return this;
|
|
};
|
|
this.setUA(_ua);
|
|
return this;
|
|
};
|
|
UAParser2.VERSION = LIBVERSION;
|
|
UAParser2.BROWSER = enumerize([NAME, VERSION, MAJOR]);
|
|
UAParser2.CPU = enumerize([ARCHITECTURE]);
|
|
UAParser2.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]);
|
|
UAParser2.ENGINE = UAParser2.OS = enumerize([NAME, VERSION]);
|
|
if (typeof exports !== UNDEF_TYPE) {
|
|
if (typeof module2 !== UNDEF_TYPE && module2.exports) {
|
|
exports = module2.exports = UAParser2;
|
|
}
|
|
exports.UAParser = UAParser2;
|
|
} else {
|
|
if (typeof define === FUNC_TYPE && define.amd) {
|
|
define(function() {
|
|
return UAParser2;
|
|
});
|
|
} else if (typeof window2 !== UNDEF_TYPE) {
|
|
window2.UAParser = UAParser2;
|
|
}
|
|
}
|
|
var $ = typeof window2 !== UNDEF_TYPE && (window2.jQuery || window2.Zepto);
|
|
if ($ && !$.ua) {
|
|
var parser = new UAParser2();
|
|
$.ua = parser.getResult();
|
|
$.ua.get = function() {
|
|
return parser.getUA();
|
|
};
|
|
$.ua.set = function(ua) {
|
|
parser.setUA(ua);
|
|
var result = parser.getResult();
|
|
for (var prop in result) {
|
|
$.ua[prop] = result[prop];
|
|
}
|
|
};
|
|
}
|
|
})(typeof window === "object" ? window : exports);
|
|
}
|
|
});
|
|
|
|
// src/main.ts
|
|
var main_exports = {};
|
|
__export(main_exports, {
|
|
default: () => AnySocketSyncPlugin
|
|
});
|
|
module.exports = __toCommonJS(main_exports);
|
|
var import_obsidian6 = require("obsidian");
|
|
|
|
// src/XSync.ts
|
|
var import_obsidian3 = require("obsidian");
|
|
|
|
// src/libs/Utils.ts
|
|
var Utils_default = new class Utils {
|
|
constructor() {
|
|
this.binaryExtensions = [
|
|
"3dm",
|
|
"3ds",
|
|
"3g2",
|
|
"3gp",
|
|
"7z",
|
|
"a",
|
|
"aac",
|
|
"adp",
|
|
"afdesign",
|
|
"afphoto",
|
|
"afpub",
|
|
"ai",
|
|
"aif",
|
|
"aiff",
|
|
"alz",
|
|
"ape",
|
|
"apk",
|
|
"appimage",
|
|
"ar",
|
|
"arj",
|
|
"asf",
|
|
"au",
|
|
"avi",
|
|
"bak",
|
|
"baml",
|
|
"bh",
|
|
"bin",
|
|
"bk",
|
|
"bmp",
|
|
"btif",
|
|
"bz2",
|
|
"bzip2",
|
|
"cab",
|
|
"caf",
|
|
"cgm",
|
|
"class",
|
|
"cmx",
|
|
"cpio",
|
|
"cr2",
|
|
"cur",
|
|
"dat",
|
|
"dcm",
|
|
"deb",
|
|
"dex",
|
|
"djvu",
|
|
"dll",
|
|
"dmg",
|
|
"dng",
|
|
"doc",
|
|
"docm",
|
|
"docx",
|
|
"dot",
|
|
"dotm",
|
|
"dra",
|
|
"DS_Store",
|
|
"dsk",
|
|
"dts",
|
|
"dtshd",
|
|
"dvb",
|
|
"dwg",
|
|
"dxf",
|
|
"ecelp4800",
|
|
"ecelp7470",
|
|
"ecelp9600",
|
|
"egg",
|
|
"eol",
|
|
"eot",
|
|
"epub",
|
|
"exe",
|
|
"f4v",
|
|
"fbs",
|
|
"fh",
|
|
"fla",
|
|
"flac",
|
|
"flatpak",
|
|
"fli",
|
|
"flv",
|
|
"fpx",
|
|
"fst",
|
|
"fvt",
|
|
"g3",
|
|
"gh",
|
|
"gif",
|
|
"graffle",
|
|
"gz",
|
|
"gzip",
|
|
"h261",
|
|
"h263",
|
|
"h264",
|
|
"icns",
|
|
"ico",
|
|
"ief",
|
|
"img",
|
|
"ipa",
|
|
"iso",
|
|
"jar",
|
|
"jpeg",
|
|
"jpg",
|
|
"jpgv",
|
|
"jpm",
|
|
"jxr",
|
|
"key",
|
|
"ktx",
|
|
"lha",
|
|
"lib",
|
|
"lvp",
|
|
"lz",
|
|
"lzh",
|
|
"lzma",
|
|
"lzo",
|
|
"m3u",
|
|
"m4a",
|
|
"m4v",
|
|
"mar",
|
|
"mdi",
|
|
"mht",
|
|
"mid",
|
|
"midi",
|
|
"mj2",
|
|
"mka",
|
|
"mkv",
|
|
"mmr",
|
|
"mng",
|
|
"mobi",
|
|
"mov",
|
|
"movie",
|
|
"mp3",
|
|
"mp4",
|
|
"mp4a",
|
|
"mpeg",
|
|
"mpg",
|
|
"mpga",
|
|
"mxu",
|
|
"nef",
|
|
"npx",
|
|
"numbers",
|
|
"nupkg",
|
|
"o",
|
|
"odp",
|
|
"ods",
|
|
"odt",
|
|
"oga",
|
|
"ogg",
|
|
"ogv",
|
|
"otf",
|
|
"ott",
|
|
"pages",
|
|
"pbm",
|
|
"pcx",
|
|
"pdb",
|
|
"pdf",
|
|
"pea",
|
|
"pgm",
|
|
"pic",
|
|
"png",
|
|
"pnm",
|
|
"pot",
|
|
"potm",
|
|
"potx",
|
|
"ppa",
|
|
"ppam",
|
|
"ppm",
|
|
"pps",
|
|
"ppsm",
|
|
"ppsx",
|
|
"ppt",
|
|
"pptm",
|
|
"pptx",
|
|
"psd",
|
|
"pya",
|
|
"pyc",
|
|
"pyo",
|
|
"pyv",
|
|
"qt",
|
|
"rar",
|
|
"ras",
|
|
"raw",
|
|
"resources",
|
|
"rgb",
|
|
"rip",
|
|
"rlc",
|
|
"rmf",
|
|
"rmvb",
|
|
"rpm",
|
|
"rtf",
|
|
"rz",
|
|
"s3m",
|
|
"s7z",
|
|
"scpt",
|
|
"sgi",
|
|
"shar",
|
|
"snap",
|
|
"sil",
|
|
"sketch",
|
|
"slk",
|
|
"smv",
|
|
"snk",
|
|
"so",
|
|
"stl",
|
|
"suo",
|
|
"sub",
|
|
"swf",
|
|
"tar",
|
|
"tbz",
|
|
"tbz2",
|
|
"tga",
|
|
"tgz",
|
|
"thmx",
|
|
"tif",
|
|
"tiff",
|
|
"tlz",
|
|
"ttc",
|
|
"ttf",
|
|
"txz",
|
|
"udf",
|
|
"uvh",
|
|
"uvi",
|
|
"uvm",
|
|
"uvp",
|
|
"uvs",
|
|
"uvu",
|
|
"viv",
|
|
"vob",
|
|
"war",
|
|
"wav",
|
|
"wax",
|
|
"wbmp",
|
|
"wdp",
|
|
"weba",
|
|
"webm",
|
|
"webp",
|
|
"whl",
|
|
"wim",
|
|
"wm",
|
|
"wma",
|
|
"wmv",
|
|
"wmx",
|
|
"woff",
|
|
"woff2",
|
|
"wrm",
|
|
"wvx",
|
|
"xbm",
|
|
"xif",
|
|
"xla",
|
|
"xlam",
|
|
"xls",
|
|
"xlsb",
|
|
"xlsm",
|
|
"xlsx",
|
|
"xlt",
|
|
"xltm",
|
|
"xltx",
|
|
"xm",
|
|
"xmind",
|
|
"xpi",
|
|
"xpm",
|
|
"xwd",
|
|
"xz",
|
|
"z",
|
|
"zip",
|
|
"zipx"
|
|
];
|
|
}
|
|
debounce(func, delay) {
|
|
let timeout = null;
|
|
return () => {
|
|
if (timeout)
|
|
clearTimeout(timeout);
|
|
timeout = window.setTimeout(() => {
|
|
func();
|
|
}, delay);
|
|
};
|
|
}
|
|
async getSHA(data) {
|
|
if (!data)
|
|
return null;
|
|
let sha = await crypto.subtle.digest("SHA-256", new TextEncoder("utf-8").encode(data));
|
|
return Array.prototype.map.call(new Uint8Array(sha), (x) => ("00" + x.toString(16)).slice(-2)).join("");
|
|
}
|
|
async getSHABinary(data) {
|
|
if (!data)
|
|
return null;
|
|
let sha = await crypto.subtle.digest("SHA-256", data);
|
|
return Array.prototype.map.call(new Uint8Array(sha), (x) => ("00" + x.toString(16)).slice(-2)).join("");
|
|
}
|
|
isBinary(path) {
|
|
path = path.split(".");
|
|
let extension = path[path.length - 1];
|
|
return this.binaryExtensions.indexOf(extension) !== -1;
|
|
}
|
|
}();
|
|
|
|
// src/libs/AnysocketManager.ts
|
|
var import_Events = __toESM(require_Events());
|
|
var import_anysocket = __toESM(require_src());
|
|
var NOTICE_COLOR = "#ffaa00";
|
|
var AnysocketManager = class extends import_Events.default {
|
|
constructor(xSync) {
|
|
super();
|
|
this.eventRefs = {};
|
|
this.isConnected = false;
|
|
this.isUpdating = false;
|
|
this.notifiedOfConnectError = false;
|
|
this.peer = null;
|
|
this.xSync = xSync;
|
|
this.plugin = xSync.plugin;
|
|
this.anysocket = new import_anysocket.default();
|
|
console.log("AnySocket Sync (" + this.plugin.VERSION + ") - Enabled");
|
|
if (app.isMobile) {
|
|
activeWindow.onblur = () => {
|
|
this.emit("unload");
|
|
};
|
|
activeWindow.onfocus = () => {
|
|
this.emit("reload");
|
|
};
|
|
}
|
|
}
|
|
async getTime() {
|
|
return Date.now();
|
|
}
|
|
async init() {
|
|
this.anysocket.removeAllListeners();
|
|
let password = await Utils_default.getSHA(this.anysocket.id.substring(0, 16) + this.plugin.settings.password + this.anysocket.id.substring(16));
|
|
this.anysocket.authPacket = () => {
|
|
return password;
|
|
};
|
|
this.anysocket.onAuth = async (packet) => {
|
|
return await Utils_default.getSHA(packet.id.substring(0, 16) + this.plugin.settings.password + packet.id.substring(16)) == packet.auth;
|
|
};
|
|
this.anysocket.on("message", async (packet) => {
|
|
this.emit("message", packet);
|
|
});
|
|
this.anysocket.on("e2e", async (peer) => {
|
|
this.getTime = async () => {
|
|
return Math.round((await peer.getSyncedTime()).time);
|
|
};
|
|
await this.getTime();
|
|
app.workspace.onLayoutReady(async () => {
|
|
await this.checkForUpdates(peer);
|
|
});
|
|
});
|
|
this.anysocket.on("disconnected", (peer) => {
|
|
this.isConnected = false;
|
|
this.peer = null;
|
|
this.emit("disconnected");
|
|
this.emit("reload");
|
|
});
|
|
this.connect();
|
|
}
|
|
async checkForUpdates(peer) {
|
|
this.isUpdating = false;
|
|
let result = await peer.rpc.onVersionCheck(this.plugin.VERSION, this.plugin.BUILD);
|
|
if (result.type == "ok") {
|
|
this.peer = peer;
|
|
this.isConnected = true;
|
|
this.isUpdating = false;
|
|
this.emit("connected", peer);
|
|
} else if (result.type == "update") {
|
|
this.isUpdating = true;
|
|
await this.xSync.storage.updatePlugin(result.files);
|
|
this.anysocket.removeAllListeners("disconnected");
|
|
this.anysocket.stop();
|
|
app.plugins.disablePlugin("anysocket-sync");
|
|
if (this.plugin.BUILD >= result.build) {
|
|
this.xSync.makeNotice(NOTICE_COLOR, "Your version is ahead of the server. Downgraded fom " + this.plugin.VERSION + " to " + result.version);
|
|
} else {
|
|
this.xSync.makeNotice(NOTICE_COLOR, "Updated to version: " + result.version);
|
|
}
|
|
app.plugins.enablePlugin("anysocket-sync");
|
|
} else {
|
|
this.anysocket.removeAllListeners();
|
|
this.emit("unload");
|
|
this.xSync.makeNotice(NOTICE_COLOR, "Incompatible client version " + this.plugin.VERSION);
|
|
}
|
|
}
|
|
connect() {
|
|
if (!this.isEnabled) {
|
|
return;
|
|
}
|
|
if (!this.plugin.settings.password) {
|
|
console.log("AnySocket Sync - Requires setup");
|
|
this.xSync.makeNotice(NOTICE_COLOR, "AnySocket Sync - Requires setup");
|
|
this.emit("unload");
|
|
return;
|
|
}
|
|
this.anysocket.connect("ws", this.plugin.settings.host, this.plugin.settings.port).then(async (peer) => {
|
|
peer.e2e();
|
|
this.notifiedOfConnectError = false;
|
|
}).catch((e) => {
|
|
console.error("AnySocket Connect Error", e);
|
|
if (!this.notifiedOfConnectError && !this.isUpdating) {
|
|
this.notifiedOfConnectError = true;
|
|
this.xSync.notifyStatus(NotifyType.NOT_CONNECTED);
|
|
}
|
|
this.isConnected = false;
|
|
this.emit("reload");
|
|
});
|
|
}
|
|
async send(packet, onReply) {
|
|
if (!this.peer)
|
|
return;
|
|
if (onReply) {
|
|
packet = await this.peer.send(packet, true);
|
|
onReply(packet);
|
|
} else {
|
|
return await this.peer.send(packet);
|
|
}
|
|
}
|
|
stop() {
|
|
this.anysocket.stop();
|
|
}
|
|
};
|
|
|
|
// src/libs/fs/FSAdapter.ts
|
|
var import_obsidian = require("obsidian");
|
|
var FSAdapter = class {
|
|
constructor(basePath) {
|
|
this.basePath = basePath;
|
|
}
|
|
async makeFolder(path) {
|
|
await app.vault.createFolder((0, import_obsidian.normalizePath)(this.basePath + path)).catch(() => {
|
|
});
|
|
}
|
|
async write(path, data, mtime, binary = false) {
|
|
if (!await this.exists(path)) {
|
|
let folder = path.split("/").slice(0, -1).join("/");
|
|
if (folder) {
|
|
await this.makeFolder(folder);
|
|
}
|
|
}
|
|
if (data != null) {
|
|
let options = null;
|
|
if (mtime) {
|
|
options = {
|
|
mtime
|
|
};
|
|
}
|
|
if (binary) {
|
|
await app.vault.adapter.writeBinary((0, import_obsidian.normalizePath)(this.basePath + path), data, options);
|
|
} else {
|
|
await app.vault.adapter.write((0, import_obsidian.normalizePath)(this.basePath + path), data, options);
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
async read(path, binary = false) {
|
|
try {
|
|
if (binary) {
|
|
return await app.vault.adapter.readBinary((0, import_obsidian.normalizePath)(this.basePath + path));
|
|
}
|
|
return await app.vault.adapter.read((0, import_obsidian.normalizePath)(this.basePath + path));
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
async exists(path) {
|
|
return await app.vault.adapter.exists((0, import_obsidian.normalizePath)(this.basePath + path));
|
|
}
|
|
async delete(path) {
|
|
await app.fileManager.trashFile(this.getFile(path));
|
|
}
|
|
async iterate(callback) {
|
|
let files = app.vault.getAllLoadedFiles();
|
|
for (let file of files) {
|
|
await callback(file);
|
|
}
|
|
}
|
|
getFile(path) {
|
|
return app.vault.getAbstractFileByPath((0, import_obsidian.normalizePath)(path));
|
|
}
|
|
};
|
|
|
|
// src/libs/fs/Storage.ts
|
|
var import_obsidian2 = require("obsidian");
|
|
var Storage = class {
|
|
constructor(plugin) {
|
|
this.inited = false;
|
|
this.fsVault = new FSAdapter((0, import_obsidian2.normalizePath)("/"));
|
|
this.fsInternal = new FSAdapter(plugin.manifest.dir + "/");
|
|
}
|
|
async init() {
|
|
if (this.inited)
|
|
return;
|
|
this.tree = {};
|
|
this.inited = true;
|
|
}
|
|
async write(path, data, metadata) {
|
|
await this.writeMetadata(path, metadata);
|
|
return await this.fsVault.write(path, data, metadata.mtime);
|
|
}
|
|
async writeBinary(path, data, metadata) {
|
|
await this.writeMetadata(path, metadata);
|
|
return await this.fsVault.write(path, data, metadata.mtime, true);
|
|
}
|
|
async makeFolder(path, metadata) {
|
|
await this.writeMetadata(path, metadata);
|
|
return await this.fsVault.makeFolder(path);
|
|
}
|
|
async read(path) {
|
|
return await this.fsVault.read(path);
|
|
}
|
|
async readBinary(path) {
|
|
return await this.fsVault.read(path, true);
|
|
}
|
|
async delete(path, metadata) {
|
|
await this.writeMetadata(path, metadata);
|
|
return await this.fsVault.delete(path);
|
|
}
|
|
async exists(path) {
|
|
return await this.fsVault.exists(path);
|
|
}
|
|
async iterate(callback) {
|
|
await this.fsVault.iterate(async (item) => {
|
|
if (item.path == "/")
|
|
return;
|
|
await callback(item);
|
|
});
|
|
}
|
|
async readMetadata(path) {
|
|
if (!this.tree[path]) {
|
|
return null;
|
|
}
|
|
return this.tree[path];
|
|
}
|
|
async writeMetadata(path, metadata) {
|
|
if (!this.tree[path]) {
|
|
this.tree[path] = {};
|
|
}
|
|
for (let key in metadata) {
|
|
this.tree[path][key] = metadata[key];
|
|
}
|
|
return this.tree[path];
|
|
}
|
|
async updatePlugin(files) {
|
|
for (let item of files) {
|
|
await this.fsInternal.write(item.path, item.data);
|
|
}
|
|
}
|
|
getFileByPath(path) {
|
|
if (path.substring(0, 1) == "/") {
|
|
path = path.substring(1);
|
|
}
|
|
return this.fsVault.getFile(path);
|
|
}
|
|
};
|
|
Storage.tree = null;
|
|
|
|
// src/XSync.ts
|
|
var import_AnySocket = __toESM(require_AnySocket());
|
|
var import_XTimeouts = __toESM(require_XTimeouts());
|
|
var STATUS_OK = "#339933";
|
|
var STATUS_SYNC = "#9900ff";
|
|
var STATUS_WARN = "#ffaa00";
|
|
var STATUS_ERROR = "#cc0000";
|
|
var NotifyType = {
|
|
PLUGIN_DISABLED: "Disabled",
|
|
NOT_CONNECTED: "Not connected",
|
|
SYNCING: "Syncing...",
|
|
SYNC_COMPLETED: "Sync completed",
|
|
AUTO_SYNC_DISABLED: "Auto Sync disabled",
|
|
CONNECTION_LOST: "Connection lost",
|
|
CONNECTED: "Connected"
|
|
};
|
|
var XSync2 = class {
|
|
constructor(plugin) {
|
|
this.isEnabled = false;
|
|
this.eventRefs = {};
|
|
this.reloadTimeout = null;
|
|
this.plugin = plugin;
|
|
this.unsentSessionEvents = {};
|
|
this.anysocket = new AnysocketManager(this);
|
|
this.storage = new Storage(plugin);
|
|
this.xTimeouts = new import_XTimeouts.default();
|
|
}
|
|
async enabled(value) {
|
|
if (this.isEnabled !== value) {
|
|
this.isEnabled = value;
|
|
if (this.isEnabled) {
|
|
await this.load(false);
|
|
} else {
|
|
this.unload(false);
|
|
}
|
|
}
|
|
}
|
|
connectionOK() {
|
|
if (!this.isEnabled) {
|
|
this.notifyStatus(NotifyType.PLUGIN_DISABLED);
|
|
return false;
|
|
}
|
|
if (!this.anysocket.isConnected) {
|
|
this.notifyStatus(NotifyType.NOT_CONNECTED);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
async listVersionHistory(path, callback) {
|
|
this.anysocket.send({
|
|
type: "file_history",
|
|
data: {
|
|
type: "list_versions",
|
|
path
|
|
}
|
|
}, (packet) => {
|
|
callback(packet.msg);
|
|
});
|
|
}
|
|
async readVersionHistory(path, timestamp, callback) {
|
|
if (!this.connectionOK())
|
|
return;
|
|
this.anysocket.send({
|
|
type: "file_history",
|
|
data: {
|
|
type: "read",
|
|
binary: Utils_default.isBinary(path),
|
|
path,
|
|
timestamp
|
|
}
|
|
}, (packet) => {
|
|
callback(packet.msg);
|
|
});
|
|
}
|
|
async listFilesHistory(deletedOnly, callback) {
|
|
if (!this.connectionOK())
|
|
return;
|
|
this.anysocket.send({
|
|
type: "file_history",
|
|
data: {
|
|
type: "list_files",
|
|
mode: deletedOnly ? "deleted" : "all"
|
|
}
|
|
}, (packet) => {
|
|
callback(packet.msg);
|
|
});
|
|
}
|
|
async sync() {
|
|
if (!this.anysocket.isConnected)
|
|
return;
|
|
if (this.isSyncing)
|
|
return;
|
|
for (let key in this.unsentSessionEvents) {
|
|
let event = this.unsentSessionEvents[key];
|
|
await this.processLocalEvent(event.action, event.file, event.args, true);
|
|
}
|
|
this.unsentSessionEvents = {};
|
|
this.isSyncing = true;
|
|
this.notifyStatus(NotifyType.SYNCING);
|
|
this.debug && console.log("sync");
|
|
let data = [];
|
|
await this.storage.iterate(async (item) => {
|
|
let mtime = null;
|
|
if (item.children === void 0) {
|
|
mtime = item.stat.mtime;
|
|
} else {
|
|
mtime = await this.getFolderMtime(item);
|
|
if (mtime === false) {
|
|
return;
|
|
}
|
|
}
|
|
let result = await this.getMetadata("sync", item, mtime);
|
|
data.push({
|
|
path: item.path,
|
|
metadata: result.metadata
|
|
});
|
|
});
|
|
this.anysocket.send({
|
|
type: "sync",
|
|
data
|
|
});
|
|
}
|
|
async onSyncCompleted(peer) {
|
|
this.isSyncing = false;
|
|
this.notifyStatus(NotifyType.SYNC_COMPLETED);
|
|
}
|
|
async onFocusChanged() {
|
|
this.xTimeouts.executeAll();
|
|
}
|
|
async processLocalEvent(action, file, args, fromUnsent = false) {
|
|
if (!this.anysocket.isConnected) {
|
|
return;
|
|
}
|
|
if (!this.plugin.settings.autoSync && !fromUnsent) {
|
|
this.unsentSessionEvents[file.path] = {
|
|
action,
|
|
file,
|
|
args
|
|
};
|
|
return;
|
|
}
|
|
if (action == "rename") {
|
|
await this.processLocalEvent("delete", { path: args[0] }, null, fromUnsent);
|
|
await this.processLocalEvent("create", file, null, fromUnsent);
|
|
return;
|
|
}
|
|
let metadata = await this.getMetadata(action, file);
|
|
if (action == "modify" && this.plugin.settings.delayedSync > 0) {
|
|
this.xTimeouts.set(file.path, this.plugin.settings.delayedSync * 1e3, async () => {
|
|
await this._processLocalEvent(action, file, metadata);
|
|
});
|
|
} else {
|
|
await this._processLocalEvent(action, file, metadata);
|
|
}
|
|
}
|
|
async _processLocalEvent(action, file, metadata) {
|
|
this.debug && console.log("anysocket sync event", action, file.path, metadata);
|
|
try {
|
|
let result = metadata || await this.getMetadata(action, file);
|
|
if (!result.changed || !this.anysocket.isConnected) {
|
|
return;
|
|
}
|
|
result.metadata.path = file.path;
|
|
this.anysocket.send({
|
|
type: "file_event",
|
|
data: result.metadata
|
|
});
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
registerEvent(type) {
|
|
this.eventRefs[type] = app.vault.on(type, async (file, ...args) => {
|
|
if (!this.isEnabled)
|
|
return;
|
|
await this.processLocalEvent(type, file, args);
|
|
});
|
|
}
|
|
unregisterEvent(type) {
|
|
app.vault.offref(this.eventRefs[type]);
|
|
}
|
|
async load() {
|
|
if (!this.isEnabled)
|
|
return;
|
|
if (this.inited == true)
|
|
return;
|
|
this.inited = true;
|
|
this.anysocket.isEnabled = this.plugin.settings.syncEnabled;
|
|
this.debug = this.plugin.settings.debug;
|
|
await this.storage.init();
|
|
this.registerEvent("create");
|
|
this.registerEvent("modify");
|
|
this.registerEvent("delete");
|
|
this.registerEvent("rename");
|
|
let focusChanged = Utils_default.debounce(this.onFocusChanged.bind(this), 500);
|
|
this.eventRefs["active-leaf-change"] = app.workspace.on("active-leaf-change", focusChanged);
|
|
this.eventRefs["layout-change"] = app.workspace.on("layout-change", focusChanged);
|
|
this.anysocket.on("connected", async (peer) => {
|
|
this.notifyStatus(NotifyType.CONNECTED);
|
|
let deviceName = this.plugin.settings.deviceName || null;
|
|
if (deviceName != null && deviceName != "Unknown") {
|
|
await peer.rpc.setDeviceId(deviceName);
|
|
}
|
|
if (!this.plugin.settings.autoSync) {
|
|
await peer.rpc.autoSync(this.plugin.settings.autoSync);
|
|
}
|
|
if (this.plugin.settings.autoSync) {
|
|
await this.sync();
|
|
} else {
|
|
this.notifyStatus(NotifyType.AUTO_SYNC_DISABLED);
|
|
}
|
|
});
|
|
this.anysocket.on("message", (packet) => {
|
|
switch (packet.msg.type) {
|
|
case "file_data":
|
|
this.onFileData(packet.msg.data, packet.peer);
|
|
break;
|
|
case "sync_complete":
|
|
this.onSyncCompleted(packet.peer);
|
|
break;
|
|
}
|
|
});
|
|
this.anysocket.on("reload", this.reload.bind(this));
|
|
this.anysocket.on("unload", this.unload.bind(this));
|
|
this.anysocket.on("disconnected", () => {
|
|
this.notifyStatus(NotifyType.CONNECTION_LOST);
|
|
this.debug && console.log("disconnected");
|
|
});
|
|
this.anysocket.init();
|
|
}
|
|
unload() {
|
|
clearTimeout(this.reloadTimeout);
|
|
if (this.inited == false)
|
|
return;
|
|
this.inited = false;
|
|
this.unregisterEvent("create");
|
|
this.unregisterEvent("modify");
|
|
this.unregisterEvent("delete");
|
|
this.unregisterEvent("rename");
|
|
app.workspace.offref(this.eventRefs["active-leaf-change"]);
|
|
app.workspace.offref(this.eventRefs["layout-change"]);
|
|
this.anysocket.stop();
|
|
this.anysocket.removeAllListeners();
|
|
}
|
|
reload() {
|
|
this.debug && console.log("reloaded");
|
|
this.unload();
|
|
this.reloadTimeout = setTimeout(() => {
|
|
this.load();
|
|
}, 1e3);
|
|
}
|
|
async onFileData(data, peer) {
|
|
this.debug && console.log("FileData:", data);
|
|
if (!this.plugin.settings.autoSync && !this.isSyncing) {
|
|
return;
|
|
}
|
|
if (data.type == "send") {
|
|
let isBinary = Utils_default.isBinary(data.path);
|
|
this.anysocket.send({
|
|
type: "file_data",
|
|
data: {
|
|
type: "apply",
|
|
binary: isBinary,
|
|
data: isBinary ? import_AnySocket.default.Packer.pack(await this.storage.readBinary(data.path)) : await this.storage.read(data.path),
|
|
path: data.path,
|
|
metadata: await this.storage.readMetadata(data.path)
|
|
}
|
|
});
|
|
} else if (data.type == "apply") {
|
|
switch (data.metadata.action) {
|
|
case "created":
|
|
if (data.metadata.type == "folder") {
|
|
await this.storage.makeFolder(data.path, data.metadata);
|
|
} else {
|
|
if (data.binary) {
|
|
await this.storage.writeBinary(data.path, import_AnySocket.default.Packer.unpack(data.data), data.metadata);
|
|
} else {
|
|
await this.storage.write(data.path, data.data, data.metadata);
|
|
}
|
|
}
|
|
break;
|
|
case "deleted":
|
|
await this.storage.delete(data.path, data.metadata);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
async getMetadata(action, file, itemTime) {
|
|
let isBinary = Utils_default.isBinary(file.path);
|
|
let typeToAction = {
|
|
"sync": "created",
|
|
"restore": "created",
|
|
"create": "created",
|
|
"modify": "created",
|
|
"rename": "created",
|
|
"delete": "deleted"
|
|
};
|
|
let itemType;
|
|
let itemData;
|
|
if (action == "restore") {
|
|
itemData = file.data;
|
|
itemType = "file";
|
|
} else {
|
|
itemData = isBinary ? await this.storage.readBinary(file.path) : await this.storage.read(file.path);
|
|
itemType = file.stat ? "file" : "folder";
|
|
}
|
|
let metadata = {
|
|
action: typeToAction[action],
|
|
sha1: isBinary ? await Utils_default.getSHABinary(itemData) : await Utils_default.getSHA(itemData),
|
|
mtime: itemTime || await this.anysocket.getTime(),
|
|
type: itemType
|
|
};
|
|
if (action == "restore") {
|
|
return metadata;
|
|
}
|
|
let storedMetadata = await this.storage.readMetadata(file.path);
|
|
if (storedMetadata && metadata.action == storedMetadata.action && metadata.sha1 == storedMetadata.sha1) {
|
|
return {
|
|
changed: false,
|
|
metadata: storedMetadata
|
|
};
|
|
}
|
|
await this.storage.writeMetadata(file.path, metadata);
|
|
return {
|
|
changed: true,
|
|
metadata
|
|
};
|
|
}
|
|
async getFolderMtime(file) {
|
|
if (file.stat) {
|
|
return file.stat.mtime;
|
|
}
|
|
if (file.children.length <= 0) {
|
|
return false;
|
|
}
|
|
let hasValue = false;
|
|
let minMtime = await this.anysocket.getTime();
|
|
for (let child of file.children) {
|
|
let mtime = await this.getFolderMtime(child);
|
|
if (mtime == false) {
|
|
continue;
|
|
}
|
|
if (minMtime > mtime) {
|
|
hasValue = true;
|
|
minMtime = mtime;
|
|
}
|
|
}
|
|
return hasValue ? minMtime : false;
|
|
}
|
|
makeStatusBarItem(statusbar) {
|
|
this.statusBarItem = statusbar;
|
|
let container = this.statusBarItem.createEl("span");
|
|
container.style.verticalAlign = "middle";
|
|
container.style.display = "inline-flex";
|
|
container.style.alignItems = "center";
|
|
this.statusBarIcon = container.createEl("span");
|
|
this.statusBarIcon.style.paddingRight = "4px";
|
|
this.statusBarIcon.style.color = STATUS_ERROR;
|
|
this.statusBarIcon.innerHTML = this.plugin.getSVGIcon();
|
|
this.statusBarMessage = container.createEl("span");
|
|
}
|
|
setStatusMessage(message, keep = false) {
|
|
this.statusBarMessage.innerText = message;
|
|
clearTimeout(this.timeoutStatusMessage);
|
|
if (!keep) {
|
|
this.timeoutStatusMessage = setTimeout(() => {
|
|
this.statusBarMessage.innerText = "";
|
|
}, 2e3);
|
|
}
|
|
}
|
|
makeNotice(color, text) {
|
|
let notice = new import_obsidian3.Notice().noticeEl;
|
|
let container = notice.createEl("span");
|
|
container.style.verticalAlign = "middle";
|
|
container.style.display = "inline-flex";
|
|
container.style.alignItems = "center";
|
|
let icon = container.createEl("span");
|
|
icon.style.paddingRight = "4px";
|
|
icon.style.color = color;
|
|
icon.innerHTML = this.plugin.getSVGIcon();
|
|
container.createEl("span", { text });
|
|
}
|
|
notifyStatus(type) {
|
|
switch (type) {
|
|
case NotifyType.PLUGIN_DISABLED:
|
|
if (this.settings.notifications > 0) {
|
|
this.makeNotice(STATUS_ERROR, NotifyType.PLUGIN_DISABLED);
|
|
}
|
|
this.statusBarIcon.style.color = STATUS_ERROR;
|
|
this.setStatusMessage(NotifyType.PLUGIN_DISABLED, false);
|
|
break;
|
|
case NotifyType.NOT_CONNECTED:
|
|
if (this.plugin.settings.notifications > 0) {
|
|
this.makeNotice(STATUS_ERROR, NotifyType.NOT_CONNECTED);
|
|
}
|
|
this.statusBarIcon.style.color = STATUS_ERROR;
|
|
this.setStatusMessage(NotifyType.NOT_CONNECTED, false);
|
|
break;
|
|
case NotifyType.SYNCING:
|
|
if (this.plugin.settings.notifications > 1) {
|
|
this.makeNotice(STATUS_SYNC, NotifyType.SYNCING);
|
|
}
|
|
this.statusBarIcon.style.color = STATUS_SYNC;
|
|
this.setStatusMessage(NotifyType.SYNCING, true);
|
|
break;
|
|
case NotifyType.SYNC_COMPLETED:
|
|
if (this.plugin.settings.notifications > 1) {
|
|
this.makeNotice(STATUS_OK, NotifyType.SYNC_COMPLETED);
|
|
}
|
|
this.statusBarIcon.style.color = STATUS_OK;
|
|
this.setStatusMessage(NotifyType.SYNC_COMPLETED, false);
|
|
break;
|
|
case NotifyType.AUTO_SYNC_DISABLED:
|
|
if (this.plugin.settings.notifications > 1) {
|
|
this.makeNotice(STATUS_WARN, NotifyType.AUTO_SYNC_DISABLED);
|
|
}
|
|
this.statusBarIcon.style.color = STATUS_WARN;
|
|
this.setStatusMessage(NotifyType.AUTO_SYNC_DISABLED, false);
|
|
break;
|
|
case NotifyType.CONNECTION_LOST:
|
|
if (this.plugin.settings.notifications > 0) {
|
|
this.makeNotice(STATUS_ERROR, NotifyType.CONNECTION_LOST);
|
|
}
|
|
this.statusBarIcon.style.color = STATUS_ERROR;
|
|
this.setStatusMessage(NotifyType.CONNECTION_LOST, false);
|
|
break;
|
|
case NotifyType.CONNECTED:
|
|
if (this.plugin.settings.notifications > 0) {
|
|
this.makeNotice(STATUS_OK, NotifyType.CONNECTED);
|
|
}
|
|
this.statusBarIcon.style.color = STATUS_OK;
|
|
this.setStatusMessage(NotifyType.CONNECTED, false);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
// src/libs/modals/VersionHistoryModal.ts
|
|
var import_obsidian4 = require("obsidian");
|
|
var VersionHistoryModal = class extends import_obsidian4.Modal {
|
|
constructor(plugin, path) {
|
|
super(app);
|
|
this.plugin = plugin;
|
|
this.path = path;
|
|
this.name = "Unknown";
|
|
this.versions = [];
|
|
this.type = "created";
|
|
this.open();
|
|
this.setup();
|
|
}
|
|
setup() {
|
|
this.modalEl.addClass("anysocket-version-history");
|
|
this.elList = this.contentEl.createDiv("history-list");
|
|
this.elContainer = this.contentEl.createDiv("version-container");
|
|
let elContent = this.elContainer.createDiv("version-content");
|
|
let elTitle = elContent.createDiv("version-titlebar");
|
|
this.backButton = elTitle.createEl("button", { text: "Back", onclick: this.onBack.bind(this) });
|
|
if (!this.app.isMobile) {
|
|
this.backButton.hide();
|
|
}
|
|
let parts = this.path.split("/");
|
|
this.name = parts[parts.length - 1];
|
|
let fileName = elTitle.createDiv("version-filename").textContent = "";
|
|
this.titleEl.setText(this.name);
|
|
window._x = this.modalEl;
|
|
let actions = elTitle.createDiv("version-actions");
|
|
this.buttonRestore = actions.createEl("button", { text: "Restore", onclick: this.onRestore.bind(this) });
|
|
this.buttonRestore.disabled = true;
|
|
let _originalContentEl = this.contentEl;
|
|
this.contentEl = elContent;
|
|
this.markdownView = new import_obsidian4.MarkdownPreviewView(this);
|
|
this.contentEl = _originalContentEl;
|
|
if (this.app.isMobile) {
|
|
this.elContainer.hide();
|
|
}
|
|
this.plugin.xSync.listVersionHistory(this.path, (data) => {
|
|
this.versions = [];
|
|
if (data && data.data.length <= 0) {
|
|
return;
|
|
}
|
|
if (data.deleted) {
|
|
this.type = "deleted";
|
|
}
|
|
for (let timestamp of data.data) {
|
|
let item = this.elList.createDiv("version-timestamp");
|
|
let versionItem = {
|
|
timestamp,
|
|
el: item
|
|
};
|
|
item.textContent = this.formatTimestamp(timestamp);
|
|
item.onclick = () => {
|
|
this.internalItemSelect(versionItem);
|
|
};
|
|
this.versions.push(versionItem);
|
|
}
|
|
if (!this.app.isMobile) {
|
|
this.internalItemSelect(this.versions[0]);
|
|
}
|
|
});
|
|
}
|
|
formatTimestamp(timestamp) {
|
|
let date = new Date(timestamp);
|
|
let month = date.getMonth() + 1;
|
|
let day = date.getDate();
|
|
let year = date.getFullYear();
|
|
let hours = date.getHours();
|
|
let minutes = date.getMinutes();
|
|
let ampm = hours >= 12 ? "PM" : "AM";
|
|
hours = hours % 12;
|
|
hours = hours ? hours : 12;
|
|
minutes = minutes < 10 ? "0" + minutes : minutes;
|
|
return `${month}/${day}/${year} ${hours}:${minutes} ${ampm}`;
|
|
}
|
|
internalItemSelect(item) {
|
|
this.versions.map((v) => v.el.removeClass("active"));
|
|
item.el.addClass("active");
|
|
this.plugin.xSync.readVersionHistory(this.path, item.timestamp, (data) => {
|
|
if (typeof data !== "string") {
|
|
data = "";
|
|
}
|
|
this.markdownView.set(data, true);
|
|
this.markdownView.applyScroll(0);
|
|
});
|
|
this.selectedVersion = item;
|
|
this.buttonRestore.textContent = "Restore";
|
|
this.buttonRestore.disabled = false;
|
|
if (this.type == "created") {
|
|
if (this.selectedVersion.timestamp == this.versions[0].timestamp) {
|
|
this.buttonRestore.textContent = "Current";
|
|
this.buttonRestore.disabled = true;
|
|
} else {
|
|
this.buttonRestore.textContent = "Restore";
|
|
this.buttonRestore.disabled = false;
|
|
}
|
|
}
|
|
if (this.app.isMobile) {
|
|
this.elContainer.show();
|
|
this.backButton.show();
|
|
this.elList.hide();
|
|
}
|
|
}
|
|
async onBack() {
|
|
if (this.app.isMobile) {
|
|
this.elContainer.hide();
|
|
this.backButton.hide();
|
|
this.elList.show();
|
|
}
|
|
}
|
|
async onRestore() {
|
|
let data = this.markdownView.get();
|
|
let metadata = await this.plugin.xSync.getMetadata("restore", {
|
|
path: this.path,
|
|
data
|
|
});
|
|
metadata.sha1 = null;
|
|
await this.plugin.xSync.storage.write(this.path, data, metadata);
|
|
new import_obsidian4.Notice("Restored - " + this.name + " (" + this.formatTimestamp(this.selectedVersion.timestamp) + ")");
|
|
this.close();
|
|
}
|
|
};
|
|
|
|
// src/main.ts
|
|
var import_os = require("os");
|
|
|
|
// src/libs/modals/FilesHistoryModal.ts
|
|
var import_obsidian5 = require("obsidian");
|
|
var import_AnySocket2 = __toESM(require_AnySocket());
|
|
var FilesHistoryModal = class extends import_obsidian5.SuggestModal {
|
|
constructor(plugin, deletedOnly = false) {
|
|
super(app);
|
|
this.plugin = plugin;
|
|
this.data = [];
|
|
this.deletedOnly = deletedOnly;
|
|
if (this.deletedOnly) {
|
|
this.setPlaceholder("Search for deleted files...");
|
|
} else {
|
|
this.setPlaceholder("Search for files...");
|
|
}
|
|
this.plugin.xSync.listFilesHistory(this.deletedOnly, (data) => {
|
|
this.data = data;
|
|
this.open();
|
|
});
|
|
this.containerEl.addClass("anysocket-files-history");
|
|
}
|
|
getSuggestions(query) {
|
|
return this.data.filter((item) => item.path.toLowerCase().includes(query.toLowerCase()));
|
|
}
|
|
async onChooseSuggestion(item, evt) {
|
|
if (Utils_default.isBinary(item.path)) {
|
|
let parts = item.path.split("/");
|
|
await this.plugin.xSync.listVersionHistory(item.path, async (data) => {
|
|
await this.plugin.xSync.readVersionHistory(item.path, data.data[0], async (data2) => {
|
|
data2 = import_AnySocket2.default.Packer.unpack(data2);
|
|
let metadata = await this.plugin.xSync.getMetadata("restore", {
|
|
path: item.path,
|
|
data: data2
|
|
});
|
|
metadata.sha1 = null;
|
|
await this.plugin.xSync.storage.writeBinary(item.path, data2, metadata);
|
|
new import_obsidian5.Notice("Restored - " + parts[parts.length - 1]);
|
|
});
|
|
});
|
|
} else {
|
|
new VersionHistoryModal(this.plugin, item.path);
|
|
}
|
|
}
|
|
renderSuggestion(value, el) {
|
|
el.createEl("div", { text: value.path }).addClass("item-path");
|
|
let prefix = "Modified: ";
|
|
if (this.deletedOnly) {
|
|
prefix = "Deleted: ";
|
|
}
|
|
el.createEl("div", { text: prefix + this.formatTimestamp(value.mtime) }).addClass("item-metadata");
|
|
}
|
|
formatTimestamp(timestamp) {
|
|
let date = new Date(timestamp);
|
|
let month = date.getMonth() + 1;
|
|
let day = date.getDate();
|
|
let year = date.getFullYear();
|
|
let hours = date.getHours();
|
|
let minutes = date.getMinutes();
|
|
let ampm = hours >= 12 ? "PM" : "AM";
|
|
hours = hours % 12;
|
|
hours = hours ? hours : 12;
|
|
minutes = minutes < 10 ? "0" + minutes : minutes;
|
|
return `${month}/${day}/${year} ${hours}:${minutes} ${ampm}`;
|
|
}
|
|
};
|
|
|
|
// src/main.ts
|
|
var import_ua_parser_js = __toESM(require_ua_parser());
|
|
var deviceInfo = new import_ua_parser_js.UAParser(navigator.userAgent).getDevice();
|
|
function getDefaultDeviceName() {
|
|
return import_obsidian6.Platform.isDesktop ? (0, import_os.hostname)() : deviceInfo.model || "Unknown";
|
|
}
|
|
var DEFAULT_SETTINGS = {
|
|
host: "127.0.0.1",
|
|
port: "3000",
|
|
password: "",
|
|
syncEnabled: false,
|
|
delayedSync: 3,
|
|
autoSync: true,
|
|
notifications: 1,
|
|
deviceName: getDefaultDeviceName(),
|
|
debug: false
|
|
};
|
|
var AnySocketSyncPlugin = class extends import_obsidian6.Plugin {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.VERSION = "1.3.3";
|
|
this.BUILD = "1730812576031";
|
|
this.isReady = false;
|
|
}
|
|
async onload() {
|
|
await this.loadSettings();
|
|
this.registerEvent(this.app.workspace.on("file-menu", (menu, file) => {
|
|
if (!file.stat) {
|
|
return;
|
|
}
|
|
if (!Utils_default.isBinary(file.path)) {
|
|
menu.addItem((item) => {
|
|
item.setTitle("Version history").setIcon("history").onClick(async () => {
|
|
new VersionHistoryModal(this, file.path);
|
|
});
|
|
});
|
|
}
|
|
menu.addItem((item) => {
|
|
item.setTitle("Deleted files history").setIcon("history").onClick(async () => {
|
|
new FilesHistoryModal(this, true);
|
|
});
|
|
});
|
|
}));
|
|
this.addCommand({
|
|
id: "files-version-history",
|
|
name: "Version history",
|
|
callback: async () => {
|
|
new FilesHistoryModal(this, false);
|
|
}
|
|
});
|
|
this.addCommand({
|
|
id: "sync-now",
|
|
name: "Sync Now",
|
|
callback: async () => {
|
|
await this.xSync.sync();
|
|
}
|
|
});
|
|
this.addCommand({
|
|
id: "deleted-version-history",
|
|
name: "Deleted files history",
|
|
callback: async () => {
|
|
new FilesHistoryModal(this, true);
|
|
}
|
|
});
|
|
this.addSettingTab(new AnySocketSyncSettingTab(this));
|
|
this.xSync = new XSync2(this);
|
|
this.xSync.makeStatusBarItem(this.addStatusBarItem());
|
|
if (!app.workspace.layoutReady) {
|
|
this.registerEvent(this.app.workspace.on("layout-ready", async () => {
|
|
await this.ready();
|
|
}));
|
|
} else {
|
|
await this.ready();
|
|
}
|
|
}
|
|
async ready() {
|
|
this.isReady = true;
|
|
await this.xSync.enabled(true);
|
|
}
|
|
async onunload() {
|
|
await this.xSync.enabled(false);
|
|
}
|
|
async loadSettings() {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
|
}
|
|
async saveSettings() {
|
|
await this.saveData(this.settings);
|
|
this.xSync.reload();
|
|
}
|
|
getSVGIcon() {
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide-send"><path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"></path><path d="m21.854 2.147-10.94 10.939"></path></svg>';
|
|
}
|
|
};
|
|
var AnySocketSyncSettingTab = class extends import_obsidian6.PluginSettingTab {
|
|
constructor(plugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
display() {
|
|
const { containerEl } = this;
|
|
containerEl.empty();
|
|
new import_obsidian6.Setting(containerEl).setName("Connection settings").setHeading();
|
|
new import_obsidian6.Setting(containerEl).setName("Device name").addText((text) => text.setPlaceholder(getDefaultDeviceName()).setValue(this.plugin.settings.deviceName).onChange(async (value) => {
|
|
if (value == "") {
|
|
value = getDefaultDeviceName();
|
|
}
|
|
this.plugin.settings.deviceName = value;
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new import_obsidian6.Setting(containerEl).setName("Host").addText((text) => text.setPlaceholder("127.0.0.1").setValue(this.plugin.settings.host).onChange(async (value) => {
|
|
this.plugin.settings.host = value;
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new import_obsidian6.Setting(containerEl).setName("Port").addText((text) => text.setPlaceholder("3000").setValue(this.plugin.settings.port).onChange(async (value) => {
|
|
this.plugin.settings.port = value;
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new import_obsidian6.Setting(containerEl).setName("Password").addText((text) => {
|
|
text.setPlaceholder("pass").setValue(this.plugin.settings.password).onChange(async (value) => {
|
|
this.plugin.settings.password = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
text.inputEl.type = "password";
|
|
});
|
|
new import_obsidian6.Setting(containerEl).setName("Enable Connection").addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.syncEnabled).onChange(async (value) => {
|
|
this.plugin.settings.syncEnabled = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
new import_obsidian6.Setting(containerEl).setName("Sync settings").setHeading();
|
|
new import_obsidian6.Setting(containerEl).setName("Delayed Sync").setDesc("Delay sync until no changes for the specified duration (or focus changed)").addDropdown((dropdown) => {
|
|
dropdown.addOption("0", "Instant");
|
|
dropdown.addOption("3", "3s");
|
|
dropdown.addOption("4", "4s");
|
|
dropdown.addOption("5", "5s");
|
|
dropdown.addOption("10", "10s");
|
|
dropdown.addOption("15", "15s");
|
|
dropdown.addOption("20", "20s");
|
|
dropdown.addOption("25", "25s");
|
|
dropdown.addOption("30", "30s");
|
|
dropdown.addOption("60", "1m");
|
|
dropdown.addOption("300", "5m");
|
|
dropdown.addOption("600", "10m");
|
|
dropdown.addOption("900", "15m");
|
|
dropdown.setValue(this.plugin.settings.delayedSync.toString()).onChange(async (value) => {
|
|
this.plugin.settings.delayedSync = parseInt(value);
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
new import_obsidian6.Setting(containerEl).setName("Auto Sync").setDesc("Automatically sync when local/remote changes are detected").addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.autoSync).onChange(async (value) => {
|
|
this.plugin.settings.autoSync = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
new import_obsidian6.Setting(containerEl).setName("Notifications").addDropdown((dropdown) => {
|
|
dropdown.addOption("0", "Off");
|
|
dropdown.addOption("1", "Connection status");
|
|
dropdown.addOption("2", "Connection & sync status");
|
|
dropdown.setValue(this.plugin.settings.notifications.toString()).onChange(async (value) => {
|
|
this.plugin.settings.notifications = parseInt(value);
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
new import_obsidian6.Setting(containerEl).setName("Debug").addToggle((toggle) => toggle.setValue(this.plugin.settings.debug).onChange(async (value) => {
|
|
this.plugin.settings.debug = value;
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
}
|
|
};
|
|
|
|
|
|
/* nosourcemap */ |