From 483177d6d719852febd3bc7d14d7989e1aa9b2b5 Mon Sep 17 00:00:00 2001 From: pubkey <8926560+pubkey@users.noreply.github.com> Date: Mon, 2 Aug 2021 03:45:29 +0200 Subject: [PATCH] 4.1.0 --- CHANGELOG.md | 6 + dist/es5node/method-chooser.js | 14 +- dist/es5node/methods/node.js | 192 +-- dist/{es => esbrowser}/broadcast-channel.js | 0 dist/{es => esbrowser}/browserify.index.js | 0 dist/{es => esbrowser}/index.es5.js | 0 dist/{es => esbrowser}/index.js | 0 dist/{es => esbrowser}/leader-election.js | 0 dist/{es => esbrowser}/method-chooser.js | 12 +- dist/{es => esbrowser}/methods/cookies.js | 0 dist/{es => esbrowser}/methods/indexed-db.js | 0 .../{es => esbrowser}/methods/localstorage.js | 0 dist/{es => esbrowser}/methods/native.js | 0 dist/{es => esbrowser}/methods/node.js | 161 +-- dist/{es => esbrowser}/methods/simulate.js | 0 dist/{es => esbrowser}/options.js | 0 dist/{es => esbrowser}/util.js | 0 dist/esnode/broadcast-channel.js | 262 ++++ dist/esnode/browserify.index.js | 6 + dist/esnode/index.es5.js | 16 + dist/esnode/index.js | 2 + dist/esnode/leader-election.js | 262 ++++ dist/esnode/method-chooser.js | 45 + dist/esnode/methods/cookies.js | 4 + dist/esnode/methods/indexed-db.js | 309 +++++ dist/esnode/methods/localstorage.js | 163 +++ dist/esnode/methods/native.js | 62 + dist/esnode/methods/node.js | 1050 +++++++++++++++++ dist/esnode/methods/simulate.js | 51 + dist/esnode/options.js | 24 + dist/esnode/util.js | 55 + dist/lib/browser.js | 14 +- dist/lib/browser.min.js | 2 +- dist/lib/method-chooser.js | 10 +- dist/lib/methods/node.js | 192 +-- docs/e2e.js | 14 +- docs/iframe.js | 14 +- docs/index.js | 14 +- docs/leader-iframe.js | 14 +- docs/worker.js | 14 +- package.json | 2 +- 41 files changed, 2592 insertions(+), 394 deletions(-) rename dist/{es => esbrowser}/broadcast-channel.js (100%) rename dist/{es => esbrowser}/browserify.index.js (100%) rename dist/{es => esbrowser}/index.es5.js (100%) rename dist/{es => esbrowser}/index.js (100%) rename dist/{es => esbrowser}/leader-election.js (100%) rename dist/{es => esbrowser}/method-chooser.js (69%) rename dist/{es => esbrowser}/methods/cookies.js (100%) rename dist/{es => esbrowser}/methods/indexed-db.js (100%) rename dist/{es => esbrowser}/methods/localstorage.js (100%) rename dist/{es => esbrowser}/methods/native.js (100%) rename dist/{es => esbrowser}/methods/node.js (91%) rename dist/{es => esbrowser}/methods/simulate.js (100%) rename dist/{es => esbrowser}/options.js (100%) rename dist/{es => esbrowser}/util.js (100%) create mode 100644 dist/esnode/broadcast-channel.js create mode 100644 dist/esnode/browserify.index.js create mode 100644 dist/esnode/index.es5.js create mode 100644 dist/esnode/index.js create mode 100644 dist/esnode/leader-election.js create mode 100644 dist/esnode/method-chooser.js create mode 100644 dist/esnode/methods/cookies.js create mode 100644 dist/esnode/methods/indexed-db.js create mode 100644 dist/esnode/methods/localstorage.js create mode 100644 dist/esnode/methods/native.js create mode 100644 dist/esnode/methods/node.js create mode 100644 dist/esnode/methods/simulate.js create mode 100644 dist/esnode/options.js create mode 100644 dist/esnode/util.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8be518b2..7cfbf4b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## X.X.X (comming soon) +## 4.1.0 (2 August 2021) + +Bugfixes: + - Fixed various problems with the module loading. Thanks [benmccann](https://github.com/benmccann) and [chbdetta](https://github.com/chbdetta) + + ## 4.0.0 (15 July 2021) Other: diff --git a/dist/es5node/method-chooser.js b/dist/es5node/method-chooser.js index a6753973..c039e49f 100644 --- a/dist/es5node/method-chooser.js +++ b/dist/es5node/method-chooser.js @@ -17,7 +17,7 @@ var _localstorage = _interopRequireDefault(require("./methods/localstorage.js")) var _simulate = _interopRequireDefault(require("./methods/simulate.js")); -var NodeMethod = _interopRequireWildcard(require("../../src/methods/node.js")); +var NodeMethod = _interopRequireWildcard(require("./methods/node.js")); var _util = require("./util"); @@ -26,22 +26,14 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // the line below will be removed from es5/browser builds -// the non-transpiled code runs faster // order is important var METHODS = [_native["default"], // fastest _indexedDb["default"], _localstorage["default"]]; function chooseMethod(options) { - var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // process.browser check allows ES6 builds to be used on server or client. Bundlers like - // Browserify, Webpack, etc. define process.browser and can then dead code eliminate the unused - // import. However, we still use sed during build of es5/browser build to remove the import so - // that it's also removed from non-minified version - - if (!process.browser) { - // the line below will be removed from es5/browser builds - chooseMethods.push(NodeMethod); - } // directly chosen + var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // the line below will be removed from es5/browser builds + chooseMethods.push(NodeMethod); // directly chosen if (options.type) { if (options.type === 'simulate') { diff --git a/dist/es5node/methods/node.js b/dist/es5node/methods/node.js index 094a480e..2439f173 100644 --- a/dist/es5node/methods/node.js +++ b/dist/es5node/methods/node.js @@ -2,51 +2,80 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.cleanPipeName = cleanPipeName; +exports.getPaths = getPaths; +exports.ensureFoldersExist = ensureFoldersExist; +exports.clearNodeFolder = clearNodeFolder; +exports.socketPath = socketPath; +exports.socketInfoPath = socketInfoPath; +exports.createSocketInfoFile = createSocketInfoFile; +exports.countChannelFolders = countChannelFolders; +exports.createSocketEventEmitter = createSocketEventEmitter; +exports.openClientConnection = openClientConnection; +exports.writeMessage = writeMessage; +exports.getReadersUuids = getReadersUuids; +exports.messagePath = messagePath; +exports.getAllMessages = getAllMessages; +exports.getSingleMessage = getSingleMessage; +exports.readMessage = readMessage; +exports.cleanOldMessages = cleanOldMessages; +exports.create = create; +exports._filterMessage = _filterMessage; +exports.handleMessagePing = handleMessagePing; +exports.refreshReaderClients = refreshReaderClients; +exports.postMessage = postMessage; +exports.emitOverFastPath = emitOverFastPath; +exports.onMessage = onMessage; +exports.close = close; +exports.canBeUsed = canBeUsed; +exports.averageResponseTime = averageResponseTime; +exports.microSeconds = microSeconds; +exports.type = exports.TMP_FOLDER_BASE = void 0; + var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); -/** - * this method is used in nodejs-environments. - * The ipc is handled via sockets and file-writes to the tmp-folder - */ -var util = require('util'); +var _util = _interopRequireDefault(require("util")); -var fs = require('fs'); +var _fs = _interopRequireDefault(require("fs")); -var os = require('os'); +var _os = _interopRequireDefault(require("os")); -var events = require('events'); +var _events = _interopRequireDefault(require("events")); -var net = require('net'); +var _net = _interopRequireDefault(require("net")); -var path = require('path'); +var _path = _interopRequireDefault(require("path")); -var micro = require('nano-time'); +var _nanoTime = _interopRequireDefault(require("nano-time")); -var rimraf = require('rimraf'); +var _rimraf = _interopRequireDefault(require("rimraf")); -var sha3_224 = require('js-sha3').sha3_224; +var _jsSha = require("js-sha3"); -var isNode = require('detect-node'); +var _detectNode = _interopRequireDefault(require("detect-node")); -var unload = require('unload'); +var _unload = _interopRequireDefault(require("unload")); -var fillOptionsWithDefaults = require('../../dist/lib/options.js').fillOptionsWithDefaults; +var _options = require("../options.js"); -var ownUtil = require('../../dist/lib/util.js'); +var _util2 = require("../util.js"); -var randomInt = ownUtil.randomInt; -var randomToken = ownUtil.randomToken; +var _obliviousSet = require("oblivious-set"); + +/** + * this method is used in nodejs-environments. + * The ipc is handled via sockets and file-writes to the tmp-folder + */ -var _require = require('oblivious-set'), - ObliviousSet = _require.ObliviousSet; /** * windows sucks, so we have handle windows-type of socket-paths * @link https://gist.github.com/domenic/2790533#gistcomment-331356 */ - - function cleanPipeName(str) { if (process.platform === 'win32' && !str.startsWith('\\\\.\\pipe\\')) { str = str.replace(/^\//, ''); @@ -57,21 +86,31 @@ function cleanPipeName(str) { } } -var mkdir = util.promisify(fs.mkdir); -var writeFile = util.promisify(fs.writeFile); -var readFile = util.promisify(fs.readFile); -var unlink = util.promisify(fs.unlink); -var readdir = util.promisify(fs.readdir); -var chmod = util.promisify(fs.chmod); -var removeDir = util.promisify(rimraf); +var mkdir = _util["default"].promisify(_fs["default"].mkdir); + +var writeFile = _util["default"].promisify(_fs["default"].writeFile); + +var readFile = _util["default"].promisify(_fs["default"].readFile); + +var unlink = _util["default"].promisify(_fs["default"].unlink); + +var readdir = _util["default"].promisify(_fs["default"].readdir); + +var chmod = _util["default"].promisify(_fs["default"].chmod); + +var removeDir = _util["default"].promisify(_rimraf["default"]); + var OTHER_INSTANCES = {}; var TMP_FOLDER_NAME = 'pubkey.bc'; -var TMP_FOLDER_BASE = path.join(os.tmpdir(), TMP_FOLDER_NAME); + +var TMP_FOLDER_BASE = _path["default"].join(_os["default"].tmpdir(), TMP_FOLDER_NAME); + +exports.TMP_FOLDER_BASE = TMP_FOLDER_BASE; var getPathsCache = new Map(); function getPaths(channelName) { if (!getPathsCache.has(channelName)) { - var channelHash = sha3_224(channelName); // use hash incase of strange characters + var channelHash = (0, _jsSha.sha3_224)(channelName); // use hash incase of strange characters /** * because the lenght of socket-paths is limited, we use only the first 20 chars @@ -80,9 +119,13 @@ function getPaths(channelName) { */ var channelFolder = 'A' + channelHash.substring(0, 20); - var channelPathBase = path.join(TMP_FOLDER_BASE, channelFolder); - var folderPathReaders = path.join(channelPathBase, 'rdrs'); - var folderPathMessages = path.join(channelPathBase, 'messages'); + + var channelPathBase = _path["default"].join(TMP_FOLDER_BASE, channelFolder); + + var folderPathReaders = _path["default"].join(channelPathBase, 'rdrs'); + + var folderPathMessages = _path["default"].join(channelPathBase, 'messages'); + var ret = { channelBase: channelPathBase, readers: folderPathReaders, @@ -215,13 +258,17 @@ function _clearNodeFolder() { function socketPath(channelName, readerUuid, paths) { paths = paths || getPaths(channelName); - var socketPath = path.join(paths.readers, readerUuid + '.s'); + + var socketPath = _path["default"].join(paths.readers, readerUuid + '.s'); + return cleanPipeName(socketPath); } function socketInfoPath(channelName, readerUuid, paths) { paths = paths || getPaths(channelName); - var socketPath = path.join(paths.readers, readerUuid + '.json'); + + var socketPath = _path["default"].join(paths.readers, readerUuid + '.json'); + return socketPath; } /** @@ -339,8 +386,8 @@ function _createSocketEventEmitter() { switch (_context11.prev = _context11.next) { case 0: pathToSocket = socketPath(channelName, readerUuid, paths); - emitter = new events.EventEmitter(); - server = net.createServer(function (stream) { + emitter = new _events["default"].EventEmitter(); + server = _net["default"].createServer(function (stream) { stream.on('end', function () {}); stream.on('data', function (msg) { emitter.emit('data', msg.toString()); @@ -447,7 +494,7 @@ function _openClientConnection() { switch (_context12.prev = _context12.next) { case 0: pathToSocket = socketPath(channelName, readerUuid); - client = new net.Socket(); + client = new _net["default"].Socket(); return _context12.abrupt("return", new Promise(function (res, rej) { client.connect(pathToSocket, function () { return res(client); @@ -475,9 +522,11 @@ function writeMessage(channelName, readerUuid, messageJson, paths) { time: time, data: messageJson }; - var token = randomToken(); + var token = (0, _util2.randomToken)(); var fileName = time + '_' + readerUuid + '_' + token + '.json'; - var msgPath = path.join(paths.messages, fileName); + + var msgPath = _path["default"].join(paths.messages, fileName); + return writeFile(msgPath, JSON.stringify(writeObject)).then(function () { return { time: time, @@ -542,7 +591,7 @@ function _messagePath() { switch (_context14.prev = _context14.next) { case 0: fileName = time + '_' + writerUuid + '_' + token + '.json'; - msgPath = path.join(getPaths(channelName).messages, fileName); + msgPath = _path["default"].join(getPaths(channelName).messages, fileName); return _context14.abrupt("return", msgPath); case 3: @@ -577,7 +626,7 @@ function _getAllMessages() { var fileName = file.split('.')[0]; var split = fileName.split('_'); return { - path: path.join(messagesPath, file), + path: _path["default"].join(messagesPath, file), time: parseInt(split[0]), senderUuid: split[1], token: split[2] @@ -597,7 +646,7 @@ function _getAllMessages() { function getSingleMessage(channelName, msgObj, paths) { paths = paths || getPaths(channelName); return { - path: path.join(paths.messages, msgObj.t + '_' + msgObj.u + '_' + msgObj.to + '.json'), + path: _path["default"].join(paths.messages, msgObj.t + '_' + msgObj.u + '_' + msgObj.to + '.json'), time: msgObj.t, senderUuid: msgObj.u, token: msgObj.to @@ -647,6 +696,8 @@ var type = 'node'; * @return {Promise} */ +exports.type = type; + function create(_x19) { return _create.apply(this, arguments); } @@ -669,11 +720,11 @@ function _create() { switch (_context17.prev = _context17.next) { case 0: options = _args17.length > 1 && _args17[1] !== undefined ? _args17[1] : {}; - options = fillOptionsWithDefaults(options); + options = (0, _options.fillOptionsWithDefaults)(options); time = microSeconds(); paths = getPaths(channelName); ensureFolderExistsPromise = ensureFoldersExist(channelName, paths); - uuid = randomToken(); + uuid = (0, _util2.randomToken)(); state = { time: time, channelName: channelName, @@ -681,14 +732,14 @@ function _create() { uuid: uuid, paths: paths, // contains all messages that have been emitted before - emittedMessagesIds: new ObliviousSet(options.node.ttl * 2), + emittedMessagesIds: new _obliviousSet.ObliviousSet(options.node.ttl * 2), messagesCallbackTime: null, messagesCallback: null, // ensures we do not read messages in parrallel writeBlockPromise: Promise.resolve(), otherReaderClients: {}, // ensure if process crashes, everything is cleaned up - removeUnload: unload.add(function () { + removeUnload: _unload["default"].add(function () { return close(state); }), closed: false @@ -981,7 +1032,7 @@ function postMessage(channelState, messageJson) { * only if random-int matches, we clean up old messages */ - if (randomInt(0, 20) === 0) { + if ((0, _util2.randomInt)(0, 20) === 0) { /* await */ getAllMessages(channelState.channelName, channelState.paths).then(function (allMessages) { return cleanOldMessages(allMessages, channelState.options.node.ttl); @@ -1057,7 +1108,7 @@ function close(channelState) { if (channelState.infoFilePath) { try { - fs.unlinkSync(channelState.infoFilePath); + _fs["default"].unlinkSync(channelState.infoFilePath); } catch (err) {} } /** @@ -1074,7 +1125,7 @@ function close(channelState) { } function canBeUsed() { - return isNode; + return _detectNode["default"]; } /** * on node we use a relatively height averageResponseTime, @@ -1089,38 +1140,5 @@ function averageResponseTime() { } function microSeconds() { - return parseInt(micro.microseconds()); -} - -module.exports = { - TMP_FOLDER_BASE: TMP_FOLDER_BASE, - cleanPipeName: cleanPipeName, - getPaths: getPaths, - ensureFoldersExist: ensureFoldersExist, - clearNodeFolder: clearNodeFolder, - socketPath: socketPath, - socketInfoPath: socketInfoPath, - createSocketInfoFile: createSocketInfoFile, - countChannelFolders: countChannelFolders, - createSocketEventEmitter: createSocketEventEmitter, - openClientConnection: openClientConnection, - writeMessage: writeMessage, - getReadersUuids: getReadersUuids, - messagePath: messagePath, - getAllMessages: getAllMessages, - getSingleMessage: getSingleMessage, - readMessage: readMessage, - cleanOldMessages: cleanOldMessages, - type: type, - create: create, - _filterMessage: _filterMessage, - handleMessagePing: handleMessagePing, - refreshReaderClients: refreshReaderClients, - postMessage: postMessage, - emitOverFastPath: emitOverFastPath, - onMessage: onMessage, - close: close, - canBeUsed: canBeUsed, - averageResponseTime: averageResponseTime, - microSeconds: microSeconds -}; \ No newline at end of file + return parseInt(_nanoTime["default"].microseconds()); +} \ No newline at end of file diff --git a/dist/es/broadcast-channel.js b/dist/esbrowser/broadcast-channel.js similarity index 100% rename from dist/es/broadcast-channel.js rename to dist/esbrowser/broadcast-channel.js diff --git a/dist/es/browserify.index.js b/dist/esbrowser/browserify.index.js similarity index 100% rename from dist/es/browserify.index.js rename to dist/esbrowser/browserify.index.js diff --git a/dist/es/index.es5.js b/dist/esbrowser/index.es5.js similarity index 100% rename from dist/es/index.es5.js rename to dist/esbrowser/index.es5.js diff --git a/dist/es/index.js b/dist/esbrowser/index.js similarity index 100% rename from dist/es/index.js rename to dist/esbrowser/index.js diff --git a/dist/es/leader-election.js b/dist/esbrowser/leader-election.js similarity index 100% rename from dist/es/leader-election.js rename to dist/esbrowser/leader-election.js diff --git a/dist/es/method-chooser.js b/dist/esbrowser/method-chooser.js similarity index 69% rename from dist/es/method-chooser.js rename to dist/esbrowser/method-chooser.js index f45f8cb6..42f78f5c 100644 --- a/dist/es/method-chooser.js +++ b/dist/esbrowser/method-chooser.js @@ -3,22 +3,14 @@ import IndexeDbMethod from './methods/indexed-db.js'; import LocalstorageMethod from './methods/localstorage.js'; import SimulateMethod from './methods/simulate.js'; // the line below will be removed from es5/browser builds -import * as NodeMethod from '../../src/methods/node.js'; // the non-transpiled code runs faster import { isNode } from './util'; // order is important var METHODS = [NativeMethod, // fastest IndexeDbMethod, LocalstorageMethod]; export function chooseMethod(options) { - var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // process.browser check allows ES6 builds to be used on server or client. Bundlers like - // Browserify, Webpack, etc. define process.browser and can then dead code eliminate the unused - // import. However, we still use sed during build of es5/browser build to remove the import so - // that it's also removed from non-minified version - - if (!process.browser) { - // the line below will be removed from es5/browser builds - chooseMethods.push(NodeMethod); - } // directly chosen + var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // the line below will be removed from es5/browser builds + if (options.type) { diff --git a/dist/es/methods/cookies.js b/dist/esbrowser/methods/cookies.js similarity index 100% rename from dist/es/methods/cookies.js rename to dist/esbrowser/methods/cookies.js diff --git a/dist/es/methods/indexed-db.js b/dist/esbrowser/methods/indexed-db.js similarity index 100% rename from dist/es/methods/indexed-db.js rename to dist/esbrowser/methods/indexed-db.js diff --git a/dist/es/methods/localstorage.js b/dist/esbrowser/methods/localstorage.js similarity index 100% rename from dist/es/methods/localstorage.js rename to dist/esbrowser/methods/localstorage.js diff --git a/dist/es/methods/native.js b/dist/esbrowser/methods/native.js similarity index 100% rename from dist/es/methods/native.js rename to dist/esbrowser/methods/native.js diff --git a/dist/es/methods/node.js b/dist/esbrowser/methods/node.js similarity index 91% rename from dist/es/methods/node.js rename to dist/esbrowser/methods/node.js index 724b07fc..4993741d 100644 --- a/dist/es/methods/node.js +++ b/dist/esbrowser/methods/node.js @@ -5,44 +5,26 @@ import _regeneratorRuntime from "@babel/runtime/regenerator"; * this method is used in nodejs-environments. * The ipc is handled via sockets and file-writes to the tmp-folder */ -var util = require('util'); - -var fs = require('fs'); - -var os = require('os'); - -var events = require('events'); - -var net = require('net'); - -var path = require('path'); - -var micro = require('nano-time'); - -var rimraf = require('rimraf'); - -var sha3_224 = require('js-sha3').sha3_224; - -var isNode = require('detect-node'); - -var unload = require('unload'); - -var fillOptionsWithDefaults = require('../../dist/lib/options.js').fillOptionsWithDefaults; - -var ownUtil = require('../../dist/lib/util.js'); - -var randomInt = ownUtil.randomInt; -var randomToken = ownUtil.randomToken; - -var _require = require('oblivious-set'), - ObliviousSet = _require.ObliviousSet; +import util from 'util'; +import fs from 'fs'; +import os from 'os'; +import events from 'events'; +import net from 'net'; +import path from 'path'; +import micro from 'nano-time'; +import rimraf from 'rimraf'; +import { sha3_224 } from 'js-sha3'; +import isNode from 'detect-node'; +import unload from 'unload'; +import { fillOptionsWithDefaults } from '../options.js'; +import { randomInt, randomToken } from '../util.js'; +import { ObliviousSet } from 'oblivious-set'; /** * windows sucks, so we have handle windows-type of socket-paths * @link https://gist.github.com/domenic/2790533#gistcomment-331356 */ - -function cleanPipeName(str) { +export function cleanPipeName(str) { if (process.platform === 'win32' && !str.startsWith('\\\\.\\pipe\\')) { str = str.replace(/^\//, ''); str = str.replace(/\//g, '-'); @@ -51,7 +33,6 @@ function cleanPipeName(str) { return str; } } - var mkdir = util.promisify(fs.mkdir); var writeFile = util.promisify(fs.writeFile); var readFile = util.promisify(fs.readFile); @@ -61,10 +42,9 @@ var chmod = util.promisify(fs.chmod); var removeDir = util.promisify(rimraf); var OTHER_INSTANCES = {}; var TMP_FOLDER_NAME = 'pubkey.bc'; -var TMP_FOLDER_BASE = path.join(os.tmpdir(), TMP_FOLDER_NAME); +export var TMP_FOLDER_BASE = path.join(os.tmpdir(), TMP_FOLDER_NAME); var getPathsCache = new Map(); - -function getPaths(channelName) { +export function getPaths(channelName) { if (!getPathsCache.has(channelName)) { var channelHash = sha3_224(channelName); // use hash incase of strange characters @@ -89,7 +69,6 @@ function getPaths(channelName) { return getPathsCache.get(channelName); } - var ENSURE_BASE_FOLDER_EXISTS_PROMISE = null; function ensureBaseFolderExists() { @@ -120,7 +99,7 @@ function _ensureBaseFolderExists() { return _ensureBaseFolderExists.apply(this, arguments); } -function ensureFoldersExist(_x, _x2) { +export function ensureFoldersExist(_x, _x2) { return _ensureFoldersExist.apply(this, arguments); } /** @@ -128,7 +107,6 @@ function ensureFoldersExist(_x, _x2) { * @return {Promise} */ - function _ensureFoldersExist() { _ensureFoldersExist = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(channelName, paths) { var chmodValue; @@ -172,7 +150,7 @@ function _ensureFoldersExist() { return _ensureFoldersExist.apply(this, arguments); } -function clearNodeFolder() { +export function clearNodeFolder() { return _clearNodeFolder.apply(this, arguments); } @@ -208,13 +186,12 @@ function _clearNodeFolder() { return _clearNodeFolder.apply(this, arguments); } -function socketPath(channelName, readerUuid, paths) { +export function socketPath(channelName, readerUuid, paths) { paths = paths || getPaths(channelName); var socketPath = path.join(paths.readers, readerUuid + '.s'); return cleanPipeName(socketPath); } - -function socketInfoPath(channelName, readerUuid, paths) { +export function socketInfoPath(channelName, readerUuid, paths) { paths = paths || getPaths(channelName); var socketPath = path.join(paths.readers, readerUuid + '.json'); return socketPath; @@ -225,8 +202,7 @@ function socketInfoPath(channelName, readerUuid, paths) { * we have to set a normal file so other readers know our socket exists */ - -function createSocketInfoFile(channelName, readerUuid, paths) { +export function createSocketInfoFile(channelName, readerUuid, paths) { var pathToFile = socketInfoPath(channelName, readerUuid, paths); return writeFile(pathToFile, JSON.stringify({ time: microSeconds() @@ -239,8 +215,7 @@ function createSocketInfoFile(channelName, readerUuid, paths) { * @return {Promise} */ - -function countChannelFolders() { +export function countChannelFolders() { return _countChannelFolders.apply(this, arguments); } @@ -322,7 +297,7 @@ function _connectionError() { return _connectionError.apply(this, arguments); } -function createSocketEventEmitter(_x4, _x5, _x6) { +export function createSocketEventEmitter(_x4, _x5, _x6) { return _createSocketEventEmitter.apply(this, arguments); } @@ -424,7 +399,7 @@ function _createSocketEventEmitter() { return _createSocketEventEmitter.apply(this, arguments); } -function openClientConnection(_x7, _x8) { +export function openClientConnection(_x7, _x8) { return _openClientConnection.apply(this, arguments); } /** @@ -433,7 +408,6 @@ function openClientConnection(_x7, _x8) { * @return {Promise} */ - function _openClientConnection() { _openClientConnection = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12(channelName, readerUuid) { var pathToSocket, client; @@ -462,7 +436,7 @@ function _openClientConnection() { return _openClientConnection.apply(this, arguments); } -function writeMessage(channelName, readerUuid, messageJson, paths) { +export function writeMessage(channelName, readerUuid, messageJson, paths) { paths = paths || getPaths(channelName); var time = microSeconds(); var writeObject = { @@ -487,8 +461,7 @@ function writeMessage(channelName, readerUuid, messageJson, paths) { * @return {string[]} */ - -function getReadersUuids(_x9, _x10) { +export function getReadersUuids(_x9, _x10) { return _getReadersUuids.apply(this, arguments); } @@ -525,7 +498,7 @@ function _getReadersUuids() { return _getReadersUuids.apply(this, arguments); } -function messagePath(_x11, _x12, _x13, _x14) { +export function messagePath(_x11, _x12, _x13, _x14) { return _messagePath.apply(this, arguments); } @@ -550,7 +523,7 @@ function _messagePath() { return _messagePath.apply(this, arguments); } -function getAllMessages(_x15, _x16) { +export function getAllMessages(_x15, _x16) { return _getAllMessages.apply(this, arguments); } @@ -589,7 +562,7 @@ function _getAllMessages() { return _getAllMessages.apply(this, arguments); } -function getSingleMessage(channelName, msgObj, paths) { +export function getSingleMessage(channelName, msgObj, paths) { paths = paths || getPaths(channelName); return { path: path.join(paths.messages, msgObj.t + '_' + msgObj.u + '_' + msgObj.to + '.json'), @@ -598,14 +571,12 @@ function getSingleMessage(channelName, msgObj, paths) { token: msgObj.to }; } - -function readMessage(messageObj) { +export function readMessage(messageObj) { return readFile(messageObj.path, 'utf8').then(function (content) { return JSON.parse(content); }); } - -function cleanOldMessages(_x17, _x18) { +export function cleanOldMessages(_x17, _x18) { return _cleanOldMessages.apply(this, arguments); } @@ -636,13 +607,13 @@ function _cleanOldMessages() { return _cleanOldMessages.apply(this, arguments); } -var type = 'node'; +export var type = 'node'; /** * creates a new channelState * @return {Promise} */ -function create(_x19) { +export function create(_x19) { return _create.apply(this, arguments); } @@ -731,7 +702,7 @@ function _create() { return _create.apply(this, arguments); } -function _filterMessage(msgObj, state) { +export function _filterMessage(msgObj, state) { if (msgObj.senderUuid === state.uuid) return false; // not send by own if (state.emittedMessagesIds.has(msgObj.token)) return false; // not already emitted @@ -750,8 +721,7 @@ function _filterMessage(msgObj, state) { * run this */ - -function handleMessagePing(_x20, _x21) { +export function handleMessagePing(_x20, _x21) { return _handleMessagePing.apply(this, arguments); } /** @@ -759,7 +729,6 @@ function handleMessagePing(_x20, _x21) { * @return {Promise} */ - function _handleMessagePing() { _handleMessagePing = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18(state, msgObj) { var messages, useMessages; @@ -835,7 +804,7 @@ function _handleMessagePing() { return _handleMessagePing.apply(this, arguments); } -function refreshReaderClients(channelState) { +export function refreshReaderClients(channelState) { return getReadersUuids(channelState.channelName, channelState.paths).then(function (otherReaders) { // remove subscriptions to closed readers Object.keys(channelState.otherReaderClients).filter(function (readerUuid) { @@ -938,8 +907,7 @@ function refreshReaderClients(channelState) { * @return {Promise} */ - -function postMessage(channelState, messageJson) { +export function postMessage(channelState, messageJson) { var writePromise = writeMessage(channelState.channelName, channelState.uuid, messageJson, channelState.paths); channelState.writeBlockPromise = channelState.writeBlockPromise.then( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() { var _yield$Promise$all, msgObj, pingStr, writeToReadersPromise; @@ -1001,8 +969,7 @@ function postMessage(channelState, messageJson) { * but will speed up things when this module is used in unit-tests. */ - -function emitOverFastPath(state, msgObj, messageJson) { +export function emitOverFastPath(state, msgObj, messageJson) { if (!state.options.node.useFastPath) return; // disabled var others = OTHER_INSTANCES[state.channelName].filter(function (s) { @@ -1019,8 +986,7 @@ function emitOverFastPath(state, msgObj, messageJson) { otherState.messagesCallback(messageJson); }); } - -function onMessage(channelState, fn) { +export function onMessage(channelState, fn) { var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : microSeconds(); channelState.messagesCallbackTime = time; channelState.messagesCallback = fn; @@ -1031,8 +997,7 @@ function onMessage(channelState, fn) { * @return {Promise} */ - -function close(channelState) { +export function close(channelState) { if (channelState.closed) return; channelState.closed = true; channelState.emittedMessagesIds.clear(); @@ -1067,8 +1032,7 @@ function close(channelState) { }, 200); }); } - -function canBeUsed() { +export function canBeUsed() { return isNode; } /** @@ -1078,44 +1042,9 @@ function canBeUsed() { * then to have a fast election. */ - -function averageResponseTime() { +export function averageResponseTime() { return 200; } - -function microSeconds() { +export function microSeconds() { return parseInt(micro.microseconds()); -} - -module.exports = { - TMP_FOLDER_BASE: TMP_FOLDER_BASE, - cleanPipeName: cleanPipeName, - getPaths: getPaths, - ensureFoldersExist: ensureFoldersExist, - clearNodeFolder: clearNodeFolder, - socketPath: socketPath, - socketInfoPath: socketInfoPath, - createSocketInfoFile: createSocketInfoFile, - countChannelFolders: countChannelFolders, - createSocketEventEmitter: createSocketEventEmitter, - openClientConnection: openClientConnection, - writeMessage: writeMessage, - getReadersUuids: getReadersUuids, - messagePath: messagePath, - getAllMessages: getAllMessages, - getSingleMessage: getSingleMessage, - readMessage: readMessage, - cleanOldMessages: cleanOldMessages, - type: type, - create: create, - _filterMessage: _filterMessage, - handleMessagePing: handleMessagePing, - refreshReaderClients: refreshReaderClients, - postMessage: postMessage, - emitOverFastPath: emitOverFastPath, - onMessage: onMessage, - close: close, - canBeUsed: canBeUsed, - averageResponseTime: averageResponseTime, - microSeconds: microSeconds -}; \ No newline at end of file +} \ No newline at end of file diff --git a/dist/es/methods/simulate.js b/dist/esbrowser/methods/simulate.js similarity index 100% rename from dist/es/methods/simulate.js rename to dist/esbrowser/methods/simulate.js diff --git a/dist/es/options.js b/dist/esbrowser/options.js similarity index 100% rename from dist/es/options.js rename to dist/esbrowser/options.js diff --git a/dist/es/util.js b/dist/esbrowser/util.js similarity index 100% rename from dist/es/util.js rename to dist/esbrowser/util.js diff --git a/dist/esnode/broadcast-channel.js b/dist/esnode/broadcast-channel.js new file mode 100644 index 00000000..d57d6050 --- /dev/null +++ b/dist/esnode/broadcast-channel.js @@ -0,0 +1,262 @@ +import { isPromise } from './util.js'; +import { chooseMethod } from './method-chooser.js'; +import { fillOptionsWithDefaults } from './options.js'; +export var BroadcastChannel = function BroadcastChannel(name, options) { + this.name = name; + + if (ENFORCED_OPTIONS) { + options = ENFORCED_OPTIONS; + } + + this.options = fillOptionsWithDefaults(options); + this.method = chooseMethod(this.options); // isListening + + this._iL = false; + /** + * _onMessageListener + * setting onmessage twice, + * will overwrite the first listener + */ + + this._onML = null; + /** + * _addEventListeners + */ + + this._addEL = { + message: [], + internal: [] + }; + /** + * Unsend message promises + * where the sending is still in progress + * @type {Set} + */ + + this._uMP = new Set(); + /** + * _beforeClose + * array of promises that will be awaited + * before the channel is closed + */ + + this._befC = []; + /** + * _preparePromise + */ + + this._prepP = null; + + _prepareChannel(this); +}; // STATICS + +/** + * used to identify if someone overwrites + * window.BroadcastChannel with this + * See methods/native.js + */ + +BroadcastChannel._pubkey = true; +/** + * clears the tmp-folder if is node + * @return {Promise} true if has run, false if not node + */ + +export function clearNodeFolder(options) { + options = fillOptionsWithDefaults(options); + var method = chooseMethod(options); + + if (method.type === 'node') { + return method.clearNodeFolder().then(function () { + return true; + }); + } else { + return Promise.resolve(false); + } +} +/** + * if set, this method is enforced, + * no mather what the options are + */ + +var ENFORCED_OPTIONS; +export function enforceOptions(options) { + ENFORCED_OPTIONS = options; +} // PROTOTYPE + +BroadcastChannel.prototype = { + postMessage: function postMessage(msg) { + if (this.closed) { + throw new Error('BroadcastChannel.postMessage(): ' + 'Cannot post message after channel has closed'); + } + + return _post(this, 'message', msg); + }, + postInternal: function postInternal(msg) { + return _post(this, 'internal', msg); + }, + + set onmessage(fn) { + var time = this.method.microSeconds(); + var listenObj = { + time: time, + fn: fn + }; + + _removeListenerObject(this, 'message', this._onML); + + if (fn && typeof fn === 'function') { + this._onML = listenObj; + + _addListenerObject(this, 'message', listenObj); + } else { + this._onML = null; + } + }, + + addEventListener: function addEventListener(type, fn) { + var time = this.method.microSeconds(); + var listenObj = { + time: time, + fn: fn + }; + + _addListenerObject(this, type, listenObj); + }, + removeEventListener: function removeEventListener(type, fn) { + var obj = this._addEL[type].find(function (obj) { + return obj.fn === fn; + }); + + _removeListenerObject(this, type, obj); + }, + close: function close() { + var _this = this; + + if (this.closed) { + return; + } + + this.closed = true; + var awaitPrepare = this._prepP ? this._prepP : Promise.resolve(); + this._onML = null; + this._addEL.message = []; + return awaitPrepare // wait until all current sending are processed + .then(function () { + return Promise.all(Array.from(_this._uMP)); + }) // run before-close hooks + .then(function () { + return Promise.all(_this._befC.map(function (fn) { + return fn(); + })); + }) // close the channel + .then(function () { + return _this.method.close(_this._state); + }); + }, + + get type() { + return this.method.type; + }, + + get isClosed() { + return this.closed; + } + +}; +/** + * Post a message over the channel + * @returns {Promise} that resolved when the message sending is done + */ + +function _post(broadcastChannel, type, msg) { + var time = broadcastChannel.method.microSeconds(); + var msgObj = { + time: time, + type: type, + data: msg + }; + var awaitPrepare = broadcastChannel._prepP ? broadcastChannel._prepP : Promise.resolve(); + return awaitPrepare.then(function () { + var sendPromise = broadcastChannel.method.postMessage(broadcastChannel._state, msgObj); // add/remove to unsend messages list + + broadcastChannel._uMP.add(sendPromise); + + sendPromise["catch"]().then(function () { + return broadcastChannel._uMP["delete"](sendPromise); + }); + return sendPromise; + }); +} + +function _prepareChannel(channel) { + var maybePromise = channel.method.create(channel.name, channel.options); + + if (isPromise(maybePromise)) { + channel._prepP = maybePromise; + maybePromise.then(function (s) { + // used in tests to simulate slow runtime + + /*if (channel.options.prepareDelay) { + await new Promise(res => setTimeout(res, this.options.prepareDelay)); + }*/ + channel._state = s; + }); + } else { + channel._state = maybePromise; + } +} + +function _hasMessageListeners(channel) { + if (channel._addEL.message.length > 0) return true; + if (channel._addEL.internal.length > 0) return true; + return false; +} + +function _addListenerObject(channel, type, obj) { + channel._addEL[type].push(obj); + + _startListening(channel); +} + +function _removeListenerObject(channel, type, obj) { + channel._addEL[type] = channel._addEL[type].filter(function (o) { + return o !== obj; + }); + + _stopListening(channel); +} + +function _startListening(channel) { + if (!channel._iL && _hasMessageListeners(channel)) { + // someone is listening, start subscribing + var listenerFn = function listenerFn(msgObj) { + channel._addEL[msgObj.type].forEach(function (obj) { + if (msgObj.time >= obj.time) { + obj.fn(msgObj.data); + } + }); + }; + + var time = channel.method.microSeconds(); + + if (channel._prepP) { + channel._prepP.then(function () { + channel._iL = true; + channel.method.onMessage(channel._state, listenerFn, time); + }); + } else { + channel._iL = true; + channel.method.onMessage(channel._state, listenerFn, time); + } + } +} + +function _stopListening(channel) { + if (channel._iL && !_hasMessageListeners(channel)) { + // noone is listening, stop subscribing + channel._iL = false; + var time = channel.method.microSeconds(); + channel.method.onMessage(channel._state, null, time); + } +} \ No newline at end of file diff --git a/dist/esnode/browserify.index.js b/dist/esnode/browserify.index.js new file mode 100644 index 00000000..e500d3df --- /dev/null +++ b/dist/esnode/browserify.index.js @@ -0,0 +1,6 @@ +var module = require('./index.es5.js'); + +var BroadcastChannel = module.BroadcastChannel; +var createLeaderElection = module.createLeaderElection; +window['BroadcastChannel2'] = BroadcastChannel; +window['createLeaderElection'] = createLeaderElection; \ No newline at end of file diff --git a/dist/esnode/index.es5.js b/dist/esnode/index.es5.js new file mode 100644 index 00000000..4d6b827b --- /dev/null +++ b/dist/esnode/index.es5.js @@ -0,0 +1,16 @@ +/** + * because babel can only export on default-attribute, + * we use this for the non-module-build + * this ensures that users do not have to use + * var BroadcastChannel = require('broadcast-channel').default; + * but + * var BroadcastChannel = require('broadcast-channel'); + */ +import { BroadcastChannel, createLeaderElection, clearNodeFolder, enforceOptions, beLeader } from './index.js'; +module.exports = { + BroadcastChannel: BroadcastChannel, + createLeaderElection: createLeaderElection, + clearNodeFolder: clearNodeFolder, + enforceOptions: enforceOptions, + beLeader: beLeader +}; \ No newline at end of file diff --git a/dist/esnode/index.js b/dist/esnode/index.js new file mode 100644 index 00000000..0338ab48 --- /dev/null +++ b/dist/esnode/index.js @@ -0,0 +1,2 @@ +export { BroadcastChannel, clearNodeFolder, enforceOptions } from './broadcast-channel'; +export { createLeaderElection, beLeader } from './leader-election'; \ No newline at end of file diff --git a/dist/esnode/leader-election.js b/dist/esnode/leader-election.js new file mode 100644 index 00000000..e201934b --- /dev/null +++ b/dist/esnode/leader-election.js @@ -0,0 +1,262 @@ +import { sleep, randomToken } from './util.js'; +import unload from 'unload'; + +var LeaderElection = function LeaderElection(channel, options) { + this._channel = channel; + this._options = options; + this.isLeader = false; + this.isDead = false; + this.token = randomToken(); + this._isApl = false; // _isApplying + + this._reApply = false; // things to clean up + + this._unl = []; // _unloads + + this._lstns = []; // _listeners + + this._invs = []; // _intervals + + this._dpL = function () {}; // onduplicate listener + + + this._dpLC = false; // true when onduplicate called +}; + +LeaderElection.prototype = { + applyOnce: function applyOnce() { + var _this = this; + + if (this.isLeader) return Promise.resolve(false); + if (this.isDead) return Promise.resolve(false); // do nothing if already running + + if (this._isApl) { + this._reApply = true; + return Promise.resolve(false); + } + + this._isApl = true; + var stopCriteria = false; + var recieved = []; + + var handleMessage = function handleMessage(msg) { + if (msg.context === 'leader' && msg.token != _this.token) { + recieved.push(msg); + + if (msg.action === 'apply') { + // other is applying + if (msg.token > _this.token) { + // other has higher token, stop applying + stopCriteria = true; + } + } + + if (msg.action === 'tell') { + // other is already leader + stopCriteria = true; + } + } + }; + + this._channel.addEventListener('internal', handleMessage); + + var ret = _sendMessage(this, 'apply') // send out that this one is applying + .then(function () { + return sleep(_this._options.responseTime); + }) // let others time to respond + .then(function () { + if (stopCriteria) return Promise.reject(new Error());else return _sendMessage(_this, 'apply'); + }).then(function () { + return sleep(_this._options.responseTime); + }) // let others time to respond + .then(function () { + if (stopCriteria) return Promise.reject(new Error());else return _sendMessage(_this); + }).then(function () { + return beLeader(_this); + }) // no one disagreed -> this one is now leader + .then(function () { + return true; + })["catch"](function () { + return false; + }) // apply not successfull + .then(function (success) { + _this._channel.removeEventListener('internal', handleMessage); + + _this._isApl = false; + + if (!success && _this._reApply) { + _this._reApply = false; + return _this.applyOnce(); + } else return success; + }); + + return ret; + }, + awaitLeadership: function awaitLeadership() { + if ( + /* _awaitLeadershipPromise */ + !this._aLP) { + this._aLP = _awaitLeadershipOnce(this); + } + + return this._aLP; + }, + + set onduplicate(fn) { + this._dpL = fn; + }, + + die: function die() { + var _this2 = this; + + if (this.isDead) return; + this.isDead = true; + + this._lstns.forEach(function (listener) { + return _this2._channel.removeEventListener('internal', listener); + }); + + this._invs.forEach(function (interval) { + return clearInterval(interval); + }); + + this._unl.forEach(function (uFn) { + uFn.remove(); + }); + + return _sendMessage(this, 'death'); + } +}; + +function _awaitLeadershipOnce(leaderElector) { + if (leaderElector.isLeader) return Promise.resolve(); + return new Promise(function (res) { + var resolved = false; + + function finish() { + if (resolved) { + return; + } + + resolved = true; + clearInterval(interval); + + leaderElector._channel.removeEventListener('internal', whenDeathListener); + + res(true); + } // try once now + + + leaderElector.applyOnce().then(function () { + if (leaderElector.isLeader) { + finish(); + } + }); // try on fallbackInterval + + var interval = setInterval(function () { + leaderElector.applyOnce().then(function () { + if (leaderElector.isLeader) { + finish(); + } + }); + }, leaderElector._options.fallbackInterval); + + leaderElector._invs.push(interval); // try when other leader dies + + + var whenDeathListener = function whenDeathListener(msg) { + if (msg.context === 'leader' && msg.action === 'death') { + leaderElector.applyOnce().then(function () { + if (leaderElector.isLeader) finish(); + }); + } + }; + + leaderElector._channel.addEventListener('internal', whenDeathListener); + + leaderElector._lstns.push(whenDeathListener); + }); +} +/** + * sends and internal message over the broadcast-channel + */ + + +function _sendMessage(leaderElector, action) { + var msgJson = { + context: 'leader', + action: action, + token: leaderElector.token + }; + return leaderElector._channel.postInternal(msgJson); +} + +export function beLeader(leaderElector) { + leaderElector.isLeader = true; + var unloadFn = unload.add(function () { + return leaderElector.die(); + }); + + leaderElector._unl.push(unloadFn); + + var isLeaderListener = function isLeaderListener(msg) { + if (msg.context === 'leader' && msg.action === 'apply') { + _sendMessage(leaderElector, 'tell'); + } + + if (msg.context === 'leader' && msg.action === 'tell' && !leaderElector._dpLC) { + /** + * another instance is also leader! + * This can happen on rare events + * like when the CPU is at 100% for long time + * or the tabs are open very long and the browser throttles them. + * @link https://github.com/pubkey/broadcast-channel/issues/414 + * @link https://github.com/pubkey/broadcast-channel/issues/385 + */ + leaderElector._dpLC = true; + + leaderElector._dpL(); // message the lib user so the app can handle the problem + + + _sendMessage(leaderElector, 'tell'); // ensure other leader also knows the problem + + } + }; + + leaderElector._channel.addEventListener('internal', isLeaderListener); + + leaderElector._lstns.push(isLeaderListener); + + return _sendMessage(leaderElector, 'tell'); +} + +function fillOptionsWithDefaults(options, channel) { + if (!options) options = {}; + options = JSON.parse(JSON.stringify(options)); + + if (!options.fallbackInterval) { + options.fallbackInterval = 3000; + } + + if (!options.responseTime) { + options.responseTime = channel.method.averageResponseTime(channel.options); + } + + return options; +} + +export function createLeaderElection(channel, options) { + if (channel._leaderElector) { + throw new Error('BroadcastChannel already has a leader-elector'); + } + + options = fillOptionsWithDefaults(options, channel); + var elector = new LeaderElection(channel, options); + + channel._befC.push(function () { + return elector.die(); + }); + + channel._leaderElector = elector; + return elector; +} \ No newline at end of file diff --git a/dist/esnode/method-chooser.js b/dist/esnode/method-chooser.js new file mode 100644 index 00000000..03e39cf2 --- /dev/null +++ b/dist/esnode/method-chooser.js @@ -0,0 +1,45 @@ +import NativeMethod from './methods/native.js'; +import IndexeDbMethod from './methods/indexed-db.js'; +import LocalstorageMethod from './methods/localstorage.js'; +import SimulateMethod from './methods/simulate.js'; // the line below will be removed from es5/browser builds + +import * as NodeMethod from './methods/node.js'; +import { isNode } from './util'; // order is important + +var METHODS = [NativeMethod, // fastest +IndexeDbMethod, LocalstorageMethod]; +export function chooseMethod(options) { + var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // the line below will be removed from es5/browser builds + + chooseMethods.push(NodeMethod); // directly chosen + + if (options.type) { + if (options.type === 'simulate') { + // only use simulate-method if directly chosen + return SimulateMethod; + } + + var ret = chooseMethods.find(function (m) { + return m.type === options.type; + }); + if (!ret) throw new Error('method-type ' + options.type + ' not found');else return ret; + } + /** + * if no webworker support is needed, + * remove idb from the list so that localstorage is been chosen + */ + + + if (!options.webWorkerSupport && !isNode) { + chooseMethods = chooseMethods.filter(function (m) { + return m.type !== 'idb'; + }); + } + + var useMethod = chooseMethods.find(function (method) { + return method.canBeUsed(); + }); + if (!useMethod) throw new Error("No useable method found in " + JSON.stringify(METHODS.map(function (m) { + return m.type; + })));else return useMethod; +} \ No newline at end of file diff --git a/dist/esnode/methods/cookies.js b/dist/esnode/methods/cookies.js new file mode 100644 index 00000000..f0cf25c8 --- /dev/null +++ b/dist/esnode/methods/cookies.js @@ -0,0 +1,4 @@ +/** + * if you really need this method, + * implement it + */ \ No newline at end of file diff --git a/dist/esnode/methods/indexed-db.js b/dist/esnode/methods/indexed-db.js new file mode 100644 index 00000000..e47568ca --- /dev/null +++ b/dist/esnode/methods/indexed-db.js @@ -0,0 +1,309 @@ +/** + * this method uses indexeddb to store the messages + * There is currently no observerAPI for idb + * @link https://github.com/w3c/IndexedDB/issues/51 + */ +import { sleep, randomInt, randomToken, microSeconds as micro, isNode } from '../util.js'; +export var microSeconds = micro; +import { ObliviousSet } from 'oblivious-set'; +import { fillOptionsWithDefaults } from '../options'; +var DB_PREFIX = 'pubkey.broadcast-channel-0-'; +var OBJECT_STORE_ID = 'messages'; +export var type = 'idb'; +export function getIdb() { + if (typeof indexedDB !== 'undefined') return indexedDB; + + if (typeof window !== 'undefined') { + if (typeof window.mozIndexedDB !== 'undefined') return window.mozIndexedDB; + if (typeof window.webkitIndexedDB !== 'undefined') return window.webkitIndexedDB; + if (typeof window.msIndexedDB !== 'undefined') return window.msIndexedDB; + } + + return false; +} +export function createDatabase(channelName) { + var IndexedDB = getIdb(); // create table + + var dbName = DB_PREFIX + channelName; + var openRequest = IndexedDB.open(dbName, 1); + + openRequest.onupgradeneeded = function (ev) { + var db = ev.target.result; + db.createObjectStore(OBJECT_STORE_ID, { + keyPath: 'id', + autoIncrement: true + }); + }; + + var dbPromise = new Promise(function (res, rej) { + openRequest.onerror = function (ev) { + return rej(ev); + }; + + openRequest.onsuccess = function () { + res(openRequest.result); + }; + }); + return dbPromise; +} +/** + * writes the new message to the database + * so other readers can find it + */ + +export function writeMessage(db, readerUuid, messageJson) { + var time = new Date().getTime(); + var writeObject = { + uuid: readerUuid, + time: time, + data: messageJson + }; + var transaction = db.transaction([OBJECT_STORE_ID], 'readwrite'); + return new Promise(function (res, rej) { + transaction.oncomplete = function () { + return res(); + }; + + transaction.onerror = function (ev) { + return rej(ev); + }; + + var objectStore = transaction.objectStore(OBJECT_STORE_ID); + objectStore.add(writeObject); + }); +} +export function getAllMessages(db) { + var objectStore = db.transaction(OBJECT_STORE_ID).objectStore(OBJECT_STORE_ID); + var ret = []; + return new Promise(function (res) { + objectStore.openCursor().onsuccess = function (ev) { + var cursor = ev.target.result; + + if (cursor) { + ret.push(cursor.value); //alert("Name for SSN " + cursor.key + " is " + cursor.value.name); + + cursor["continue"](); + } else { + res(ret); + } + }; + }); +} +export function getMessagesHigherThan(db, lastCursorId) { + var objectStore = db.transaction(OBJECT_STORE_ID).objectStore(OBJECT_STORE_ID); + var ret = []; + + function openCursor() { + // Occasionally Safari will fail on IDBKeyRange.bound, this + // catches that error, having it open the cursor to the first + // item. When it gets data it will advance to the desired key. + try { + var keyRangeValue = IDBKeyRange.bound(lastCursorId + 1, Infinity); + return objectStore.openCursor(keyRangeValue); + } catch (e) { + return objectStore.openCursor(); + } + } + + return new Promise(function (res) { + openCursor().onsuccess = function (ev) { + var cursor = ev.target.result; + + if (cursor) { + if (cursor.value.id < lastCursorId + 1) { + cursor["continue"](lastCursorId + 1); + } else { + ret.push(cursor.value); + cursor["continue"](); + } + } else { + res(ret); + } + }; + }); +} +export function removeMessageById(db, id) { + var request = db.transaction([OBJECT_STORE_ID], 'readwrite').objectStore(OBJECT_STORE_ID)["delete"](id); + return new Promise(function (res) { + request.onsuccess = function () { + return res(); + }; + }); +} +export function getOldMessages(db, ttl) { + var olderThen = new Date().getTime() - ttl; + var objectStore = db.transaction(OBJECT_STORE_ID).objectStore(OBJECT_STORE_ID); + var ret = []; + return new Promise(function (res) { + objectStore.openCursor().onsuccess = function (ev) { + var cursor = ev.target.result; + + if (cursor) { + var msgObk = cursor.value; + + if (msgObk.time < olderThen) { + ret.push(msgObk); //alert("Name for SSN " + cursor.key + " is " + cursor.value.name); + + cursor["continue"](); + } else { + // no more old messages, + res(ret); + return; + } + } else { + res(ret); + } + }; + }); +} +export function cleanOldMessages(db, ttl) { + return getOldMessages(db, ttl).then(function (tooOld) { + return Promise.all(tooOld.map(function (msgObj) { + return removeMessageById(db, msgObj.id); + })); + }); +} +export function create(channelName, options) { + options = fillOptionsWithDefaults(options); + return createDatabase(channelName).then(function (db) { + var state = { + closed: false, + lastCursorId: 0, + channelName: channelName, + options: options, + uuid: randomToken(), + + /** + * emittedMessagesIds + * contains all messages that have been emitted before + * @type {ObliviousSet} + */ + eMIs: new ObliviousSet(options.idb.ttl * 2), + // ensures we do not read messages in parrallel + writeBlockPromise: Promise.resolve(), + messagesCallback: null, + readQueuePromises: [], + db: db + }; + /** + * Handle abrupt closes that do not originate from db.close(). + * This could happen, for example, if the underlying storage is + * removed or if the user clears the database in the browser's + * history preferences. + */ + + db.onclose = function () { + state.closed = true; + if (options.idb.onclose) options.idb.onclose(); + }; + /** + * if service-workers are used, + * we have no 'storage'-event if they post a message, + * therefore we also have to set an interval + */ + + + _readLoop(state); + + return state; + }); +} + +function _readLoop(state) { + if (state.closed) return; + readNewMessages(state).then(function () { + return sleep(state.options.idb.fallbackInterval); + }).then(function () { + return _readLoop(state); + }); +} + +function _filterMessage(msgObj, state) { + if (msgObj.uuid === state.uuid) return false; // send by own + + if (state.eMIs.has(msgObj.id)) return false; // already emitted + + if (msgObj.data.time < state.messagesCallbackTime) return false; // older then onMessageCallback + + return true; +} +/** + * reads all new messages from the database and emits them + */ + + +function readNewMessages(state) { + // channel already closed + if (state.closed) return Promise.resolve(); // if no one is listening, we do not need to scan for new messages + + if (!state.messagesCallback) return Promise.resolve(); + return getMessagesHigherThan(state.db, state.lastCursorId).then(function (newerMessages) { + var useMessages = newerMessages + /** + * there is a bug in iOS where the msgObj can be undefined some times + * so we filter them out + * @link https://github.com/pubkey/broadcast-channel/issues/19 + */ + .filter(function (msgObj) { + return !!msgObj; + }).map(function (msgObj) { + if (msgObj.id > state.lastCursorId) { + state.lastCursorId = msgObj.id; + } + + return msgObj; + }).filter(function (msgObj) { + return _filterMessage(msgObj, state); + }).sort(function (msgObjA, msgObjB) { + return msgObjA.time - msgObjB.time; + }); // sort by time + + useMessages.forEach(function (msgObj) { + if (state.messagesCallback) { + state.eMIs.add(msgObj.id); + state.messagesCallback(msgObj.data); + } + }); + return Promise.resolve(); + }); +} + +export function close(channelState) { + channelState.closed = true; + channelState.db.close(); +} +export function postMessage(channelState, messageJson) { + channelState.writeBlockPromise = channelState.writeBlockPromise.then(function () { + return writeMessage(channelState.db, channelState.uuid, messageJson); + }).then(function () { + if (randomInt(0, 10) === 0) { + /* await (do not await) */ + cleanOldMessages(channelState.db, channelState.options.idb.ttl); + } + }); + return channelState.writeBlockPromise; +} +export function onMessage(channelState, fn, time) { + channelState.messagesCallbackTime = time; + channelState.messagesCallback = fn; + readNewMessages(channelState); +} +export function canBeUsed() { + if (isNode) return false; + var idb = getIdb(); + if (!idb) return false; + return true; +} +export function averageResponseTime(options) { + return options.idb.fallbackInterval * 2; +} +export default { + create: create, + close: close, + onMessage: onMessage, + postMessage: postMessage, + canBeUsed: canBeUsed, + type: type, + averageResponseTime: averageResponseTime, + microSeconds: microSeconds +}; \ No newline at end of file diff --git a/dist/esnode/methods/localstorage.js b/dist/esnode/methods/localstorage.js new file mode 100644 index 00000000..d656c9fc --- /dev/null +++ b/dist/esnode/methods/localstorage.js @@ -0,0 +1,163 @@ +/** + * A localStorage-only method which uses localstorage and its 'storage'-event + * This does not work inside of webworkers because they have no access to locastorage + * This is basically implemented to support IE9 or your grandmothers toaster. + * @link https://caniuse.com/#feat=namevalue-storage + * @link https://caniuse.com/#feat=indexeddb + */ +import { ObliviousSet } from 'oblivious-set'; +import { fillOptionsWithDefaults } from '../options'; +import { sleep, randomToken, microSeconds as micro, isNode } from '../util'; +export var microSeconds = micro; +var KEY_PREFIX = 'pubkey.broadcastChannel-'; +export var type = 'localstorage'; +/** + * copied from crosstab + * @link https://github.com/tejacques/crosstab/blob/master/src/crosstab.js#L32 + */ + +export function getLocalStorage() { + var localStorage; + if (typeof window === 'undefined') return null; + + try { + localStorage = window.localStorage; + localStorage = window['ie8-eventlistener/storage'] || window.localStorage; + } catch (e) {// New versions of Firefox throw a Security exception + // if cookies are disabled. See + // https://bugzilla.mozilla.org/show_bug.cgi?id=1028153 + } + + return localStorage; +} +export function storageKey(channelName) { + return KEY_PREFIX + channelName; +} +/** +* writes the new message to the storage +* and fires the storage-event so other readers can find it +*/ + +export function postMessage(channelState, messageJson) { + return new Promise(function (res) { + sleep().then(function () { + var key = storageKey(channelState.channelName); + var writeObj = { + token: randomToken(), + time: new Date().getTime(), + data: messageJson, + uuid: channelState.uuid + }; + var value = JSON.stringify(writeObj); + getLocalStorage().setItem(key, value); + /** + * StorageEvent does not fire the 'storage' event + * in the window that changes the state of the local storage. + * So we fire it manually + */ + + var ev = document.createEvent('Event'); + ev.initEvent('storage', true, true); + ev.key = key; + ev.newValue = value; + window.dispatchEvent(ev); + res(); + }); + }); +} +export function addStorageEventListener(channelName, fn) { + var key = storageKey(channelName); + + var listener = function listener(ev) { + if (ev.key === key) { + fn(JSON.parse(ev.newValue)); + } + }; + + window.addEventListener('storage', listener); + return listener; +} +export function removeStorageEventListener(listener) { + window.removeEventListener('storage', listener); +} +export function create(channelName, options) { + options = fillOptionsWithDefaults(options); + + if (!canBeUsed()) { + throw new Error('BroadcastChannel: localstorage cannot be used'); + } + + var uuid = randomToken(); + /** + * eMIs + * contains all messages that have been emitted before + * @type {ObliviousSet} + */ + + var eMIs = new ObliviousSet(options.localstorage.removeTimeout); + var state = { + channelName: channelName, + uuid: uuid, + eMIs: eMIs // emittedMessagesIds + + }; + state.listener = addStorageEventListener(channelName, function (msgObj) { + if (!state.messagesCallback) return; // no listener + + if (msgObj.uuid === uuid) return; // own message + + if (!msgObj.token || eMIs.has(msgObj.token)) return; // already emitted + + if (msgObj.data.time && msgObj.data.time < state.messagesCallbackTime) return; // too old + + eMIs.add(msgObj.token); + state.messagesCallback(msgObj.data); + }); + return state; +} +export function close(channelState) { + removeStorageEventListener(channelState.listener); +} +export function onMessage(channelState, fn, time) { + channelState.messagesCallbackTime = time; + channelState.messagesCallback = fn; +} +export function canBeUsed() { + if (isNode) return false; + var ls = getLocalStorage(); + if (!ls) return false; + + try { + var key = '__broadcastchannel_check'; + ls.setItem(key, 'works'); + ls.removeItem(key); + } catch (e) { + // Safari 10 in private mode will not allow write access to local + // storage and fail with a QuotaExceededError. See + // https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API#Private_Browsing_Incognito_modes + return false; + } + + return true; +} +export function averageResponseTime() { + var defaultTime = 120; + var userAgent = navigator.userAgent.toLowerCase(); + + if (userAgent.includes('safari') && !userAgent.includes('chrome')) { + // safari is much slower so this time is higher + return defaultTime * 2; + } + + return defaultTime; +} +export default { + create: create, + close: close, + onMessage: onMessage, + postMessage: postMessage, + canBeUsed: canBeUsed, + type: type, + averageResponseTime: averageResponseTime, + microSeconds: microSeconds +}; \ No newline at end of file diff --git a/dist/esnode/methods/native.js b/dist/esnode/methods/native.js new file mode 100644 index 00000000..cd8bf407 --- /dev/null +++ b/dist/esnode/methods/native.js @@ -0,0 +1,62 @@ +import { microSeconds as micro, isNode } from '../util'; +export var microSeconds = micro; +export var type = 'native'; +export function create(channelName) { + var state = { + messagesCallback: null, + bc: new BroadcastChannel(channelName), + subFns: [] // subscriberFunctions + + }; + + state.bc.onmessage = function (msg) { + if (state.messagesCallback) { + state.messagesCallback(msg.data); + } + }; + + return state; +} +export function close(channelState) { + channelState.bc.close(); + channelState.subFns = []; +} +export function postMessage(channelState, messageJson) { + try { + channelState.bc.postMessage(messageJson, false); + return Promise.resolve(); + } catch (err) { + return Promise.reject(err); + } +} +export function onMessage(channelState, fn) { + channelState.messagesCallback = fn; +} +export function canBeUsed() { + /** + * in the electron-renderer, isNode will be true even if we are in browser-context + * so we also check if window is undefined + */ + if (isNode && typeof window === 'undefined') return false; + + if (typeof BroadcastChannel === 'function') { + if (BroadcastChannel._pubkey) { + throw new Error('BroadcastChannel: Do not overwrite window.BroadcastChannel with this module, this is not a polyfill'); + } + + return true; + } else return false; +} +export function averageResponseTime() { + return 150; +} +export default { + create: create, + close: close, + onMessage: onMessage, + postMessage: postMessage, + canBeUsed: canBeUsed, + type: type, + averageResponseTime: averageResponseTime, + microSeconds: microSeconds +}; \ No newline at end of file diff --git a/dist/esnode/methods/node.js b/dist/esnode/methods/node.js new file mode 100644 index 00000000..4993741d --- /dev/null +++ b/dist/esnode/methods/node.js @@ -0,0 +1,1050 @@ +import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; +import _regeneratorRuntime from "@babel/runtime/regenerator"; + +/** + * this method is used in nodejs-environments. + * The ipc is handled via sockets and file-writes to the tmp-folder + */ +import util from 'util'; +import fs from 'fs'; +import os from 'os'; +import events from 'events'; +import net from 'net'; +import path from 'path'; +import micro from 'nano-time'; +import rimraf from 'rimraf'; +import { sha3_224 } from 'js-sha3'; +import isNode from 'detect-node'; +import unload from 'unload'; +import { fillOptionsWithDefaults } from '../options.js'; +import { randomInt, randomToken } from '../util.js'; +import { ObliviousSet } from 'oblivious-set'; +/** + * windows sucks, so we have handle windows-type of socket-paths + * @link https://gist.github.com/domenic/2790533#gistcomment-331356 + */ + +export function cleanPipeName(str) { + if (process.platform === 'win32' && !str.startsWith('\\\\.\\pipe\\')) { + str = str.replace(/^\//, ''); + str = str.replace(/\//g, '-'); + return '\\\\.\\pipe\\' + str; + } else { + return str; + } +} +var mkdir = util.promisify(fs.mkdir); +var writeFile = util.promisify(fs.writeFile); +var readFile = util.promisify(fs.readFile); +var unlink = util.promisify(fs.unlink); +var readdir = util.promisify(fs.readdir); +var chmod = util.promisify(fs.chmod); +var removeDir = util.promisify(rimraf); +var OTHER_INSTANCES = {}; +var TMP_FOLDER_NAME = 'pubkey.bc'; +export var TMP_FOLDER_BASE = path.join(os.tmpdir(), TMP_FOLDER_NAME); +var getPathsCache = new Map(); +export function getPaths(channelName) { + if (!getPathsCache.has(channelName)) { + var channelHash = sha3_224(channelName); // use hash incase of strange characters + + /** + * because the lenght of socket-paths is limited, we use only the first 20 chars + * and also start with A to ensure we do not start with a number + * @link https://serverfault.com/questions/641347/check-if-a-path-exceeds-maximum-for-unix-domain-socket + */ + + var channelFolder = 'A' + channelHash.substring(0, 20); + var channelPathBase = path.join(TMP_FOLDER_BASE, channelFolder); + var folderPathReaders = path.join(channelPathBase, 'rdrs'); + var folderPathMessages = path.join(channelPathBase, 'messages'); + var ret = { + channelBase: channelPathBase, + readers: folderPathReaders, + messages: folderPathMessages + }; + getPathsCache.set(channelName, ret); + return ret; + } + + return getPathsCache.get(channelName); +} +var ENSURE_BASE_FOLDER_EXISTS_PROMISE = null; + +function ensureBaseFolderExists() { + return _ensureBaseFolderExists.apply(this, arguments); +} + +function _ensureBaseFolderExists() { + _ensureBaseFolderExists = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4() { + return _regeneratorRuntime.wrap(function _callee4$(_context4) { + while (1) { + switch (_context4.prev = _context4.next) { + case 0: + if (!ENSURE_BASE_FOLDER_EXISTS_PROMISE) { + ENSURE_BASE_FOLDER_EXISTS_PROMISE = mkdir(TMP_FOLDER_BASE)["catch"](function () { + return null; + }); + } + + return _context4.abrupt("return", ENSURE_BASE_FOLDER_EXISTS_PROMISE); + + case 2: + case "end": + return _context4.stop(); + } + } + }, _callee4); + })); + return _ensureBaseFolderExists.apply(this, arguments); +} + +export function ensureFoldersExist(_x, _x2) { + return _ensureFoldersExist.apply(this, arguments); +} +/** + * removes the tmp-folder + * @return {Promise} + */ + +function _ensureFoldersExist() { + _ensureFoldersExist = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(channelName, paths) { + var chmodValue; + return _regeneratorRuntime.wrap(function _callee5$(_context5) { + while (1) { + switch (_context5.prev = _context5.next) { + case 0: + paths = paths || getPaths(channelName); + _context5.next = 3; + return ensureBaseFolderExists(); + + case 3: + _context5.next = 5; + return mkdir(paths.channelBase)["catch"](function () { + return null; + }); + + case 5: + _context5.next = 7; + return Promise.all([mkdir(paths.readers)["catch"](function () { + return null; + }), mkdir(paths.messages)["catch"](function () { + return null; + })]); + + case 7: + // set permissions so other users can use the same channel + chmodValue = '777'; + _context5.next = 10; + return Promise.all([chmod(paths.channelBase, chmodValue), chmod(paths.readers, chmodValue), chmod(paths.messages, chmodValue)])["catch"](function () { + return null; + }); + + case 10: + case "end": + return _context5.stop(); + } + } + }, _callee5); + })); + return _ensureFoldersExist.apply(this, arguments); +} + +export function clearNodeFolder() { + return _clearNodeFolder.apply(this, arguments); +} + +function _clearNodeFolder() { + _clearNodeFolder = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6() { + return _regeneratorRuntime.wrap(function _callee6$(_context6) { + while (1) { + switch (_context6.prev = _context6.next) { + case 0: + if (!(!TMP_FOLDER_BASE || TMP_FOLDER_BASE === '' || TMP_FOLDER_BASE === '/')) { + _context6.next = 2; + break; + } + + throw new Error('BroadcastChannel.clearNodeFolder(): path is wrong'); + + case 2: + ENSURE_BASE_FOLDER_EXISTS_PROMISE = null; + _context6.next = 5; + return removeDir(TMP_FOLDER_BASE); + + case 5: + ENSURE_BASE_FOLDER_EXISTS_PROMISE = null; + return _context6.abrupt("return", true); + + case 7: + case "end": + return _context6.stop(); + } + } + }, _callee6); + })); + return _clearNodeFolder.apply(this, arguments); +} + +export function socketPath(channelName, readerUuid, paths) { + paths = paths || getPaths(channelName); + var socketPath = path.join(paths.readers, readerUuid + '.s'); + return cleanPipeName(socketPath); +} +export function socketInfoPath(channelName, readerUuid, paths) { + paths = paths || getPaths(channelName); + var socketPath = path.join(paths.readers, readerUuid + '.json'); + return socketPath; +} +/** + * Because it is not possible to get all socket-files in a folder, + * when used under fucking windows, + * we have to set a normal file so other readers know our socket exists + */ + +export function createSocketInfoFile(channelName, readerUuid, paths) { + var pathToFile = socketInfoPath(channelName, readerUuid, paths); + return writeFile(pathToFile, JSON.stringify({ + time: microSeconds() + })).then(function () { + return pathToFile; + }); +} +/** + * returns the amount of channel-folders in the tmp-directory + * @return {Promise} + */ + +export function countChannelFolders() { + return _countChannelFolders.apply(this, arguments); +} + +function _countChannelFolders() { + _countChannelFolders = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7() { + var folders; + return _regeneratorRuntime.wrap(function _callee7$(_context7) { + while (1) { + switch (_context7.prev = _context7.next) { + case 0: + _context7.next = 2; + return ensureBaseFolderExists(); + + case 2: + _context7.next = 4; + return readdir(TMP_FOLDER_BASE); + + case 4: + folders = _context7.sent; + return _context7.abrupt("return", folders.length); + + case 6: + case "end": + return _context7.stop(); + } + } + }, _callee7); + })); + return _countChannelFolders.apply(this, arguments); +} + +function connectionError(_x3) { + return _connectionError.apply(this, arguments); +} +/** + * creates the socket-file and subscribes to it + * @return {{emitter: EventEmitter, server: any}} + */ + + +function _connectionError() { + _connectionError = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8(originalError) { + var count, addObj, text, newError; + return _regeneratorRuntime.wrap(function _callee8$(_context8) { + while (1) { + switch (_context8.prev = _context8.next) { + case 0: + _context8.next = 2; + return countChannelFolders(); + + case 2: + count = _context8.sent; + + if (!(count < 30)) { + _context8.next = 5; + break; + } + + return _context8.abrupt("return", originalError); + + case 5: + addObj = {}; + Object.entries(originalError).forEach(function (_ref4) { + var k = _ref4[0], + v = _ref4[1]; + return addObj[k] = v; + }); + text = 'BroadcastChannel.create(): error: ' + 'This might happen if you have created to many channels, ' + 'like when you use BroadcastChannel in unit-tests.' + 'Try using BroadcastChannel.clearNodeFolder() to clear the tmp-folder before each test.' + 'See https://github.com/pubkey/broadcast-channel#clear-tmp-folder'; + newError = new Error(text + ': ' + JSON.stringify(addObj, null, 2)); + return _context8.abrupt("return", newError); + + case 10: + case "end": + return _context8.stop(); + } + } + }, _callee8); + })); + return _connectionError.apply(this, arguments); +} + +export function createSocketEventEmitter(_x4, _x5, _x6) { + return _createSocketEventEmitter.apply(this, arguments); +} + +function _createSocketEventEmitter() { + _createSocketEventEmitter = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11(channelName, readerUuid, paths) { + var pathToSocket, emitter, server; + return _regeneratorRuntime.wrap(function _callee11$(_context11) { + while (1) { + switch (_context11.prev = _context11.next) { + case 0: + pathToSocket = socketPath(channelName, readerUuid, paths); + emitter = new events.EventEmitter(); + server = net.createServer(function (stream) { + stream.on('end', function () {}); + stream.on('data', function (msg) { + emitter.emit('data', msg.toString()); + }); + }); + _context11.next = 5; + return new Promise(function (resolve, reject) { + server.on('error', /*#__PURE__*/function () { + var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9(err) { + var useErr; + return _regeneratorRuntime.wrap(function _callee9$(_context9) { + while (1) { + switch (_context9.prev = _context9.next) { + case 0: + _context9.next = 2; + return connectionError(err); + + case 2: + useErr = _context9.sent; + reject(useErr); + + case 4: + case "end": + return _context9.stop(); + } + } + }, _callee9); + })); + + return function (_x24) { + return _ref5.apply(this, arguments); + }; + }()); + server.listen(pathToSocket, /*#__PURE__*/function () { + var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10(err, res) { + var useErr; + return _regeneratorRuntime.wrap(function _callee10$(_context10) { + while (1) { + switch (_context10.prev = _context10.next) { + case 0: + if (!err) { + _context10.next = 7; + break; + } + + _context10.next = 3; + return connectionError(err); + + case 3: + useErr = _context10.sent; + reject(useErr); + _context10.next = 8; + break; + + case 7: + resolve(res); + + case 8: + case "end": + return _context10.stop(); + } + } + }, _callee10); + })); + + return function (_x25, _x26) { + return _ref6.apply(this, arguments); + }; + }()); + }); + + case 5: + return _context11.abrupt("return", { + path: pathToSocket, + emitter: emitter, + server: server + }); + + case 6: + case "end": + return _context11.stop(); + } + } + }, _callee11); + })); + return _createSocketEventEmitter.apply(this, arguments); +} + +export function openClientConnection(_x7, _x8) { + return _openClientConnection.apply(this, arguments); +} +/** + * writes the new message to the file-system + * so other readers can find it + * @return {Promise} + */ + +function _openClientConnection() { + _openClientConnection = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee12(channelName, readerUuid) { + var pathToSocket, client; + return _regeneratorRuntime.wrap(function _callee12$(_context12) { + while (1) { + switch (_context12.prev = _context12.next) { + case 0: + pathToSocket = socketPath(channelName, readerUuid); + client = new net.Socket(); + return _context12.abrupt("return", new Promise(function (res, rej) { + client.connect(pathToSocket, function () { + return res(client); + }); + client.on('error', function (err) { + return rej(err); + }); + })); + + case 3: + case "end": + return _context12.stop(); + } + } + }, _callee12); + })); + return _openClientConnection.apply(this, arguments); +} + +export function writeMessage(channelName, readerUuid, messageJson, paths) { + paths = paths || getPaths(channelName); + var time = microSeconds(); + var writeObject = { + uuid: readerUuid, + time: time, + data: messageJson + }; + var token = randomToken(); + var fileName = time + '_' + readerUuid + '_' + token + '.json'; + var msgPath = path.join(paths.messages, fileName); + return writeFile(msgPath, JSON.stringify(writeObject)).then(function () { + return { + time: time, + uuid: readerUuid, + token: token, + path: msgPath + }; + }); +} +/** + * returns the uuids of all readers + * @return {string[]} + */ + +export function getReadersUuids(_x9, _x10) { + return _getReadersUuids.apply(this, arguments); +} + +function _getReadersUuids() { + _getReadersUuids = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee13(channelName, paths) { + var readersPath, files; + return _regeneratorRuntime.wrap(function _callee13$(_context13) { + while (1) { + switch (_context13.prev = _context13.next) { + case 0: + paths = paths || getPaths(channelName); + readersPath = paths.readers; + _context13.next = 4; + return readdir(readersPath); + + case 4: + files = _context13.sent; + return _context13.abrupt("return", files.map(function (file) { + return file.split('.'); + }).filter(function (split) { + return split[1] === 'json'; + }) // do not scan .socket-files + .map(function (split) { + return split[0]; + })); + + case 6: + case "end": + return _context13.stop(); + } + } + }, _callee13); + })); + return _getReadersUuids.apply(this, arguments); +} + +export function messagePath(_x11, _x12, _x13, _x14) { + return _messagePath.apply(this, arguments); +} + +function _messagePath() { + _messagePath = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14(channelName, time, token, writerUuid) { + var fileName, msgPath; + return _regeneratorRuntime.wrap(function _callee14$(_context14) { + while (1) { + switch (_context14.prev = _context14.next) { + case 0: + fileName = time + '_' + writerUuid + '_' + token + '.json'; + msgPath = path.join(getPaths(channelName).messages, fileName); + return _context14.abrupt("return", msgPath); + + case 3: + case "end": + return _context14.stop(); + } + } + }, _callee14); + })); + return _messagePath.apply(this, arguments); +} + +export function getAllMessages(_x15, _x16) { + return _getAllMessages.apply(this, arguments); +} + +function _getAllMessages() { + _getAllMessages = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee15(channelName, paths) { + var messagesPath, files; + return _regeneratorRuntime.wrap(function _callee15$(_context15) { + while (1) { + switch (_context15.prev = _context15.next) { + case 0: + paths = paths || getPaths(channelName); + messagesPath = paths.messages; + _context15.next = 4; + return readdir(messagesPath); + + case 4: + files = _context15.sent; + return _context15.abrupt("return", files.map(function (file) { + var fileName = file.split('.')[0]; + var split = fileName.split('_'); + return { + path: path.join(messagesPath, file), + time: parseInt(split[0]), + senderUuid: split[1], + token: split[2] + }; + })); + + case 6: + case "end": + return _context15.stop(); + } + } + }, _callee15); + })); + return _getAllMessages.apply(this, arguments); +} + +export function getSingleMessage(channelName, msgObj, paths) { + paths = paths || getPaths(channelName); + return { + path: path.join(paths.messages, msgObj.t + '_' + msgObj.u + '_' + msgObj.to + '.json'), + time: msgObj.t, + senderUuid: msgObj.u, + token: msgObj.to + }; +} +export function readMessage(messageObj) { + return readFile(messageObj.path, 'utf8').then(function (content) { + return JSON.parse(content); + }); +} +export function cleanOldMessages(_x17, _x18) { + return _cleanOldMessages.apply(this, arguments); +} + +function _cleanOldMessages() { + _cleanOldMessages = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee16(messageObjects, ttl) { + var olderThen; + return _regeneratorRuntime.wrap(function _callee16$(_context16) { + while (1) { + switch (_context16.prev = _context16.next) { + case 0: + olderThen = Date.now() - ttl; + _context16.next = 3; + return Promise.all(messageObjects.filter(function (obj) { + return obj.time / 1000 < olderThen; + }).map(function (obj) { + return unlink(obj.path)["catch"](function () { + return null; + }); + })); + + case 3: + case "end": + return _context16.stop(); + } + } + }, _callee16); + })); + return _cleanOldMessages.apply(this, arguments); +} + +export var type = 'node'; +/** + * creates a new channelState + * @return {Promise} + */ + +export function create(_x19) { + return _create.apply(this, arguments); +} + +function _create() { + _create = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee17(channelName) { + var options, + time, + paths, + ensureFolderExistsPromise, + uuid, + state, + _yield$Promise$all2, + socketEE, + infoFilePath, + _args17 = arguments; + + return _regeneratorRuntime.wrap(function _callee17$(_context17) { + while (1) { + switch (_context17.prev = _context17.next) { + case 0: + options = _args17.length > 1 && _args17[1] !== undefined ? _args17[1] : {}; + options = fillOptionsWithDefaults(options); + time = microSeconds(); + paths = getPaths(channelName); + ensureFolderExistsPromise = ensureFoldersExist(channelName, paths); + uuid = randomToken(); + state = { + time: time, + channelName: channelName, + options: options, + uuid: uuid, + paths: paths, + // contains all messages that have been emitted before + emittedMessagesIds: new ObliviousSet(options.node.ttl * 2), + messagesCallbackTime: null, + messagesCallback: null, + // ensures we do not read messages in parrallel + writeBlockPromise: Promise.resolve(), + otherReaderClients: {}, + // ensure if process crashes, everything is cleaned up + removeUnload: unload.add(function () { + return close(state); + }), + closed: false + }; + if (!OTHER_INSTANCES[channelName]) OTHER_INSTANCES[channelName] = []; + OTHER_INSTANCES[channelName].push(state); + _context17.next = 11; + return ensureFolderExistsPromise; + + case 11: + _context17.next = 13; + return Promise.all([createSocketEventEmitter(channelName, uuid, paths), createSocketInfoFile(channelName, uuid, paths), refreshReaderClients(state)]); + + case 13: + _yield$Promise$all2 = _context17.sent; + socketEE = _yield$Promise$all2[0]; + infoFilePath = _yield$Promise$all2[1]; + state.socketEE = socketEE; + state.infoFilePath = infoFilePath; // when new message comes in, we read it and emit it + + socketEE.emitter.on('data', function (data) { + // if the socket is used fast, it may appear that multiple messages are flushed at once + // so we have to split them before + var singleOnes = data.split('|'); + singleOnes.filter(function (single) { + return single !== ''; + }).forEach(function (single) { + try { + var obj = JSON.parse(single); + handleMessagePing(state, obj); + } catch (err) { + throw new Error('could not parse data: ' + single); + } + }); + }); + return _context17.abrupt("return", state); + + case 20: + case "end": + return _context17.stop(); + } + } + }, _callee17); + })); + return _create.apply(this, arguments); +} + +export function _filterMessage(msgObj, state) { + if (msgObj.senderUuid === state.uuid) return false; // not send by own + + if (state.emittedMessagesIds.has(msgObj.token)) return false; // not already emitted + + if (!state.messagesCallback) return false; // no listener + + if (msgObj.time < state.messagesCallbackTime) return false; // not older then onMessageCallback + + if (msgObj.time < state.time) return false; // msgObj is older then channel + + state.emittedMessagesIds.add(msgObj.token); + return true; +} +/** + * when the socket pings, so that we now new messages came, + * run this + */ + +export function handleMessagePing(_x20, _x21) { + return _handleMessagePing.apply(this, arguments); +} +/** + * ensures that the channelState is connected with all other readers + * @return {Promise} + */ + +function _handleMessagePing() { + _handleMessagePing = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee18(state, msgObj) { + var messages, useMessages; + return _regeneratorRuntime.wrap(function _callee18$(_context18) { + while (1) { + switch (_context18.prev = _context18.next) { + case 0: + if (state.messagesCallback) { + _context18.next = 2; + break; + } + + return _context18.abrupt("return"); + + case 2: + if (msgObj) { + _context18.next = 8; + break; + } + + _context18.next = 5; + return getAllMessages(state.channelName, state.paths); + + case 5: + messages = _context18.sent; + _context18.next = 9; + break; + + case 8: + // get single message + messages = [getSingleMessage(state.channelName, msgObj, state.paths)]; + + case 9: + useMessages = messages.filter(function (msgObj) { + return _filterMessage(msgObj, state); + }).sort(function (msgObjA, msgObjB) { + return msgObjA.time - msgObjB.time; + }); // sort by time + // if no listener or message, so not do anything + + if (!(!useMessages.length || !state.messagesCallback)) { + _context18.next = 12; + break; + } + + return _context18.abrupt("return"); + + case 12: + _context18.next = 14; + return Promise.all(useMessages.map(function (msgObj) { + return readMessage(msgObj).then(function (content) { + return msgObj.content = content; + }); + })); + + case 14: + useMessages.forEach(function (msgObj) { + state.emittedMessagesIds.add(msgObj.token); + + if (state.messagesCallback) { + // emit to subscribers + state.messagesCallback(msgObj.content.data); + } + }); + + case 15: + case "end": + return _context18.stop(); + } + } + }, _callee18); + })); + return _handleMessagePing.apply(this, arguments); +} + +export function refreshReaderClients(channelState) { + return getReadersUuids(channelState.channelName, channelState.paths).then(function (otherReaders) { + // remove subscriptions to closed readers + Object.keys(channelState.otherReaderClients).filter(function (readerUuid) { + return !otherReaders.includes(readerUuid); + }).forEach( /*#__PURE__*/function () { + var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(readerUuid) { + return _regeneratorRuntime.wrap(function _callee$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + _context.prev = 0; + _context.next = 3; + return channelState.otherReaderClients[readerUuid].destroy(); + + case 3: + _context.next = 7; + break; + + case 5: + _context.prev = 5; + _context.t0 = _context["catch"](0); + + case 7: + delete channelState.otherReaderClients[readerUuid]; + + case 8: + case "end": + return _context.stop(); + } + } + }, _callee, null, [[0, 5]]); + })); + + return function (_x22) { + return _ref.apply(this, arguments); + }; + }()); // add new readers + + return Promise.all(otherReaders.filter(function (readerUuid) { + return readerUuid !== channelState.uuid; + }) // not own + .filter(function (readerUuid) { + return !channelState.otherReaderClients[readerUuid]; + }) // not already has client + .map( /*#__PURE__*/function () { + var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(readerUuid) { + var client; + return _regeneratorRuntime.wrap(function _callee2$(_context2) { + while (1) { + switch (_context2.prev = _context2.next) { + case 0: + _context2.prev = 0; + + if (!channelState.closed) { + _context2.next = 3; + break; + } + + return _context2.abrupt("return"); + + case 3: + _context2.prev = 3; + _context2.next = 6; + return openClientConnection(channelState.channelName, readerUuid); + + case 6: + client = _context2.sent; + channelState.otherReaderClients[readerUuid] = client; + _context2.next = 12; + break; + + case 10: + _context2.prev = 10; + _context2.t0 = _context2["catch"](3); + + case 12: + _context2.next = 16; + break; + + case 14: + _context2.prev = 14; + _context2.t1 = _context2["catch"](0); + + case 16: + case "end": + return _context2.stop(); + } + } + }, _callee2, null, [[0, 14], [3, 10]]); + })); + + return function (_x23) { + return _ref2.apply(this, arguments); + }; + }())); + }); +} +/** + * post a message to the other readers + * @return {Promise} + */ + +export function postMessage(channelState, messageJson) { + var writePromise = writeMessage(channelState.channelName, channelState.uuid, messageJson, channelState.paths); + channelState.writeBlockPromise = channelState.writeBlockPromise.then( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() { + var _yield$Promise$all, msgObj, pingStr, writeToReadersPromise; + + return _regeneratorRuntime.wrap(function _callee3$(_context3) { + while (1) { + switch (_context3.prev = _context3.next) { + case 0: + _context3.next = 2; + return new Promise(function (res) { + return setTimeout(res, 0); + }); + + case 2: + _context3.next = 4; + return Promise.all([writePromise, refreshReaderClients(channelState)]); + + case 4: + _yield$Promise$all = _context3.sent; + msgObj = _yield$Promise$all[0]; + emitOverFastPath(channelState, msgObj, messageJson); + pingStr = '{"t":' + msgObj.time + ',"u":"' + msgObj.uuid + '","to":"' + msgObj.token + '"}|'; + writeToReadersPromise = Promise.all(Object.values(channelState.otherReaderClients).filter(function (client) { + return client.writable; + }) // client might have closed in between + .map(function (client) { + return new Promise(function (res) { + client.write(pingStr, res); + }); + })); + /** + * clean up old messages + * to not waste resources on cleaning up, + * only if random-int matches, we clean up old messages + */ + + if (randomInt(0, 20) === 0) { + /* await */ + getAllMessages(channelState.channelName, channelState.paths).then(function (allMessages) { + return cleanOldMessages(allMessages, channelState.options.node.ttl); + }); + } + + return _context3.abrupt("return", writeToReadersPromise); + + case 11: + case "end": + return _context3.stop(); + } + } + }, _callee3); + }))); + return channelState.writeBlockPromise; +} +/** + * When multiple BroadcastChannels with the same name + * are created in a single node-process, we can access them directly and emit messages. + * This might not happen often in production + * but will speed up things when this module is used in unit-tests. + */ + +export function emitOverFastPath(state, msgObj, messageJson) { + if (!state.options.node.useFastPath) return; // disabled + + var others = OTHER_INSTANCES[state.channelName].filter(function (s) { + return s !== state; + }); + var checkObj = { + time: msgObj.time, + senderUuid: msgObj.uuid, + token: msgObj.token + }; + others.filter(function (otherState) { + return _filterMessage(checkObj, otherState); + }).forEach(function (otherState) { + otherState.messagesCallback(messageJson); + }); +} +export function onMessage(channelState, fn) { + var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : microSeconds(); + channelState.messagesCallbackTime = time; + channelState.messagesCallback = fn; + handleMessagePing(channelState); +} +/** + * closes the channel + * @return {Promise} + */ + +export function close(channelState) { + if (channelState.closed) return; + channelState.closed = true; + channelState.emittedMessagesIds.clear(); + OTHER_INSTANCES[channelState.channelName] = OTHER_INSTANCES[channelState.channelName].filter(function (o) { + return o !== channelState; + }); + + if (channelState.removeUnload) { + channelState.removeUnload.remove(); + } + + return new Promise(function (res) { + if (channelState.socketEE) channelState.socketEE.emitter.removeAllListeners(); + Object.values(channelState.otherReaderClients).forEach(function (client) { + return client.destroy(); + }); + + if (channelState.infoFilePath) { + try { + fs.unlinkSync(channelState.infoFilePath); + } catch (err) {} + } + /** + * the server get closed lazy because others might still write on it + * and have not found out that the infoFile was deleted + */ + + + setTimeout(function () { + channelState.socketEE.server.close(); + res(); + }, 200); + }); +} +export function canBeUsed() { + return isNode; +} +/** + * on node we use a relatively height averageResponseTime, + * because the file-io might be in use. + * Also it is more important that the leader-election is reliable, + * then to have a fast election. + */ + +export function averageResponseTime() { + return 200; +} +export function microSeconds() { + return parseInt(micro.microseconds()); +} \ No newline at end of file diff --git a/dist/esnode/methods/simulate.js b/dist/esnode/methods/simulate.js new file mode 100644 index 00000000..135256bc --- /dev/null +++ b/dist/esnode/methods/simulate.js @@ -0,0 +1,51 @@ +import { microSeconds as micro } from '../util'; +export var microSeconds = micro; +export var type = 'simulate'; +var SIMULATE_CHANNELS = new Set(); +export function create(channelName) { + var state = { + name: channelName, + messagesCallback: null + }; + SIMULATE_CHANNELS.add(state); + return state; +} +export function close(channelState) { + SIMULATE_CHANNELS["delete"](channelState); +} +export function postMessage(channelState, messageJson) { + return new Promise(function (res) { + return setTimeout(function () { + var channelArray = Array.from(SIMULATE_CHANNELS); + channelArray.filter(function (channel) { + return channel.name === channelState.name; + }).filter(function (channel) { + return channel !== channelState; + }).filter(function (channel) { + return !!channel.messagesCallback; + }).forEach(function (channel) { + return channel.messagesCallback(messageJson); + }); + res(); + }, 5); + }); +} +export function onMessage(channelState, fn) { + channelState.messagesCallback = fn; +} +export function canBeUsed() { + return true; +} +export function averageResponseTime() { + return 5; +} +export default { + create: create, + close: close, + onMessage: onMessage, + postMessage: postMessage, + canBeUsed: canBeUsed, + type: type, + averageResponseTime: averageResponseTime, + microSeconds: microSeconds +}; \ No newline at end of file diff --git a/dist/esnode/options.js b/dist/esnode/options.js new file mode 100644 index 00000000..d5354bee --- /dev/null +++ b/dist/esnode/options.js @@ -0,0 +1,24 @@ +export function fillOptionsWithDefaults() { + var originalOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + var options = JSON.parse(JSON.stringify(originalOptions)); // main + + if (typeof options.webWorkerSupport === 'undefined') options.webWorkerSupport = true; // indexed-db + + if (!options.idb) options.idb = {}; // after this time the messages get deleted + + if (!options.idb.ttl) options.idb.ttl = 1000 * 45; + if (!options.idb.fallbackInterval) options.idb.fallbackInterval = 150; // handles abrupt db onclose events. + + if (originalOptions.idb && typeof originalOptions.idb.onclose === 'function') options.idb.onclose = originalOptions.idb.onclose; // localstorage + + if (!options.localstorage) options.localstorage = {}; + if (!options.localstorage.removeTimeout) options.localstorage.removeTimeout = 1000 * 60; // custom methods + + if (originalOptions.methods) options.methods = originalOptions.methods; // node + + if (!options.node) options.node = {}; + if (!options.node.ttl) options.node.ttl = 1000 * 60 * 2; // 2 minutes; + + if (typeof options.node.useFastPath === 'undefined') options.node.useFastPath = true; + return options; +} \ No newline at end of file diff --git a/dist/esnode/util.js b/dist/esnode/util.js new file mode 100644 index 00000000..f8fc8363 --- /dev/null +++ b/dist/esnode/util.js @@ -0,0 +1,55 @@ +/** + * returns true if the given object is a promise + */ +export function isPromise(obj) { + if (obj && typeof obj.then === 'function') { + return true; + } else { + return false; + } +} +export function sleep(time) { + if (!time) time = 0; + return new Promise(function (res) { + return setTimeout(res, time); + }); +} +export function randomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} +/** + * https://stackoverflow.com/a/8084248 + */ + +export function randomToken() { + return Math.random().toString(36).substring(2); +} +var lastMs = 0; +var additional = 0; +/** + * returns the current time in micro-seconds, + * WARNING: This is a pseudo-function + * Performance.now is not reliable in webworkers, so we just make sure to never return the same time. + * This is enough in browsers, and this function will not be used in nodejs. + * The main reason for this hack is to ensure that BroadcastChannel behaves equal to production when it is used in fast-running unit tests. + */ + +export function microSeconds() { + var ms = new Date().getTime(); + + if (ms === lastMs) { + additional++; + return ms * 1000 + additional; + } else { + lastMs = ms; + additional = 0; + return ms * 1000; + } +} +/** + * copied from the 'detect-node' npm module + * We cannot use the module directly because it causes problems with rollup + * @link https://github.com/iliakan/detect-node/blob/master/index.js + */ + +export var isNode = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'; \ No newline at end of file diff --git a/dist/lib/browser.js b/dist/lib/browser.js index a05ddc71..46e18b70 100644 --- a/dist/lib/browser.js +++ b/dist/lib/browser.js @@ -623,7 +623,6 @@ function createLeaderElection(channel, options) { return elector; } },{"./util.js":12,"@babel/runtime/helpers/interopRequireDefault":13,"unload":20}],6:[function(require,module,exports){ -(function (process){(function (){ "use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); @@ -652,21 +651,13 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // the line below will be removed from es5/browser builds -// the non-transpiled code runs faster // order is important var METHODS = [_native["default"], // fastest _indexedDb["default"], _localstorage["default"]]; function chooseMethod(options) { - var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // process.browser check allows ES6 builds to be used on server or client. Bundlers like - // Browserify, Webpack, etc. define process.browser and can then dead code eliminate the unused - // import. However, we still use sed during build of es5/browser build to remove the import so - // that it's also removed from non-minified version - - if (!process.browser) { - // the line below will be removed from es5/browser builds + var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // the line below will be removed from es5/browser builds - } // directly chosen if (options.type) { @@ -699,8 +690,7 @@ function chooseMethod(options) { return m.type; })));else return useMethod; } -}).call(this)}).call(this,require('_process')) -},{"./methods/indexed-db.js":7,"./methods/localstorage.js":8,"./methods/native.js":9,"./methods/simulate.js":10,"./util":12,"@babel/runtime/helpers/interopRequireDefault":13,"@babel/runtime/helpers/typeof":14,"_process":18}],7:[function(require,module,exports){ +},{"./methods/indexed-db.js":7,"./methods/localstorage.js":8,"./methods/native.js":9,"./methods/simulate.js":10,"./util":12,"@babel/runtime/helpers/interopRequireDefault":13,"@babel/runtime/helpers/typeof":14}],7:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { diff --git a/dist/lib/browser.min.js b/dist/lib/browser.min.js index e7a30c31..b5068d1b 100644 --- a/dist/lib/browser.min.js +++ b/dist/lib/browser.min.js @@ -1 +1 @@ -!function r(o,i,s){function a(t,e){if(!i[t]){if(!o[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(u)return u(t,!0);throw(n=new Error("Cannot find module '"+t+"'")).code="MODULE_NOT_FOUND",n}n=i[t]={exports:{}},o[t][0].call(n.exports,function(e){return a(o[t][1][e]||e)},n,n.exports,r,o,i,s)}return i[t].exports}for(var u="function"==typeof require&&require,e=0;e=e.time&&e.fn(t.data)})},n=e.method.microSeconds(),e._prepP?e._prepP.then(function(){e._iL=!0,e.method.onMessage(e._state,t,n)}):(e._iL=!0,e.method.onMessage(e._state,t,n)))}}(e)}function l(e,t,n){e._addEL[t]=e._addEL[t].filter(function(e){return e!==n}),function(e){{var t;e._iL&&!u(e)&&(e._iL=!1,t=e.method.microSeconds(),e.method.onMessage(e._state,null,t))}}(e)}(n.BroadcastChannel=e)._pubkey=!0,e.prototype={postMessage:function(e){if(this.closed)throw new Error("BroadcastChannel.postMessage(): Cannot post message after channel has closed");return a(this,"message",e)},postInternal:function(e){return a(this,"internal",e)},set onmessage(e){var t={time:this.method.microSeconds(),fn:e};l(this,"message",this._onML),e&&"function"==typeof e?(this._onML=t,c(this,"message",t)):this._onML=null},addEventListener:function(e,t){var n=this.method.microSeconds();c(this,e,{time:n,fn:t})},removeEventListener:function(e,t){var n=this._addEL[e].find(function(e){return e.fn===t});l(this,e,n)},close:function(){var e=this;if(!this.closed){this.closed=!0;var t=this._prepP||Promise.resolve();return this._onML=null,this._addEL.message=[],t.then(function(){return Promise.all(Array.from(e._uMP))}).then(function(){return Promise.all(e._befC.map(function(e){return e()}))}).then(function(){return e.method.close(e._state)})}},get type(){return this.method.type},get isClosed(){return this.closed}}},{"./method-chooser.js":6,"./options.js":11,"./util.js":12}],2:[function(e,t,n){"use strict";var r=e("./index.es5.js"),e=r.BroadcastChannel,r=r.createLeaderElection;window.BroadcastChannel2=e,window.createLeaderElection=r},{"./index.es5.js":3}],3:[function(e,t,n){"use strict";e=e("./index.js");t.exports={BroadcastChannel:e.BroadcastChannel,createLeaderElection:e.createLeaderElection,clearNodeFolder:e.clearNodeFolder,enforceOptions:e.enforceOptions,beLeader:e.beLeader}},{"./index.js":4}],4:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"BroadcastChannel",{enumerable:!0,get:function(){return r.BroadcastChannel}}),Object.defineProperty(n,"clearNodeFolder",{enumerable:!0,get:function(){return r.clearNodeFolder}}),Object.defineProperty(n,"enforceOptions",{enumerable:!0,get:function(){return r.enforceOptions}}),Object.defineProperty(n,"createLeaderElection",{enumerable:!0,get:function(){return o.createLeaderElection}}),Object.defineProperty(n,"beLeader",{enumerable:!0,get:function(){return o.beLeader}});var r=e("./broadcast-channel"),o=e("./leader-election")},{"./broadcast-channel":1,"./leader-election":5}],5:[function(e,t,n){"use strict";var r=e("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(n,"__esModule",{value:!0}),n.beLeader=u,n.createLeaderElection=function(e,t){if(e._leaderElector)throw new Error("BroadcastChannel already has a leader-elector");t=function(e,t){e=e||{};(e=JSON.parse(JSON.stringify(e))).fallbackInterval||(e.fallbackInterval=3e3);e.responseTime||(e.responseTime=t.method.averageResponseTime(t.options));return e}(t,e);var n=new s(e,t);return e._befC.push(function(){return n.die()}),e._leaderElector=n};var i=e("./util.js"),o=r(e("unload")),s=function(e,t){this._channel=e,this._options=t,this.isLeader=!1,this.isDead=!1,this.token=(0,i.randomToken)(),this._isApl=!1,this._reApply=!1,this._unl=[],this._lstns=[],this._invs=[],this._dpL=function(){},this._dpLC=!1};function a(e,t){t={context:"leader",action:t,token:e.token};return e._channel.postInternal(t)}function u(t){t.isLeader=!0;var e=o.default.add(function(){return t.die()});t._unl.push(e);e=function(e){"leader"===e.context&&"apply"===e.action&&a(t,"tell"),"leader"!==e.context||"tell"!==e.action||t._dpLC||(t._dpLC=!0,t._dpL(),a(t,"tell"))};return t._channel.addEventListener("internal",e),t._lstns.push(e),a(t,"tell")}s.prototype={applyOnce:function(){var t=this;if(this.isLeader)return Promise.resolve(!1);if(this.isDead)return Promise.resolve(!1);if(this._isApl)return this._reApply=!0,Promise.resolve(!1);function n(e){"leader"===e.context&&e.token!=t.token&&(o.push(e),"apply"===e.action&&e.token>t.token&&(r=!0),"tell"===e.action&&(r=!0))}var r=!(this._isApl=!0),o=[];return this._channel.addEventListener("internal",n),a(this,"apply").then(function(){return(0,i.sleep)(t._options.responseTime)}).then(function(){return r?Promise.reject(new Error):a(t,"apply")}).then(function(){return(0,i.sleep)(t._options.responseTime)}).then(function(){return r?Promise.reject(new Error):a(t)}).then(function(){return u(t)}).then(function(){return!0}).catch(function(){return!1}).then(function(e){return t._channel.removeEventListener("internal",n),t._isApl=!1,!e&&t._reApply?(t._reApply=!1,t.applyOnce()):e})},awaitLeadership:function(){var i;return this._aLP||(this._aLP=(i=this).isLeader?Promise.resolve():new Promise(function(e){var t=!1;function n(){t||(t=!0,clearInterval(r),i._channel.removeEventListener("internal",o),e(!0))}i.applyOnce().then(function(){i.isLeader&&n()});var r=setInterval(function(){i.applyOnce().then(function(){i.isLeader&&n()})},i._options.fallbackInterval);i._invs.push(r);var o=function(e){"leader"===e.context&&"death"===e.action&&i.applyOnce().then(function(){i.isLeader&&n()})};i._channel.addEventListener("internal",o),i._lstns.push(o)})),this._aLP},set onduplicate(e){this._dpL=e},die:function(){var t=this;if(!this.isDead)return this.isDead=!0,this._lstns.forEach(function(e){return t._channel.removeEventListener("internal",e)}),this._invs.forEach(function(e){return clearInterval(e)}),this._unl.forEach(function(e){e.remove()}),a(this,"death")}}},{"./util.js":12,"@babel/runtime/helpers/interopRequireDefault":13,unload:20}],6:[function(u,e,c){!function(a){!function(){"use strict";var e=u("@babel/runtime/helpers/interopRequireDefault");u("@babel/runtime/helpers/typeof");Object.defineProperty(c,"__esModule",{value:!0}),c.chooseMethod=function(t){var e=[].concat(t.methods,s).filter(Boolean);a.browser;if(t.type){if("simulate"===t.type)return o.default;var n=e.find(function(e){return e.type===t.type});if(n)return n;throw new Error("method-type "+t.type+" not found")}t.webWorkerSupport||i.isNode||(e=e.filter(function(e){return"idb"!==e.type}));e=e.find(function(e){return e.canBeUsed()});{if(e)return e;throw new Error("No useable method found in "+JSON.stringify(s.map(function(e){return e.type})))}};var t=e(u("./methods/native.js")),n=e(u("./methods/indexed-db.js")),r=e(u("./methods/localstorage.js")),o=e(u("./methods/simulate.js")),i=u("./util");var s=[t.default,n.default,r.default]}.call(this)}.call(this,u("_process"))},{"./methods/indexed-db.js":7,"./methods/localstorage.js":8,"./methods/native.js":9,"./methods/simulate.js":10,"./util":12,"@babel/runtime/helpers/interopRequireDefault":13,"@babel/runtime/helpers/typeof":14,_process:18}],7:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.getIdb=u,n.createDatabase=c,n.writeMessage=l,n.getAllMessages=function(e){var n=e.transaction(a).objectStore(a),r=[];return new Promise(function(t){n.openCursor().onsuccess=function(e){e=e.target.result;e?(r.push(e.value),e.continue()):t(r)}})},n.getMessagesHigherThan=d,n.removeMessageById=f,n.getOldMessages=p,n.cleanOldMessages=h,n.create=m,n.close=b,n.postMessage=g,n.onMessage=y,n.canBeUsed=_,n.averageResponseTime=w,n.default=n.type=n.microSeconds=void 0;var o=e("../util.js"),i=e("oblivious-set"),s=e("../options"),e=o.microSeconds;n.microSeconds=e;var r="pubkey.broadcast-channel-0-",a="messages";function u(){if("undefined"!=typeof indexedDB)return indexedDB;if("undefined"!=typeof window){if(void 0!==window.mozIndexedDB)return window.mozIndexedDB;if(void 0!==window.webkitIndexedDB)return window.webkitIndexedDB;if(void 0!==window.msIndexedDB)return window.msIndexedDB}return!1}function c(e){var n=u().open(r+e,1);return n.onupgradeneeded=function(e){e.target.result.createObjectStore(a,{keyPath:"id",autoIncrement:!0})},new Promise(function(e,t){n.onerror=function(e){return t(e)},n.onsuccess=function(){e(n.result)}})}function l(e,t,n){var r={uuid:t,time:(new Date).getTime(),data:n},o=e.transaction([a],"readwrite");return new Promise(function(e,t){o.oncomplete=function(){return e()},o.onerror=function(e){return t(e)},o.objectStore(a).add(r)})}function d(e,n){var r=e.transaction(a).objectStore(a),o=[];return new Promise(function(t){(function(){try{var e=IDBKeyRange.bound(n+1,1/0);return r.openCursor(e)}catch(e){return r.openCursor()}})().onsuccess=function(e){e=e.target.result;e?e.value.idn.lastCursorId&&(n.lastCursorId=e.id),e}).filter(function(e){return t=n,(e=e).uuid!==t.uuid&&(!t.eMIs.has(e.id)&&!(e.data.time=e.time&&e.fn(t.data)})},n=e.method.microSeconds(),e._prepP?e._prepP.then(function(){e._iL=!0,e.method.onMessage(e._state,t,n)}):(e._iL=!0,e.method.onMessage(e._state,t,n)))}}(e)}function l(e,t,n){e._addEL[t]=e._addEL[t].filter(function(e){return e!==n}),function(e){{var t;e._iL&&!u(e)&&(e._iL=!1,t=e.method.microSeconds(),e.method.onMessage(e._state,null,t))}}(e)}(n.BroadcastChannel=e)._pubkey=!0,e.prototype={postMessage:function(e){if(this.closed)throw new Error("BroadcastChannel.postMessage(): Cannot post message after channel has closed");return a(this,"message",e)},postInternal:function(e){return a(this,"internal",e)},set onmessage(e){var t={time:this.method.microSeconds(),fn:e};l(this,"message",this._onML),e&&"function"==typeof e?(this._onML=t,c(this,"message",t)):this._onML=null},addEventListener:function(e,t){var n=this.method.microSeconds();c(this,e,{time:n,fn:t})},removeEventListener:function(e,t){var n=this._addEL[e].find(function(e){return e.fn===t});l(this,e,n)},close:function(){var e=this;if(!this.closed){this.closed=!0;var t=this._prepP||Promise.resolve();return this._onML=null,this._addEL.message=[],t.then(function(){return Promise.all(Array.from(e._uMP))}).then(function(){return Promise.all(e._befC.map(function(e){return e()}))}).then(function(){return e.method.close(e._state)})}},get type(){return this.method.type},get isClosed(){return this.closed}}},{"./method-chooser.js":6,"./options.js":11,"./util.js":12}],2:[function(e,t,n){"use strict";var r=e("./index.es5.js"),e=r.BroadcastChannel,r=r.createLeaderElection;window.BroadcastChannel2=e,window.createLeaderElection=r},{"./index.es5.js":3}],3:[function(e,t,n){"use strict";e=e("./index.js");t.exports={BroadcastChannel:e.BroadcastChannel,createLeaderElection:e.createLeaderElection,clearNodeFolder:e.clearNodeFolder,enforceOptions:e.enforceOptions,beLeader:e.beLeader}},{"./index.js":4}],4:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"BroadcastChannel",{enumerable:!0,get:function(){return r.BroadcastChannel}}),Object.defineProperty(n,"clearNodeFolder",{enumerable:!0,get:function(){return r.clearNodeFolder}}),Object.defineProperty(n,"enforceOptions",{enumerable:!0,get:function(){return r.enforceOptions}}),Object.defineProperty(n,"createLeaderElection",{enumerable:!0,get:function(){return o.createLeaderElection}}),Object.defineProperty(n,"beLeader",{enumerable:!0,get:function(){return o.beLeader}});var r=e("./broadcast-channel"),o=e("./leader-election")},{"./broadcast-channel":1,"./leader-election":5}],5:[function(e,t,n){"use strict";var r=e("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(n,"__esModule",{value:!0}),n.beLeader=u,n.createLeaderElection=function(e,t){if(e._leaderElector)throw new Error("BroadcastChannel already has a leader-elector");t=function(e,t){e=e||{};(e=JSON.parse(JSON.stringify(e))).fallbackInterval||(e.fallbackInterval=3e3);e.responseTime||(e.responseTime=t.method.averageResponseTime(t.options));return e}(t,e);var n=new s(e,t);return e._befC.push(function(){return n.die()}),e._leaderElector=n};var i=e("./util.js"),o=r(e("unload")),s=function(e,t){this._channel=e,this._options=t,this.isLeader=!1,this.isDead=!1,this.token=(0,i.randomToken)(),this._isApl=!1,this._reApply=!1,this._unl=[],this._lstns=[],this._invs=[],this._dpL=function(){},this._dpLC=!1};function a(e,t){t={context:"leader",action:t,token:e.token};return e._channel.postInternal(t)}function u(t){t.isLeader=!0;var e=o.default.add(function(){return t.die()});t._unl.push(e);e=function(e){"leader"===e.context&&"apply"===e.action&&a(t,"tell"),"leader"!==e.context||"tell"!==e.action||t._dpLC||(t._dpLC=!0,t._dpL(),a(t,"tell"))};return t._channel.addEventListener("internal",e),t._lstns.push(e),a(t,"tell")}s.prototype={applyOnce:function(){var t=this;if(this.isLeader)return Promise.resolve(!1);if(this.isDead)return Promise.resolve(!1);if(this._isApl)return this._reApply=!0,Promise.resolve(!1);function n(e){"leader"===e.context&&e.token!=t.token&&(o.push(e),"apply"===e.action&&e.token>t.token&&(r=!0),"tell"===e.action&&(r=!0))}var r=!(this._isApl=!0),o=[];return this._channel.addEventListener("internal",n),a(this,"apply").then(function(){return(0,i.sleep)(t._options.responseTime)}).then(function(){return r?Promise.reject(new Error):a(t,"apply")}).then(function(){return(0,i.sleep)(t._options.responseTime)}).then(function(){return r?Promise.reject(new Error):a(t)}).then(function(){return u(t)}).then(function(){return!0}).catch(function(){return!1}).then(function(e){return t._channel.removeEventListener("internal",n),t._isApl=!1,!e&&t._reApply?(t._reApply=!1,t.applyOnce()):e})},awaitLeadership:function(){var i;return this._aLP||(this._aLP=(i=this).isLeader?Promise.resolve():new Promise(function(e){var t=!1;function n(){t||(t=!0,clearInterval(r),i._channel.removeEventListener("internal",o),e(!0))}i.applyOnce().then(function(){i.isLeader&&n()});var r=setInterval(function(){i.applyOnce().then(function(){i.isLeader&&n()})},i._options.fallbackInterval);i._invs.push(r);var o=function(e){"leader"===e.context&&"death"===e.action&&i.applyOnce().then(function(){i.isLeader&&n()})};i._channel.addEventListener("internal",o),i._lstns.push(o)})),this._aLP},set onduplicate(e){this._dpL=e},die:function(){var t=this;if(!this.isDead)return this.isDead=!0,this._lstns.forEach(function(e){return t._channel.removeEventListener("internal",e)}),this._invs.forEach(function(e){return clearInterval(e)}),this._unl.forEach(function(e){e.remove()}),a(this,"death")}}},{"./util.js":12,"@babel/runtime/helpers/interopRequireDefault":13,unload:20}],6:[function(e,t,n){"use strict";var r=e("@babel/runtime/helpers/interopRequireDefault");e("@babel/runtime/helpers/typeof");Object.defineProperty(n,"__esModule",{value:!0}),n.chooseMethod=function(t){var e=[].concat(t.methods,u).filter(Boolean);if(t.type){if("simulate"===t.type)return s.default;var n=e.find(function(e){return e.type===t.type});if(n)return n;throw new Error("method-type "+t.type+" not found")}t.webWorkerSupport||a.isNode||(e=e.filter(function(e){return"idb"!==e.type}));e=e.find(function(e){return e.canBeUsed()});{if(e)return e;throw new Error("No useable method found in "+JSON.stringify(u.map(function(e){return e.type})))}};var o=r(e("./methods/native.js")),i=r(e("./methods/indexed-db.js")),n=r(e("./methods/localstorage.js")),s=r(e("./methods/simulate.js")),a=e("./util");var u=[o.default,i.default,n.default]},{"./methods/indexed-db.js":7,"./methods/localstorage.js":8,"./methods/native.js":9,"./methods/simulate.js":10,"./util":12,"@babel/runtime/helpers/interopRequireDefault":13,"@babel/runtime/helpers/typeof":14}],7:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.getIdb=u,n.createDatabase=c,n.writeMessage=l,n.getAllMessages=function(e){var n=e.transaction(a).objectStore(a),r=[];return new Promise(function(t){n.openCursor().onsuccess=function(e){e=e.target.result;e?(r.push(e.value),e.continue()):t(r)}})},n.getMessagesHigherThan=d,n.removeMessageById=f,n.getOldMessages=p,n.cleanOldMessages=h,n.create=m,n.close=b,n.postMessage=g,n.onMessage=y,n.canBeUsed=_,n.averageResponseTime=w,n.default=n.type=n.microSeconds=void 0;var o=e("../util.js"),i=e("oblivious-set"),s=e("../options"),e=o.microSeconds;n.microSeconds=e;var r="pubkey.broadcast-channel-0-",a="messages";function u(){if("undefined"!=typeof indexedDB)return indexedDB;if("undefined"!=typeof window){if(void 0!==window.mozIndexedDB)return window.mozIndexedDB;if(void 0!==window.webkitIndexedDB)return window.webkitIndexedDB;if(void 0!==window.msIndexedDB)return window.msIndexedDB}return!1}function c(e){var n=u().open(r+e,1);return n.onupgradeneeded=function(e){e.target.result.createObjectStore(a,{keyPath:"id",autoIncrement:!0})},new Promise(function(e,t){n.onerror=function(e){return t(e)},n.onsuccess=function(){e(n.result)}})}function l(e,t,n){var r={uuid:t,time:(new Date).getTime(),data:n},o=e.transaction([a],"readwrite");return new Promise(function(e,t){o.oncomplete=function(){return e()},o.onerror=function(e){return t(e)},o.objectStore(a).add(r)})}function d(e,n){var r=e.transaction(a).objectStore(a),o=[];return new Promise(function(t){(function(){try{var e=IDBKeyRange.bound(n+1,1/0);return r.openCursor(e)}catch(e){return r.openCursor()}})().onsuccess=function(e){e=e.target.result;e?e.value.idn.lastCursorId&&(n.lastCursorId=e.id),e}).filter(function(e){return t=n,(e=e).uuid!==t.uuid&&(!t.eMIs.has(e.id)&&!(e.data.time} */ +exports.type = type; + function create(_x19) { return _create.apply(this, arguments); } @@ -669,11 +720,11 @@ function _create() { switch (_context17.prev = _context17.next) { case 0: options = _args17.length > 1 && _args17[1] !== undefined ? _args17[1] : {}; - options = fillOptionsWithDefaults(options); + options = (0, _options.fillOptionsWithDefaults)(options); time = microSeconds(); paths = getPaths(channelName); ensureFolderExistsPromise = ensureFoldersExist(channelName, paths); - uuid = randomToken(); + uuid = (0, _util2.randomToken)(); state = { time: time, channelName: channelName, @@ -681,14 +732,14 @@ function _create() { uuid: uuid, paths: paths, // contains all messages that have been emitted before - emittedMessagesIds: new ObliviousSet(options.node.ttl * 2), + emittedMessagesIds: new _obliviousSet.ObliviousSet(options.node.ttl * 2), messagesCallbackTime: null, messagesCallback: null, // ensures we do not read messages in parrallel writeBlockPromise: Promise.resolve(), otherReaderClients: {}, // ensure if process crashes, everything is cleaned up - removeUnload: unload.add(function () { + removeUnload: _unload["default"].add(function () { return close(state); }), closed: false @@ -981,7 +1032,7 @@ function postMessage(channelState, messageJson) { * only if random-int matches, we clean up old messages */ - if (randomInt(0, 20) === 0) { + if ((0, _util2.randomInt)(0, 20) === 0) { /* await */ getAllMessages(channelState.channelName, channelState.paths).then(function (allMessages) { return cleanOldMessages(allMessages, channelState.options.node.ttl); @@ -1057,7 +1108,7 @@ function close(channelState) { if (channelState.infoFilePath) { try { - fs.unlinkSync(channelState.infoFilePath); + _fs["default"].unlinkSync(channelState.infoFilePath); } catch (err) {} } /** @@ -1074,7 +1125,7 @@ function close(channelState) { } function canBeUsed() { - return isNode; + return _detectNode["default"]; } /** * on node we use a relatively height averageResponseTime, @@ -1089,38 +1140,5 @@ function averageResponseTime() { } function microSeconds() { - return parseInt(micro.microseconds()); -} - -module.exports = { - TMP_FOLDER_BASE: TMP_FOLDER_BASE, - cleanPipeName: cleanPipeName, - getPaths: getPaths, - ensureFoldersExist: ensureFoldersExist, - clearNodeFolder: clearNodeFolder, - socketPath: socketPath, - socketInfoPath: socketInfoPath, - createSocketInfoFile: createSocketInfoFile, - countChannelFolders: countChannelFolders, - createSocketEventEmitter: createSocketEventEmitter, - openClientConnection: openClientConnection, - writeMessage: writeMessage, - getReadersUuids: getReadersUuids, - messagePath: messagePath, - getAllMessages: getAllMessages, - getSingleMessage: getSingleMessage, - readMessage: readMessage, - cleanOldMessages: cleanOldMessages, - type: type, - create: create, - _filterMessage: _filterMessage, - handleMessagePing: handleMessagePing, - refreshReaderClients: refreshReaderClients, - postMessage: postMessage, - emitOverFastPath: emitOverFastPath, - onMessage: onMessage, - close: close, - canBeUsed: canBeUsed, - averageResponseTime: averageResponseTime, - microSeconds: microSeconds -}; \ No newline at end of file + return parseInt(_nanoTime["default"].microseconds()); +} \ No newline at end of file diff --git a/docs/e2e.js b/docs/e2e.js index 13a7b360..659bdf6d 100644 --- a/docs/e2e.js +++ b/docs/e2e.js @@ -614,7 +614,6 @@ function createLeaderElection(channel, options) { return elector; } },{"./util.js":11,"@babel/runtime/helpers/interopRequireDefault":15,"unload":328}],5:[function(require,module,exports){ -(function (process){(function (){ "use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); @@ -643,21 +642,13 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // the line below will be removed from es5/browser builds -// the non-transpiled code runs faster // order is important var METHODS = [_native["default"], // fastest _indexedDb["default"], _localstorage["default"]]; function chooseMethod(options) { - var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // process.browser check allows ES6 builds to be used on server or client. Bundlers like - // Browserify, Webpack, etc. define process.browser and can then dead code eliminate the unused - // import. However, we still use sed during build of es5/browser build to remove the import so - // that it's also removed from non-minified version - - if (!process.browser) { - // the line below will be removed from es5/browser builds + var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // the line below will be removed from es5/browser builds - } // directly chosen if (options.type) { @@ -690,8 +681,7 @@ function chooseMethod(options) { return m.type; })));else return useMethod; } -}).call(this)}).call(this,require('_process')) -},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":15,"@babel/runtime/helpers/typeof":16,"_process":325}],6:[function(require,module,exports){ +},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":15,"@babel/runtime/helpers/typeof":16}],6:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { diff --git a/docs/iframe.js b/docs/iframe.js index d7dc2d7a..5b28b68c 100644 --- a/docs/iframe.js +++ b/docs/iframe.js @@ -614,7 +614,6 @@ function createLeaderElection(channel, options) { return elector; } },{"./util.js":11,"@babel/runtime/helpers/interopRequireDefault":14,"unload":326}],5:[function(require,module,exports){ -(function (process){(function (){ "use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); @@ -643,21 +642,13 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // the line below will be removed from es5/browser builds -// the non-transpiled code runs faster // order is important var METHODS = [_native["default"], // fastest _indexedDb["default"], _localstorage["default"]]; function chooseMethod(options) { - var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // process.browser check allows ES6 builds to be used on server or client. Bundlers like - // Browserify, Webpack, etc. define process.browser and can then dead code eliminate the unused - // import. However, we still use sed during build of es5/browser build to remove the import so - // that it's also removed from non-minified version - - if (!process.browser) { - // the line below will be removed from es5/browser builds + var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // the line below will be removed from es5/browser builds - } // directly chosen if (options.type) { @@ -690,8 +681,7 @@ function chooseMethod(options) { return m.type; })));else return useMethod; } -}).call(this)}).call(this,require('_process')) -},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":14,"@babel/runtime/helpers/typeof":15,"_process":323}],6:[function(require,module,exports){ +},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":14,"@babel/runtime/helpers/typeof":15}],6:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { diff --git a/docs/index.js b/docs/index.js index 34c0940f..11a78067 100644 --- a/docs/index.js +++ b/docs/index.js @@ -614,7 +614,6 @@ function createLeaderElection(channel, options) { return elector; } },{"./util.js":11,"@babel/runtime/helpers/interopRequireDefault":14,"unload":326}],5:[function(require,module,exports){ -(function (process){(function (){ "use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); @@ -643,21 +642,13 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // the line below will be removed from es5/browser builds -// the non-transpiled code runs faster // order is important var METHODS = [_native["default"], // fastest _indexedDb["default"], _localstorage["default"]]; function chooseMethod(options) { - var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // process.browser check allows ES6 builds to be used on server or client. Bundlers like - // Browserify, Webpack, etc. define process.browser and can then dead code eliminate the unused - // import. However, we still use sed during build of es5/browser build to remove the import so - // that it's also removed from non-minified version - - if (!process.browser) { - // the line below will be removed from es5/browser builds + var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // the line below will be removed from es5/browser builds - } // directly chosen if (options.type) { @@ -690,8 +681,7 @@ function chooseMethod(options) { return m.type; })));else return useMethod; } -}).call(this)}).call(this,require('_process')) -},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":14,"@babel/runtime/helpers/typeof":15,"_process":323}],6:[function(require,module,exports){ +},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":14,"@babel/runtime/helpers/typeof":15}],6:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { diff --git a/docs/leader-iframe.js b/docs/leader-iframe.js index f73b59ee..86025d43 100644 --- a/docs/leader-iframe.js +++ b/docs/leader-iframe.js @@ -614,7 +614,6 @@ function createLeaderElection(channel, options) { return elector; } },{"./util.js":11,"@babel/runtime/helpers/interopRequireDefault":14,"unload":326}],5:[function(require,module,exports){ -(function (process){(function (){ "use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); @@ -643,21 +642,13 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // the line below will be removed from es5/browser builds -// the non-transpiled code runs faster // order is important var METHODS = [_native["default"], // fastest _indexedDb["default"], _localstorage["default"]]; function chooseMethod(options) { - var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // process.browser check allows ES6 builds to be used on server or client. Bundlers like - // Browserify, Webpack, etc. define process.browser and can then dead code eliminate the unused - // import. However, we still use sed during build of es5/browser build to remove the import so - // that it's also removed from non-minified version - - if (!process.browser) { - // the line below will be removed from es5/browser builds + var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // the line below will be removed from es5/browser builds - } // directly chosen if (options.type) { @@ -690,8 +681,7 @@ function chooseMethod(options) { return m.type; })));else return useMethod; } -}).call(this)}).call(this,require('_process')) -},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":14,"@babel/runtime/helpers/typeof":15,"_process":323}],6:[function(require,module,exports){ +},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":14,"@babel/runtime/helpers/typeof":15}],6:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { diff --git a/docs/worker.js b/docs/worker.js index 4e1f437f..55ce0128 100644 --- a/docs/worker.js +++ b/docs/worker.js @@ -614,7 +614,6 @@ function createLeaderElection(channel, options) { return elector; } },{"./util.js":11,"@babel/runtime/helpers/interopRequireDefault":14,"unload":326}],5:[function(require,module,exports){ -(function (process){(function (){ "use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); @@ -643,21 +642,13 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } // the line below will be removed from es5/browser builds -// the non-transpiled code runs faster // order is important var METHODS = [_native["default"], // fastest _indexedDb["default"], _localstorage["default"]]; function chooseMethod(options) { - var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // process.browser check allows ES6 builds to be used on server or client. Bundlers like - // Browserify, Webpack, etc. define process.browser and can then dead code eliminate the unused - // import. However, we still use sed during build of es5/browser build to remove the import so - // that it's also removed from non-minified version - - if (!process.browser) { - // the line below will be removed from es5/browser builds + var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean); // the line below will be removed from es5/browser builds - } // directly chosen if (options.type) { @@ -690,8 +681,7 @@ function chooseMethod(options) { return m.type; })));else return useMethod; } -}).call(this)}).call(this,require('_process')) -},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":14,"@babel/runtime/helpers/typeof":15,"_process":323}],6:[function(require,module,exports){ +},{"./methods/indexed-db.js":6,"./methods/localstorage.js":7,"./methods/native.js":8,"./methods/simulate.js":9,"./util":11,"@babel/runtime/helpers/interopRequireDefault":14,"@babel/runtime/helpers/typeof":15}],6:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { diff --git a/package.json b/package.json index 0bd32bf1..e82f1ec4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "broadcast-channel", - "version": "4.0.0", + "version": "4.1.0", "description": "A BroadcastChannel that works in New Browsers, Old Browsers, WebWorkers and NodeJs", "exports": { "node": {