@next-core/brick-kit 2.188.3 → 2.189.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -177,6 +177,8 @@
177
177
  K["OTHER_ERROR"] = "OTHER_ERROR";
178
178
  K["GO_BACK_PREVIOUS_PAGE"] = "GO_BACK_PREVIOUS_PAGE";
179
179
  K["GO_BACK_HOME_PAGE"] = "GO_BACK_HOME_PAGE";
180
+ K["LOGIN_CHANGED"] = "LOGIN_CHANGED";
181
+ K["LOGOUT_APPLIED"] = "LOGOUT_APPLIED";
180
182
  })(K || (K = {}));
181
183
 
182
184
  /**
@@ -706,12 +708,7 @@
706
708
  suffix = getBasePath() + suffix;
707
709
  }
708
710
  if (window.APP_ID && window.APP_ID !== appId) {
709
- return suffix.replace(new RegExp("/(".concat(window.APP_ID, "|\\d+.\\d+.\\d+)/"), "g"), (_, p1) => {
710
- if (p1 === window.APP_ID) {
711
- return "/".concat(appId, "/");
712
- }
713
- return "/".concat(version, "/");
714
- });
711
+ return suffix.replace(new RegExp("(^|/)".concat(_.escapeRegExp(window.APP_ID), "/")), "$1".concat(appId, "/")).replace(/\/\d+\.\d+\.\d+\//, "/".concat(version, "/"));
715
712
  }
716
713
  return suffix;
717
714
  };
@@ -1211,7 +1208,9 @@
1211
1208
  [K.NO_PERMISSION]: "Unauthorized access, unable to retrieve the required resources for this page",
1212
1209
  [K.OTHER_ERROR]: "Oops! Something went wrong",
1213
1210
  [K.GO_BACK_PREVIOUS_PAGE]: "Go back to previous page",
1214
- [K.GO_BACK_HOME_PAGE]: "Back to home page"
1211
+ [K.GO_BACK_HOME_PAGE]: "Back to home page",
1212
+ [K.LOGIN_CHANGED]: "You have logged in as another account, click OK to refresh the page.",
1213
+ [K.LOGOUT_APPLIED]: "Your account has been logged out, click OK to refresh the page."
1215
1214
  };
1216
1215
  var en = locale$7;
1217
1216
 
@@ -1228,7 +1227,9 @@
1228
1227
  [K.NO_PERMISSION]: "没有权限,无法获取页面所需要的资源",
1229
1228
  [K.OTHER_ERROR]: "糟糕!页面出现了一些问题",
1230
1229
  [K.GO_BACK_PREVIOUS_PAGE]: "回到上一页",
1231
- [K.GO_BACK_HOME_PAGE]: "回到首页"
1230
+ [K.GO_BACK_HOME_PAGE]: "回到首页",
1231
+ [K.LOGIN_CHANGED]: "您已经登录另一个账号,点击确定刷新页面。",
1232
+ [K.LOGOUT_APPLIED]: "您的账号已经登出,点击确定刷新页面。"
1232
1233
  };
1233
1234
  var zh = locale$6;
1234
1235
 
@@ -1631,9 +1632,9 @@
1631
1632
  }
1632
1633
 
1633
1634
  /*! (c) Andrea Giammarchi - ISC */
1634
- var self = {};
1635
+ var self$1 = {};
1635
1636
  try {
1636
- self.EventTarget = new EventTarget().constructor;
1637
+ self$1.EventTarget = new EventTarget().constructor;
1637
1638
  } catch (EventTarget) {
1638
1639
  (function (Object, wm) {
1639
1640
  var create = Object.create;
@@ -1670,7 +1671,7 @@
1670
1671
  }
1671
1672
  }
1672
1673
  });
1673
- self.EventTarget = EventTarget;
1674
+ self$1.EventTarget = EventTarget;
1674
1675
  function EventTarget() {
1675
1676
 
1676
1677
  wm.set(this, create(null));
@@ -1690,7 +1691,7 @@
1690
1691
  }
1691
1692
  })(Object, new WeakMap());
1692
1693
  }
1693
- var EventTarget$1 = self.EventTarget;
1694
+ var EventTarget$1 = self$1.EventTarget;
1694
1695
 
1695
1696
  /** @internal */
1696
1697
  function looseCheckIf$1(ifContainer, context) {
@@ -8791,6 +8792,63 @@
8791
8792
  return misc;
8792
8793
  }
8793
8794
 
8795
+ var REFRESH_CHANNEL = "brick-next:authentication-change";
8796
+ var warnedToRefresh = false;
8797
+ function refreshPageIfAuthenticationChanged(_x) {
8798
+ return _refreshPageIfAuthenticationChanged.apply(this, arguments);
8799
+ }
8800
+ function _refreshPageIfAuthenticationChanged() {
8801
+ _refreshPageIfAuthenticationChanged = _asyncToGenerator__default["default"](function* (auth) {
8802
+ if (window.BroadcastChannel) {
8803
+ var channel = new BroadcastChannel(REFRESH_CHANNEL);
8804
+ channel.postMessage({
8805
+ type: "check-login",
8806
+ org: auth.org,
8807
+ username: auth.username
8808
+ });
8809
+ channel.addEventListener("message", e => {
8810
+ handleAuthenticationChange(e.data, auth);
8811
+ });
8812
+ } else {
8813
+ var PolyfillChannel = (yield Promise.resolve().then(function () { return index; })).BroadcastChannel;
8814
+ var _channel = new PolyfillChannel(REFRESH_CHANNEL);
8815
+ _channel.postMessage({
8816
+ type: "check-login",
8817
+ org: auth.org,
8818
+ username: auth.username
8819
+ });
8820
+ // The npm package `broadcast-channel` dispatch message as raw data instead of a MessageEvent.
8821
+ _channel.addEventListener("message", data => {
8822
+ handleAuthenticationChange(data, auth);
8823
+ });
8824
+ }
8825
+ });
8826
+ return _refreshPageIfAuthenticationChanged.apply(this, arguments);
8827
+ }
8828
+ function handleAuthenticationChange(_x2, _x3) {
8829
+ return _handleAuthenticationChange.apply(this, arguments);
8830
+ }
8831
+ function _handleAuthenticationChange() {
8832
+ _handleAuthenticationChange = _asyncToGenerator__default["default"](function* (payload, auth) {
8833
+ if (!warnedToRefresh && (payload.type === "logout" || payload.type === "check-login" && (payload.org !== auth.org || payload.username !== auth.username)) && getRuntime().getFeatureFlags()["refresh-page-if-authentication-changed"]) {
8834
+ // Only warn the user once.
8835
+ warnedToRefresh = true;
8836
+ // Open a modal to ask the user to refresh the page.
8837
+ // window.alert(i18next.t(`${NS_BRICK_KIT}:${payload.type === "logout" ? K.LOGOUT_APPLIED : K.LOGIN_CHANGED}`));
8838
+ antd.Modal.warning({
8839
+ icon: /*#__PURE__*/React__default["default"].createElement(icons.ExclamationCircleOutlined, null),
8840
+ content: i18next__default["default"].t("".concat(NS_BRICK_KIT, ":").concat(payload.type === "logout" ? K.LOGOUT_APPLIED : K.LOGIN_CHANGED)),
8841
+ okText: i18next__default["default"].t("".concat(NS_BRICK_KIT, ":").concat(K.MODAL_OK)),
8842
+ keyboard: false,
8843
+ onOk: () => {
8844
+ location.reload();
8845
+ }
8846
+ });
8847
+ }
8848
+ });
8849
+ return _handleAuthenticationChange.apply(this, arguments);
8850
+ }
8851
+
8794
8852
  var _excluded$3 = ["params"];
8795
8853
  var V3WidgetMates = ["basic.v3-widget-mate"];
8796
8854
  class Kernel {
@@ -8997,6 +9055,7 @@
8997
9055
  var auth = yield checkLogin();
8998
9056
  if (auth.loggedIn) {
8999
9057
  authenticate(auth);
9058
+ refreshPageIfAuthenticationChanged(auth);
9000
9059
  }
9001
9060
  }
9002
9061
  })();
@@ -15013,6 +15072,1600 @@
15013
15072
  });
15014
15073
  }
15015
15074
 
15075
+ /**
15076
+ * returns true if the given object is a promise
15077
+ */
15078
+ function isPromise(obj) {
15079
+ return obj && typeof obj.then === 'function';
15080
+ }
15081
+ var PROMISE_RESOLVED_FALSE = Promise.resolve(false);
15082
+ var PROMISE_RESOLVED_TRUE = Promise.resolve(true);
15083
+ var PROMISE_RESOLVED_VOID = Promise.resolve();
15084
+ function sleep(time, resolveWith) {
15085
+ if (!time) time = 0;
15086
+ return new Promise(function (res) {
15087
+ return setTimeout(function () {
15088
+ return res(resolveWith);
15089
+ }, time);
15090
+ });
15091
+ }
15092
+ function randomInt(min, max) {
15093
+ return Math.floor(Math.random() * (max - min + 1) + min);
15094
+ }
15095
+
15096
+ /**
15097
+ * https://stackoverflow.com/a/8084248
15098
+ */
15099
+ function randomToken() {
15100
+ return Math.random().toString(36).substring(2);
15101
+ }
15102
+ var lastMs = 0;
15103
+ var additional = 0;
15104
+
15105
+ /**
15106
+ * returns the current time in micro-seconds,
15107
+ * WARNING: This is a pseudo-function
15108
+ * Performance.now is not reliable in webworkers, so we just make sure to never return the same time.
15109
+ * This is enough in browsers, and this function will not be used in nodejs.
15110
+ * The main reason for this hack is to ensure that BroadcastChannel behaves equal to production when it is used in fast-running unit tests.
15111
+ */
15112
+ function microSeconds$4() {
15113
+ var ms = Date.now();
15114
+ if (ms === lastMs) {
15115
+ additional++;
15116
+ return ms * 1000 + additional;
15117
+ } else {
15118
+ lastMs = ms;
15119
+ additional = 0;
15120
+ return ms * 1000;
15121
+ }
15122
+ }
15123
+
15124
+ /**
15125
+ * Check if WebLock API is supported.
15126
+ * @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API
15127
+ */
15128
+ function supportsWebLockAPI() {
15129
+ if (typeof navigator !== 'undefined' && typeof navigator.locks !== 'undefined' && typeof navigator.locks.request === 'function') {
15130
+ return true;
15131
+ } else {
15132
+ return false;
15133
+ }
15134
+ }
15135
+
15136
+ var microSeconds$3 = microSeconds$4;
15137
+ var type$3 = 'native';
15138
+ function create$3(channelName) {
15139
+ var state = {
15140
+ messagesCallback: null,
15141
+ bc: new BroadcastChannel(channelName),
15142
+ subFns: [] // subscriberFunctions
15143
+ };
15144
+
15145
+ state.bc.onmessage = function (msg) {
15146
+ if (state.messagesCallback) {
15147
+ state.messagesCallback(msg.data);
15148
+ }
15149
+ };
15150
+ return state;
15151
+ }
15152
+ function close$3(channelState) {
15153
+ channelState.bc.close();
15154
+ channelState.subFns = [];
15155
+ }
15156
+ function postMessage$3(channelState, messageJson) {
15157
+ try {
15158
+ channelState.bc.postMessage(messageJson, false);
15159
+ return PROMISE_RESOLVED_VOID;
15160
+ } catch (err) {
15161
+ return Promise.reject(err);
15162
+ }
15163
+ }
15164
+ function onMessage$3(channelState, fn) {
15165
+ channelState.messagesCallback = fn;
15166
+ }
15167
+ function canBeUsed$3() {
15168
+ if ((typeof window !== 'undefined' || typeof self !== 'undefined') && typeof BroadcastChannel === 'function') {
15169
+ if (BroadcastChannel._pubkey) {
15170
+ throw new Error('BroadcastChannel: Do not overwrite window.BroadcastChannel with this module, this is not a polyfill');
15171
+ }
15172
+ return true;
15173
+ } else {
15174
+ return false;
15175
+ }
15176
+ }
15177
+ function averageResponseTime$3() {
15178
+ return 150;
15179
+ }
15180
+ var NativeMethod = {
15181
+ create: create$3,
15182
+ close: close$3,
15183
+ onMessage: onMessage$3,
15184
+ postMessage: postMessage$3,
15185
+ canBeUsed: canBeUsed$3,
15186
+ type: type$3,
15187
+ averageResponseTime: averageResponseTime$3,
15188
+ microSeconds: microSeconds$3
15189
+ };
15190
+
15191
+ /**
15192
+ * this is a set which automatically forgets
15193
+ * a given entry when a new entry is set and the ttl
15194
+ * of the old one is over
15195
+ */
15196
+ var ObliviousSet = /** @class */function () {
15197
+ function ObliviousSet(ttl) {
15198
+ this.ttl = ttl;
15199
+ this.map = new Map();
15200
+ /**
15201
+ * Creating calls to setTimeout() is expensive,
15202
+ * so we only do that if there is not timeout already open.
15203
+ */
15204
+ this._to = false;
15205
+ }
15206
+ ObliviousSet.prototype.has = function (value) {
15207
+ return this.map.has(value);
15208
+ };
15209
+ ObliviousSet.prototype.add = function (value) {
15210
+ var _this = this;
15211
+ this.map.set(value, now());
15212
+ /**
15213
+ * When a new value is added,
15214
+ * start the cleanup at the next tick
15215
+ * to not block the cpu for more important stuff
15216
+ * that might happen.
15217
+ */
15218
+ if (!this._to) {
15219
+ this._to = true;
15220
+ setTimeout(function () {
15221
+ _this._to = false;
15222
+ removeTooOldValues(_this);
15223
+ }, 0);
15224
+ }
15225
+ };
15226
+ ObliviousSet.prototype.clear = function () {
15227
+ this.map.clear();
15228
+ };
15229
+ return ObliviousSet;
15230
+ }();
15231
+ /**
15232
+ * Removes all entries from the set
15233
+ * where the TTL has expired
15234
+ */
15235
+ function removeTooOldValues(obliviousSet) {
15236
+ var olderThen = now() - obliviousSet.ttl;
15237
+ var iterator = obliviousSet.map[Symbol.iterator]();
15238
+ /**
15239
+ * Because we can assume the new values are added at the bottom,
15240
+ * we start from the top and stop as soon as we reach a non-too-old value.
15241
+ */
15242
+ while (true) {
15243
+ var next = iterator.next().value;
15244
+ if (!next) {
15245
+ return; // no more elements
15246
+ }
15247
+
15248
+ var value = next[0];
15249
+ var time = next[1];
15250
+ if (time < olderThen) {
15251
+ obliviousSet.map.delete(value);
15252
+ } else {
15253
+ // We reached a value that is not old enough
15254
+ return;
15255
+ }
15256
+ }
15257
+ }
15258
+ function now() {
15259
+ return new Date().getTime();
15260
+ }
15261
+
15262
+ function fillOptionsWithDefaults$1() {
15263
+ var originalOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
15264
+ var options = JSON.parse(JSON.stringify(originalOptions));
15265
+
15266
+ // main
15267
+ if (typeof options.webWorkerSupport === 'undefined') options.webWorkerSupport = true;
15268
+
15269
+ // indexed-db
15270
+ if (!options.idb) options.idb = {};
15271
+ // after this time the messages get deleted
15272
+ if (!options.idb.ttl) options.idb.ttl = 1000 * 45;
15273
+ if (!options.idb.fallbackInterval) options.idb.fallbackInterval = 150;
15274
+ // handles abrupt db onclose events.
15275
+ if (originalOptions.idb && typeof originalOptions.idb.onclose === 'function') options.idb.onclose = originalOptions.idb.onclose;
15276
+
15277
+ // localstorage
15278
+ if (!options.localstorage) options.localstorage = {};
15279
+ if (!options.localstorage.removeTimeout) options.localstorage.removeTimeout = 1000 * 60;
15280
+
15281
+ // custom methods
15282
+ if (originalOptions.methods) options.methods = originalOptions.methods;
15283
+
15284
+ // node
15285
+ if (!options.node) options.node = {};
15286
+ if (!options.node.ttl) options.node.ttl = 1000 * 60 * 2; // 2 minutes;
15287
+ /**
15288
+ * On linux use 'ulimit -Hn' to get the limit of open files.
15289
+ * On ubuntu this was 4096 for me, so we use half of that as maxParallelWrites default.
15290
+ */
15291
+ if (!options.node.maxParallelWrites) options.node.maxParallelWrites = 2048;
15292
+ if (typeof options.node.useFastPath === 'undefined') options.node.useFastPath = true;
15293
+ return options;
15294
+ }
15295
+
15296
+ /**
15297
+ * this method uses indexeddb to store the messages
15298
+ * There is currently no observerAPI for idb
15299
+ * @link https://github.com/w3c/IndexedDB/issues/51
15300
+ *
15301
+ * When working on this, ensure to use these performance optimizations:
15302
+ * @link https://rxdb.info/slow-indexeddb.html
15303
+ */
15304
+ var microSeconds$2 = microSeconds$4;
15305
+ var DB_PREFIX = 'pubkey.broadcast-channel-0-';
15306
+ var OBJECT_STORE_ID = 'messages';
15307
+
15308
+ /**
15309
+ * Use relaxed durability for faster performance on all transactions.
15310
+ * @link https://nolanlawson.com/2021/08/22/speeding-up-indexeddb-reads-and-writes/
15311
+ */
15312
+ var TRANSACTION_SETTINGS = {
15313
+ durability: 'relaxed'
15314
+ };
15315
+ var type$2 = 'idb';
15316
+ function getIdb() {
15317
+ if (typeof indexedDB !== 'undefined') return indexedDB;
15318
+ if (typeof window !== 'undefined') {
15319
+ if (typeof window.mozIndexedDB !== 'undefined') return window.mozIndexedDB;
15320
+ if (typeof window.webkitIndexedDB !== 'undefined') return window.webkitIndexedDB;
15321
+ if (typeof window.msIndexedDB !== 'undefined') return window.msIndexedDB;
15322
+ }
15323
+ return false;
15324
+ }
15325
+
15326
+ /**
15327
+ * If possible, we should explicitly commit IndexedDB transactions
15328
+ * for better performance.
15329
+ * @link https://nolanlawson.com/2021/08/22/speeding-up-indexeddb-reads-and-writes/
15330
+ */
15331
+ function commitIndexedDBTransaction(tx) {
15332
+ if (tx.commit) {
15333
+ tx.commit();
15334
+ }
15335
+ }
15336
+ function createDatabase(channelName) {
15337
+ var IndexedDB = getIdb();
15338
+
15339
+ // create table
15340
+ var dbName = DB_PREFIX + channelName;
15341
+
15342
+ /**
15343
+ * All IndexedDB databases are opened without version
15344
+ * because it is a bit faster, especially on firefox
15345
+ * @link http://nparashuram.com/IndexedDB/perf/#Open%20Database%20with%20version
15346
+ */
15347
+ var openRequest = IndexedDB.open(dbName);
15348
+ openRequest.onupgradeneeded = function (ev) {
15349
+ var db = ev.target.result;
15350
+ db.createObjectStore(OBJECT_STORE_ID, {
15351
+ keyPath: 'id',
15352
+ autoIncrement: true
15353
+ });
15354
+ };
15355
+ return new Promise(function (res, rej) {
15356
+ openRequest.onerror = function (ev) {
15357
+ return rej(ev);
15358
+ };
15359
+ openRequest.onsuccess = function () {
15360
+ res(openRequest.result);
15361
+ };
15362
+ });
15363
+ }
15364
+
15365
+ /**
15366
+ * writes the new message to the database
15367
+ * so other readers can find it
15368
+ */
15369
+ function writeMessage(db, readerUuid, messageJson) {
15370
+ var time = Date.now();
15371
+ var writeObject = {
15372
+ uuid: readerUuid,
15373
+ time: time,
15374
+ data: messageJson
15375
+ };
15376
+ var tx = db.transaction([OBJECT_STORE_ID], 'readwrite', TRANSACTION_SETTINGS);
15377
+ return new Promise(function (res, rej) {
15378
+ tx.oncomplete = function () {
15379
+ return res();
15380
+ };
15381
+ tx.onerror = function (ev) {
15382
+ return rej(ev);
15383
+ };
15384
+ var objectStore = tx.objectStore(OBJECT_STORE_ID);
15385
+ objectStore.add(writeObject);
15386
+ commitIndexedDBTransaction(tx);
15387
+ });
15388
+ }
15389
+ function getMessagesHigherThan(db, lastCursorId) {
15390
+ var tx = db.transaction(OBJECT_STORE_ID, 'readonly', TRANSACTION_SETTINGS);
15391
+ var objectStore = tx.objectStore(OBJECT_STORE_ID);
15392
+ var ret = [];
15393
+ var keyRangeValue = IDBKeyRange.bound(lastCursorId + 1, Infinity);
15394
+
15395
+ /**
15396
+ * Optimization shortcut,
15397
+ * if getAll() can be used, do not use a cursor.
15398
+ * @link https://rxdb.info/slow-indexeddb.html
15399
+ */
15400
+ if (objectStore.getAll) {
15401
+ var getAllRequest = objectStore.getAll(keyRangeValue);
15402
+ return new Promise(function (res, rej) {
15403
+ getAllRequest.onerror = function (err) {
15404
+ return rej(err);
15405
+ };
15406
+ getAllRequest.onsuccess = function (e) {
15407
+ res(e.target.result);
15408
+ };
15409
+ });
15410
+ }
15411
+ function openCursor() {
15412
+ // Occasionally Safari will fail on IDBKeyRange.bound, this
15413
+ // catches that error, having it open the cursor to the first
15414
+ // item. When it gets data it will advance to the desired key.
15415
+ try {
15416
+ keyRangeValue = IDBKeyRange.bound(lastCursorId + 1, Infinity);
15417
+ return objectStore.openCursor(keyRangeValue);
15418
+ } catch (e) {
15419
+ return objectStore.openCursor();
15420
+ }
15421
+ }
15422
+ return new Promise(function (res, rej) {
15423
+ var openCursorRequest = openCursor();
15424
+ openCursorRequest.onerror = function (err) {
15425
+ return rej(err);
15426
+ };
15427
+ openCursorRequest.onsuccess = function (ev) {
15428
+ var cursor = ev.target.result;
15429
+ if (cursor) {
15430
+ if (cursor.value.id < lastCursorId + 1) {
15431
+ cursor["continue"](lastCursorId + 1);
15432
+ } else {
15433
+ ret.push(cursor.value);
15434
+ cursor["continue"]();
15435
+ }
15436
+ } else {
15437
+ commitIndexedDBTransaction(tx);
15438
+ res(ret);
15439
+ }
15440
+ };
15441
+ });
15442
+ }
15443
+ function removeMessagesById(channelState, ids) {
15444
+ if (channelState.closed) {
15445
+ return Promise.resolve([]);
15446
+ }
15447
+ var tx = channelState.db.transaction(OBJECT_STORE_ID, 'readwrite', TRANSACTION_SETTINGS);
15448
+ var objectStore = tx.objectStore(OBJECT_STORE_ID);
15449
+ return Promise.all(ids.map(function (id) {
15450
+ var deleteRequest = objectStore["delete"](id);
15451
+ return new Promise(function (res) {
15452
+ deleteRequest.onsuccess = function () {
15453
+ return res();
15454
+ };
15455
+ });
15456
+ }));
15457
+ }
15458
+ function getOldMessages(db, ttl) {
15459
+ var olderThen = Date.now() - ttl;
15460
+ var tx = db.transaction(OBJECT_STORE_ID, 'readonly', TRANSACTION_SETTINGS);
15461
+ var objectStore = tx.objectStore(OBJECT_STORE_ID);
15462
+ var ret = [];
15463
+ return new Promise(function (res) {
15464
+ objectStore.openCursor().onsuccess = function (ev) {
15465
+ var cursor = ev.target.result;
15466
+ if (cursor) {
15467
+ var msgObk = cursor.value;
15468
+ if (msgObk.time < olderThen) {
15469
+ ret.push(msgObk);
15470
+ //alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
15471
+ cursor["continue"]();
15472
+ } else {
15473
+ // no more old messages,
15474
+ commitIndexedDBTransaction(tx);
15475
+ res(ret);
15476
+ }
15477
+ } else {
15478
+ res(ret);
15479
+ }
15480
+ };
15481
+ });
15482
+ }
15483
+ function cleanOldMessages(channelState) {
15484
+ return getOldMessages(channelState.db, channelState.options.idb.ttl).then(function (tooOld) {
15485
+ return removeMessagesById(channelState, tooOld.map(function (msg) {
15486
+ return msg.id;
15487
+ }));
15488
+ });
15489
+ }
15490
+ function create$2(channelName, options) {
15491
+ options = fillOptionsWithDefaults$1(options);
15492
+ return createDatabase(channelName).then(function (db) {
15493
+ var state = {
15494
+ closed: false,
15495
+ lastCursorId: 0,
15496
+ channelName: channelName,
15497
+ options: options,
15498
+ uuid: randomToken(),
15499
+ /**
15500
+ * emittedMessagesIds
15501
+ * contains all messages that have been emitted before
15502
+ * @type {ObliviousSet}
15503
+ */
15504
+ eMIs: new ObliviousSet(options.idb.ttl * 2),
15505
+ // ensures we do not read messages in parallel
15506
+ writeBlockPromise: PROMISE_RESOLVED_VOID,
15507
+ messagesCallback: null,
15508
+ readQueuePromises: [],
15509
+ db: db
15510
+ };
15511
+
15512
+ /**
15513
+ * Handle abrupt closes that do not originate from db.close().
15514
+ * This could happen, for example, if the underlying storage is
15515
+ * removed or if the user clears the database in the browser's
15516
+ * history preferences.
15517
+ */
15518
+ db.onclose = function () {
15519
+ state.closed = true;
15520
+ if (options.idb.onclose) options.idb.onclose();
15521
+ };
15522
+
15523
+ /**
15524
+ * if service-workers are used,
15525
+ * we have no 'storage'-event if they post a message,
15526
+ * therefore we also have to set an interval
15527
+ */
15528
+ _readLoop(state);
15529
+ return state;
15530
+ });
15531
+ }
15532
+ function _readLoop(state) {
15533
+ if (state.closed) return;
15534
+ readNewMessages(state).then(function () {
15535
+ return sleep(state.options.idb.fallbackInterval);
15536
+ }).then(function () {
15537
+ return _readLoop(state);
15538
+ });
15539
+ }
15540
+ function _filterMessage(msgObj, state) {
15541
+ if (msgObj.uuid === state.uuid) return false; // send by own
15542
+ if (state.eMIs.has(msgObj.id)) return false; // already emitted
15543
+ if (msgObj.data.time < state.messagesCallbackTime) return false; // older then onMessageCallback
15544
+ return true;
15545
+ }
15546
+
15547
+ /**
15548
+ * reads all new messages from the database and emits them
15549
+ */
15550
+ function readNewMessages(state) {
15551
+ // channel already closed
15552
+ if (state.closed) return PROMISE_RESOLVED_VOID;
15553
+
15554
+ // if no one is listening, we do not need to scan for new messages
15555
+ if (!state.messagesCallback) return PROMISE_RESOLVED_VOID;
15556
+ return getMessagesHigherThan(state.db, state.lastCursorId).then(function (newerMessages) {
15557
+ var useMessages = newerMessages
15558
+ /**
15559
+ * there is a bug in iOS where the msgObj can be undefined sometimes
15560
+ * so we filter them out
15561
+ * @link https://github.com/pubkey/broadcast-channel/issues/19
15562
+ */.filter(function (msgObj) {
15563
+ return !!msgObj;
15564
+ }).map(function (msgObj) {
15565
+ if (msgObj.id > state.lastCursorId) {
15566
+ state.lastCursorId = msgObj.id;
15567
+ }
15568
+ return msgObj;
15569
+ }).filter(function (msgObj) {
15570
+ return _filterMessage(msgObj, state);
15571
+ }).sort(function (msgObjA, msgObjB) {
15572
+ return msgObjA.time - msgObjB.time;
15573
+ }); // sort by time
15574
+ useMessages.forEach(function (msgObj) {
15575
+ if (state.messagesCallback) {
15576
+ state.eMIs.add(msgObj.id);
15577
+ state.messagesCallback(msgObj.data);
15578
+ }
15579
+ });
15580
+ return PROMISE_RESOLVED_VOID;
15581
+ });
15582
+ }
15583
+ function close$2(channelState) {
15584
+ channelState.closed = true;
15585
+ channelState.db.close();
15586
+ }
15587
+ function postMessage$2(channelState, messageJson) {
15588
+ channelState.writeBlockPromise = channelState.writeBlockPromise.then(function () {
15589
+ return writeMessage(channelState.db, channelState.uuid, messageJson);
15590
+ }).then(function () {
15591
+ if (randomInt(0, 10) === 0) {
15592
+ /* await (do not await) */
15593
+ cleanOldMessages(channelState);
15594
+ }
15595
+ });
15596
+ return channelState.writeBlockPromise;
15597
+ }
15598
+ function onMessage$2(channelState, fn, time) {
15599
+ channelState.messagesCallbackTime = time;
15600
+ channelState.messagesCallback = fn;
15601
+ readNewMessages(channelState);
15602
+ }
15603
+ function canBeUsed$2() {
15604
+ return !!getIdb();
15605
+ }
15606
+ function averageResponseTime$2(options) {
15607
+ return options.idb.fallbackInterval * 2;
15608
+ }
15609
+ var IndexedDBMethod = {
15610
+ create: create$2,
15611
+ close: close$2,
15612
+ onMessage: onMessage$2,
15613
+ postMessage: postMessage$2,
15614
+ canBeUsed: canBeUsed$2,
15615
+ type: type$2,
15616
+ averageResponseTime: averageResponseTime$2,
15617
+ microSeconds: microSeconds$2
15618
+ };
15619
+
15620
+ /**
15621
+ * A localStorage-only method which uses localstorage and its 'storage'-event
15622
+ * This does not work inside webworkers because they have no access to localstorage
15623
+ * This is basically implemented to support IE9 or your grandmother's toaster.
15624
+ * @link https://caniuse.com/#feat=namevalue-storage
15625
+ * @link https://caniuse.com/#feat=indexeddb
15626
+ */
15627
+ var microSeconds$1 = microSeconds$4;
15628
+ var KEY_PREFIX = 'pubkey.broadcastChannel-';
15629
+ var type$1 = 'localstorage';
15630
+
15631
+ /**
15632
+ * copied from crosstab
15633
+ * @link https://github.com/tejacques/crosstab/blob/master/src/crosstab.js#L32
15634
+ */
15635
+ function getLocalStorage() {
15636
+ var localStorage;
15637
+ if (typeof window === 'undefined') return null;
15638
+ try {
15639
+ localStorage = window.localStorage;
15640
+ localStorage = window['ie8-eventlistener/storage'] || window.localStorage;
15641
+ } catch (e) {
15642
+ // New versions of Firefox throw a Security exception
15643
+ // if cookies are disabled. See
15644
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1028153
15645
+ }
15646
+ return localStorage;
15647
+ }
15648
+ function storageKey(channelName) {
15649
+ return KEY_PREFIX + channelName;
15650
+ }
15651
+
15652
+ /**
15653
+ * writes the new message to the storage
15654
+ * and fires the storage-event so other readers can find it
15655
+ */
15656
+ function postMessage$1(channelState, messageJson) {
15657
+ return new Promise(function (res) {
15658
+ sleep().then(function () {
15659
+ var key = storageKey(channelState.channelName);
15660
+ var writeObj = {
15661
+ token: randomToken(),
15662
+ time: Date.now(),
15663
+ data: messageJson,
15664
+ uuid: channelState.uuid
15665
+ };
15666
+ var value = JSON.stringify(writeObj);
15667
+ getLocalStorage().setItem(key, value);
15668
+
15669
+ /**
15670
+ * StorageEvent does not fire the 'storage' event
15671
+ * in the window that changes the state of the local storage.
15672
+ * So we fire it manually
15673
+ */
15674
+ var ev = document.createEvent('Event');
15675
+ ev.initEvent('storage', true, true);
15676
+ ev.key = key;
15677
+ ev.newValue = value;
15678
+ window.dispatchEvent(ev);
15679
+ res();
15680
+ });
15681
+ });
15682
+ }
15683
+ function addStorageEventListener(channelName, fn) {
15684
+ var key = storageKey(channelName);
15685
+ var listener = function listener(ev) {
15686
+ if (ev.key === key) {
15687
+ fn(JSON.parse(ev.newValue));
15688
+ }
15689
+ };
15690
+ window.addEventListener('storage', listener);
15691
+ return listener;
15692
+ }
15693
+ function removeStorageEventListener(listener) {
15694
+ window.removeEventListener('storage', listener);
15695
+ }
15696
+ function create$1(channelName, options) {
15697
+ options = fillOptionsWithDefaults$1(options);
15698
+ if (!canBeUsed$1()) {
15699
+ throw new Error('BroadcastChannel: localstorage cannot be used');
15700
+ }
15701
+ var uuid = randomToken();
15702
+
15703
+ /**
15704
+ * eMIs
15705
+ * contains all messages that have been emitted before
15706
+ * @type {ObliviousSet}
15707
+ */
15708
+ var eMIs = new ObliviousSet(options.localstorage.removeTimeout);
15709
+ var state = {
15710
+ channelName: channelName,
15711
+ uuid: uuid,
15712
+ eMIs: eMIs // emittedMessagesIds
15713
+ };
15714
+
15715
+ state.listener = addStorageEventListener(channelName, function (msgObj) {
15716
+ if (!state.messagesCallback) return; // no listener
15717
+ if (msgObj.uuid === uuid) return; // own message
15718
+ if (!msgObj.token || eMIs.has(msgObj.token)) return; // already emitted
15719
+ if (msgObj.data.time && msgObj.data.time < state.messagesCallbackTime) return; // too old
15720
+
15721
+ eMIs.add(msgObj.token);
15722
+ state.messagesCallback(msgObj.data);
15723
+ });
15724
+ return state;
15725
+ }
15726
+ function close$1(channelState) {
15727
+ removeStorageEventListener(channelState.listener);
15728
+ }
15729
+ function onMessage$1(channelState, fn, time) {
15730
+ channelState.messagesCallbackTime = time;
15731
+ channelState.messagesCallback = fn;
15732
+ }
15733
+ function canBeUsed$1() {
15734
+ var ls = getLocalStorage();
15735
+ if (!ls) return false;
15736
+ try {
15737
+ var key = '__broadcastchannel_check';
15738
+ ls.setItem(key, 'works');
15739
+ ls.removeItem(key);
15740
+ } catch (e) {
15741
+ // Safari 10 in private mode will not allow write access to local
15742
+ // storage and fail with a QuotaExceededError. See
15743
+ // https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API#Private_Browsing_Incognito_modes
15744
+ return false;
15745
+ }
15746
+ return true;
15747
+ }
15748
+ function averageResponseTime$1() {
15749
+ var defaultTime = 120;
15750
+ var userAgent = navigator.userAgent.toLowerCase();
15751
+ if (userAgent.includes('safari') && !userAgent.includes('chrome')) {
15752
+ // safari is much slower so this time is higher
15753
+ return defaultTime * 2;
15754
+ }
15755
+ return defaultTime;
15756
+ }
15757
+ var LocalstorageMethod = {
15758
+ create: create$1,
15759
+ close: close$1,
15760
+ onMessage: onMessage$1,
15761
+ postMessage: postMessage$1,
15762
+ canBeUsed: canBeUsed$1,
15763
+ type: type$1,
15764
+ averageResponseTime: averageResponseTime$1,
15765
+ microSeconds: microSeconds$1
15766
+ };
15767
+
15768
+ var microSeconds = microSeconds$4;
15769
+ var type = 'simulate';
15770
+ var SIMULATE_CHANNELS = new Set();
15771
+ function create(channelName) {
15772
+ var state = {
15773
+ name: channelName,
15774
+ messagesCallback: null
15775
+ };
15776
+ SIMULATE_CHANNELS.add(state);
15777
+ return state;
15778
+ }
15779
+ function close(channelState) {
15780
+ SIMULATE_CHANNELS["delete"](channelState);
15781
+ }
15782
+ function postMessage(channelState, messageJson) {
15783
+ return new Promise(function (res) {
15784
+ return setTimeout(function () {
15785
+ var channelArray = Array.from(SIMULATE_CHANNELS);
15786
+ channelArray.filter(function (channel) {
15787
+ return channel.name === channelState.name;
15788
+ }).filter(function (channel) {
15789
+ return channel !== channelState;
15790
+ }).filter(function (channel) {
15791
+ return !!channel.messagesCallback;
15792
+ }).forEach(function (channel) {
15793
+ return channel.messagesCallback(messageJson);
15794
+ });
15795
+ res();
15796
+ }, 5);
15797
+ });
15798
+ }
15799
+ function onMessage(channelState, fn) {
15800
+ channelState.messagesCallback = fn;
15801
+ }
15802
+ function canBeUsed() {
15803
+ return true;
15804
+ }
15805
+ function averageResponseTime() {
15806
+ return 5;
15807
+ }
15808
+ var SimulateMethod = {
15809
+ create: create,
15810
+ close: close,
15811
+ onMessage: onMessage,
15812
+ postMessage: postMessage,
15813
+ canBeUsed: canBeUsed,
15814
+ type: type,
15815
+ averageResponseTime: averageResponseTime,
15816
+ microSeconds: microSeconds
15817
+ };
15818
+
15819
+ // the line below will be removed from es5/browser builds
15820
+
15821
+ // order is important
15822
+ var METHODS = [NativeMethod,
15823
+ // fastest
15824
+ IndexedDBMethod, LocalstorageMethod];
15825
+ function chooseMethod(options) {
15826
+ var chooseMethods = [].concat(options.methods, METHODS).filter(Boolean);
15827
+
15828
+ // the line below will be removed from es5/browser builds
15829
+
15830
+ // directly chosen
15831
+ if (options.type) {
15832
+ if (options.type === 'simulate') {
15833
+ // only use simulate-method if directly chosen
15834
+ return SimulateMethod;
15835
+ }
15836
+ var ret = chooseMethods.find(function (m) {
15837
+ return m.type === options.type;
15838
+ });
15839
+ if (!ret) throw new Error('method-type ' + options.type + ' not found');else return ret;
15840
+ }
15841
+
15842
+ /**
15843
+ * if no webworker support is needed,
15844
+ * remove idb from the list so that localstorage will be chosen
15845
+ */
15846
+ if (!options.webWorkerSupport) {
15847
+ chooseMethods = chooseMethods.filter(function (m) {
15848
+ return m.type !== 'idb';
15849
+ });
15850
+ }
15851
+ var useMethod = chooseMethods.find(function (method) {
15852
+ return method.canBeUsed();
15853
+ });
15854
+ if (!useMethod) {
15855
+ throw new Error("No usable method found in " + JSON.stringify(METHODS.map(function (m) {
15856
+ return m.type;
15857
+ })));
15858
+ } else {
15859
+ return useMethod;
15860
+ }
15861
+ }
15862
+
15863
+ /**
15864
+ * Contains all open channels,
15865
+ * used in tests to ensure everything is closed.
15866
+ */
15867
+ var OPEN_BROADCAST_CHANNELS = new Set();
15868
+ var lastId = 0;
15869
+ var BroadcastChannel$1 = function BroadcastChannel(name, options) {
15870
+ // identifier of the channel to debug stuff
15871
+ this.id = lastId++;
15872
+ OPEN_BROADCAST_CHANNELS.add(this);
15873
+ this.name = name;
15874
+ if (ENFORCED_OPTIONS) {
15875
+ options = ENFORCED_OPTIONS;
15876
+ }
15877
+ this.options = fillOptionsWithDefaults$1(options);
15878
+ this.method = chooseMethod(this.options);
15879
+
15880
+ // isListening
15881
+ this._iL = false;
15882
+
15883
+ /**
15884
+ * _onMessageListener
15885
+ * setting onmessage twice,
15886
+ * will overwrite the first listener
15887
+ */
15888
+ this._onML = null;
15889
+
15890
+ /**
15891
+ * _addEventListeners
15892
+ */
15893
+ this._addEL = {
15894
+ message: [],
15895
+ internal: []
15896
+ };
15897
+
15898
+ /**
15899
+ * Unsent message promises
15900
+ * where the sending is still in progress
15901
+ * @type {Set<Promise>}
15902
+ */
15903
+ this._uMP = new Set();
15904
+
15905
+ /**
15906
+ * _beforeClose
15907
+ * array of promises that will be awaited
15908
+ * before the channel is closed
15909
+ */
15910
+ this._befC = [];
15911
+
15912
+ /**
15913
+ * _preparePromise
15914
+ */
15915
+ this._prepP = null;
15916
+ _prepareChannel(this);
15917
+ };
15918
+
15919
+ // STATICS
15920
+
15921
+ /**
15922
+ * used to identify if someone overwrites
15923
+ * window.BroadcastChannel with this
15924
+ * See methods/native.js
15925
+ */
15926
+ BroadcastChannel$1._pubkey = true;
15927
+
15928
+ /**
15929
+ * clears the tmp-folder if is node
15930
+ * @return {Promise<boolean>} true if has run, false if not node
15931
+ */
15932
+ function clearNodeFolder(options) {
15933
+ options = fillOptionsWithDefaults$1(options);
15934
+ var method = chooseMethod(options);
15935
+ if (method.type === 'node') {
15936
+ return method.clearNodeFolder().then(function () {
15937
+ return true;
15938
+ });
15939
+ } else {
15940
+ return PROMISE_RESOLVED_FALSE;
15941
+ }
15942
+ }
15943
+
15944
+ /**
15945
+ * if set, this method is enforced,
15946
+ * no mather what the options are
15947
+ */
15948
+ var ENFORCED_OPTIONS;
15949
+ function enforceOptions(options) {
15950
+ ENFORCED_OPTIONS = options;
15951
+ }
15952
+
15953
+ // PROTOTYPE
15954
+ BroadcastChannel$1.prototype = {
15955
+ postMessage: function postMessage(msg) {
15956
+ if (this.closed) {
15957
+ throw new Error('BroadcastChannel.postMessage(): ' + 'Cannot post message after channel has closed ' +
15958
+ /**
15959
+ * In the past when this error appeared, it was really hard to debug.
15960
+ * So now we log the msg together with the error so it at least
15961
+ * gives some clue about where in your application this happens.
15962
+ */
15963
+ JSON.stringify(msg));
15964
+ }
15965
+ return _post(this, 'message', msg);
15966
+ },
15967
+ postInternal: function postInternal(msg) {
15968
+ return _post(this, 'internal', msg);
15969
+ },
15970
+ set onmessage(fn) {
15971
+ var time = this.method.microSeconds();
15972
+ var listenObj = {
15973
+ time: time,
15974
+ fn: fn
15975
+ };
15976
+ _removeListenerObject(this, 'message', this._onML);
15977
+ if (fn && typeof fn === 'function') {
15978
+ this._onML = listenObj;
15979
+ _addListenerObject(this, 'message', listenObj);
15980
+ } else {
15981
+ this._onML = null;
15982
+ }
15983
+ },
15984
+ addEventListener: function addEventListener(type, fn) {
15985
+ var time = this.method.microSeconds();
15986
+ var listenObj = {
15987
+ time: time,
15988
+ fn: fn
15989
+ };
15990
+ _addListenerObject(this, type, listenObj);
15991
+ },
15992
+ removeEventListener: function removeEventListener(type, fn) {
15993
+ var obj = this._addEL[type].find(function (obj) {
15994
+ return obj.fn === fn;
15995
+ });
15996
+ _removeListenerObject(this, type, obj);
15997
+ },
15998
+ close: function close() {
15999
+ var _this = this;
16000
+ if (this.closed) {
16001
+ return;
16002
+ }
16003
+ OPEN_BROADCAST_CHANNELS["delete"](this);
16004
+ this.closed = true;
16005
+ var awaitPrepare = this._prepP ? this._prepP : PROMISE_RESOLVED_VOID;
16006
+ this._onML = null;
16007
+ this._addEL.message = [];
16008
+ return awaitPrepare
16009
+ // wait until all current sending are processed
16010
+ .then(function () {
16011
+ return Promise.all(Array.from(_this._uMP));
16012
+ })
16013
+ // run before-close hooks
16014
+ .then(function () {
16015
+ return Promise.all(_this._befC.map(function (fn) {
16016
+ return fn();
16017
+ }));
16018
+ })
16019
+ // close the channel
16020
+ .then(function () {
16021
+ return _this.method.close(_this._state);
16022
+ });
16023
+ },
16024
+ get type() {
16025
+ return this.method.type;
16026
+ },
16027
+ get isClosed() {
16028
+ return this.closed;
16029
+ }
16030
+ };
16031
+
16032
+ /**
16033
+ * Post a message over the channel
16034
+ * @returns {Promise} that resolved when the message sending is done
16035
+ */
16036
+ function _post(broadcastChannel, type, msg) {
16037
+ var time = broadcastChannel.method.microSeconds();
16038
+ var msgObj = {
16039
+ time: time,
16040
+ type: type,
16041
+ data: msg
16042
+ };
16043
+ var awaitPrepare = broadcastChannel._prepP ? broadcastChannel._prepP : PROMISE_RESOLVED_VOID;
16044
+ return awaitPrepare.then(function () {
16045
+ var sendPromise = broadcastChannel.method.postMessage(broadcastChannel._state, msgObj);
16046
+
16047
+ // add/remove to unsent messages list
16048
+ broadcastChannel._uMP.add(sendPromise);
16049
+ sendPromise["catch"]().then(function () {
16050
+ return broadcastChannel._uMP["delete"](sendPromise);
16051
+ });
16052
+ return sendPromise;
16053
+ });
16054
+ }
16055
+ function _prepareChannel(channel) {
16056
+ var maybePromise = channel.method.create(channel.name, channel.options);
16057
+ if (isPromise(maybePromise)) {
16058
+ channel._prepP = maybePromise;
16059
+ maybePromise.then(function (s) {
16060
+ // used in tests to simulate slow runtime
16061
+ /*if (channel.options.prepareDelay) {
16062
+ await new Promise(res => setTimeout(res, this.options.prepareDelay));
16063
+ }*/
16064
+ channel._state = s;
16065
+ });
16066
+ } else {
16067
+ channel._state = maybePromise;
16068
+ }
16069
+ }
16070
+ function _hasMessageListeners(channel) {
16071
+ if (channel._addEL.message.length > 0) return true;
16072
+ if (channel._addEL.internal.length > 0) return true;
16073
+ return false;
16074
+ }
16075
+ function _addListenerObject(channel, type, obj) {
16076
+ channel._addEL[type].push(obj);
16077
+ _startListening(channel);
16078
+ }
16079
+ function _removeListenerObject(channel, type, obj) {
16080
+ channel._addEL[type] = channel._addEL[type].filter(function (o) {
16081
+ return o !== obj;
16082
+ });
16083
+ _stopListening(channel);
16084
+ }
16085
+ function _startListening(channel) {
16086
+ if (!channel._iL && _hasMessageListeners(channel)) {
16087
+ // someone is listening, start subscribing
16088
+
16089
+ var listenerFn = function listenerFn(msgObj) {
16090
+ channel._addEL[msgObj.type].forEach(function (listenerObject) {
16091
+ /**
16092
+ * Getting the current time in JavaScript has no good precision.
16093
+ * So instead of only listening to events that happened 'after' the listener
16094
+ * was added, we also listen to events that happened 100ms before it.
16095
+ * This ensures that when another process, like a WebWorker, sends events
16096
+ * we do not miss them out because their timestamp is a bit off compared to the main process.
16097
+ * Not doing this would make messages missing when we send data directly after subscribing and awaiting a response.
16098
+ * @link https://johnresig.com/blog/accuracy-of-javascript-time/
16099
+ */
16100
+ var hundredMsInMicro = 100 * 1000;
16101
+ var minMessageTime = listenerObject.time - hundredMsInMicro;
16102
+ if (msgObj.time >= minMessageTime) {
16103
+ listenerObject.fn(msgObj.data);
16104
+ }
16105
+ });
16106
+ };
16107
+ var time = channel.method.microSeconds();
16108
+ if (channel._prepP) {
16109
+ channel._prepP.then(function () {
16110
+ channel._iL = true;
16111
+ channel.method.onMessage(channel._state, listenerFn, time);
16112
+ });
16113
+ } else {
16114
+ channel._iL = true;
16115
+ channel.method.onMessage(channel._state, listenerFn, time);
16116
+ }
16117
+ }
16118
+ }
16119
+ function _stopListening(channel) {
16120
+ if (channel._iL && !_hasMessageListeners(channel)) {
16121
+ // no one is listening, stop subscribing
16122
+ channel._iL = false;
16123
+ var time = channel.method.microSeconds();
16124
+ channel.method.onMessage(channel._state, null, time);
16125
+ }
16126
+ }
16127
+
16128
+ /* global WorkerGlobalScope */
16129
+
16130
+ function addBrowser(fn) {
16131
+ if (typeof WorkerGlobalScope === 'function' && self instanceof WorkerGlobalScope) {
16132
+ /**
16133
+ * Because killing a worker does directly stop the excution
16134
+ * of the code, our only chance is to overwrite the close function
16135
+ * which could work some times.
16136
+ * @link https://stackoverflow.com/q/72903255/3443137
16137
+ */
16138
+ var oldClose = self.close.bind(self);
16139
+ self.close = function () {
16140
+ fn();
16141
+ return oldClose();
16142
+ };
16143
+ } else {
16144
+ /**
16145
+ * if we are on react-native, there is no window.addEventListener
16146
+ * @link https://github.com/pubkey/unload/issues/6
16147
+ */
16148
+ if (typeof window.addEventListener !== 'function') {
16149
+ return;
16150
+ }
16151
+
16152
+ /**
16153
+ * for normal browser-windows, we use the beforeunload-event
16154
+ */
16155
+ window.addEventListener('beforeunload', function () {
16156
+ fn();
16157
+ }, true);
16158
+
16159
+ /**
16160
+ * for iframes, we have to use the unload-event
16161
+ * @link https://stackoverflow.com/q/47533670/3443137
16162
+ */
16163
+ window.addEventListener('unload', function () {
16164
+ fn();
16165
+ }, true);
16166
+ }
16167
+
16168
+ /**
16169
+ * TODO add fallback for safari-mobile
16170
+ * @link https://stackoverflow.com/a/26193516/3443137
16171
+ */
16172
+ }
16173
+
16174
+ function addNode(fn) {
16175
+ process.on('exit', function () {
16176
+ return fn();
16177
+ });
16178
+
16179
+ /**
16180
+ * on the following events,
16181
+ * the process will not end if there are
16182
+ * event-handlers attached,
16183
+ * therefore we have to call process.exit()
16184
+ */
16185
+ process.on('beforeExit', function () {
16186
+ return fn().then(function () {
16187
+ return process.exit();
16188
+ });
16189
+ });
16190
+ // catches ctrl+c event
16191
+ process.on('SIGINT', function () {
16192
+ return fn().then(function () {
16193
+ return process.exit();
16194
+ });
16195
+ });
16196
+ // catches uncaught exceptions
16197
+ process.on('uncaughtException', function (err) {
16198
+ return fn().then(function () {
16199
+ console.trace(err);
16200
+ process.exit(101);
16201
+ });
16202
+ });
16203
+ }
16204
+
16205
+ /**
16206
+ * Use the code directly to prevent import problems
16207
+ * with the detect-node package.
16208
+ * @link https://github.com/iliakan/detect-node/blob/master/index.js
16209
+ */
16210
+ var isNode = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
16211
+ var USE_METHOD = isNode ? addNode : addBrowser;
16212
+ var LISTENERS = new Set();
16213
+ var startedListening = false;
16214
+ function startListening() {
16215
+ if (startedListening) {
16216
+ return;
16217
+ }
16218
+ startedListening = true;
16219
+ USE_METHOD(runAll);
16220
+ }
16221
+ function add(fn) {
16222
+ startListening();
16223
+ if (typeof fn !== 'function') {
16224
+ throw new Error('Listener is no function');
16225
+ }
16226
+ LISTENERS.add(fn);
16227
+ var addReturn = {
16228
+ remove: function remove() {
16229
+ return LISTENERS["delete"](fn);
16230
+ },
16231
+ run: function run() {
16232
+ LISTENERS["delete"](fn);
16233
+ return fn();
16234
+ }
16235
+ };
16236
+ return addReturn;
16237
+ }
16238
+ function runAll() {
16239
+ var promises = [];
16240
+ LISTENERS.forEach(function (fn) {
16241
+ promises.push(fn());
16242
+ LISTENERS["delete"](fn);
16243
+ });
16244
+ return Promise.all(promises);
16245
+ }
16246
+
16247
+ /**
16248
+ * sends and internal message over the broadcast-channel
16249
+ */
16250
+ function sendLeaderMessage(leaderElector, action) {
16251
+ var msgJson = {
16252
+ context: 'leader',
16253
+ action: action,
16254
+ token: leaderElector.token
16255
+ };
16256
+ return leaderElector.broadcastChannel.postInternal(msgJson);
16257
+ }
16258
+ function beLeader(leaderElector) {
16259
+ leaderElector.isLeader = true;
16260
+ leaderElector._hasLeader = true;
16261
+ var unloadFn = add(function () {
16262
+ return leaderElector.die();
16263
+ });
16264
+ leaderElector._unl.push(unloadFn);
16265
+ var isLeaderListener = function isLeaderListener(msg) {
16266
+ if (msg.context === 'leader' && msg.action === 'apply') {
16267
+ sendLeaderMessage(leaderElector, 'tell');
16268
+ }
16269
+ if (msg.context === 'leader' && msg.action === 'tell' && !leaderElector._dpLC) {
16270
+ /**
16271
+ * another instance is also leader!
16272
+ * This can happen on rare events
16273
+ * like when the CPU is at 100% for long time
16274
+ * or the tabs are open very long and the browser throttles them.
16275
+ * @link https://github.com/pubkey/broadcast-channel/issues/414
16276
+ * @link https://github.com/pubkey/broadcast-channel/issues/385
16277
+ */
16278
+ leaderElector._dpLC = true;
16279
+ leaderElector._dpL(); // message the lib user so the app can handle the problem
16280
+ sendLeaderMessage(leaderElector, 'tell'); // ensure other leader also knows the problem
16281
+ }
16282
+ };
16283
+
16284
+ leaderElector.broadcastChannel.addEventListener('internal', isLeaderListener);
16285
+ leaderElector._lstns.push(isLeaderListener);
16286
+ return sendLeaderMessage(leaderElector, 'tell');
16287
+ }
16288
+
16289
+ /**
16290
+ * A faster version of the leader elector that uses the WebLock API
16291
+ * @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API
16292
+ */
16293
+ var LeaderElectionWebLock = function LeaderElectionWebLock(broadcastChannel, options) {
16294
+ var _this = this;
16295
+ this.broadcastChannel = broadcastChannel;
16296
+ broadcastChannel._befC.push(function () {
16297
+ return _this.die();
16298
+ });
16299
+ this._options = options;
16300
+ this.isLeader = false;
16301
+ this.isDead = false;
16302
+ this.token = randomToken();
16303
+ this._lstns = [];
16304
+ this._unl = [];
16305
+ this._dpL = function () {}; // onduplicate listener
16306
+ this._dpLC = false; // true when onduplicate called
16307
+
16308
+ this._wKMC = {}; // stuff for cleanup
16309
+
16310
+ // lock name
16311
+ this.lN = 'pubkey-bc||' + broadcastChannel.method.type + '||' + broadcastChannel.name;
16312
+ };
16313
+ LeaderElectionWebLock.prototype = {
16314
+ hasLeader: function hasLeader() {
16315
+ var _this2 = this;
16316
+ return navigator.locks.query().then(function (locks) {
16317
+ var relevantLocks = locks.held ? locks.held.filter(function (lock) {
16318
+ return lock.name === _this2.lN;
16319
+ }) : [];
16320
+ if (relevantLocks && relevantLocks.length > 0) {
16321
+ return true;
16322
+ } else {
16323
+ return false;
16324
+ }
16325
+ });
16326
+ },
16327
+ awaitLeadership: function awaitLeadership() {
16328
+ var _this3 = this;
16329
+ if (!this._wLMP) {
16330
+ this._wKMC.c = new AbortController();
16331
+ var returnPromise = new Promise(function (res, rej) {
16332
+ _this3._wKMC.res = res;
16333
+ _this3._wKMC.rej = rej;
16334
+ });
16335
+ this._wLMP = new Promise(function (res) {
16336
+ navigator.locks.request(_this3.lN, {
16337
+ signal: _this3._wKMC.c.signal
16338
+ }, function () {
16339
+ // if the lock resolved, we can drop the abort controller
16340
+ _this3._wKMC.c = undefined;
16341
+ beLeader(_this3);
16342
+ res();
16343
+ return returnPromise;
16344
+ })["catch"](function () {});
16345
+ });
16346
+ }
16347
+ return this._wLMP;
16348
+ },
16349
+ set onduplicate(_fn) {
16350
+ // Do nothing because there are no duplicates in the WebLock version
16351
+ },
16352
+ die: function die() {
16353
+ var _this4 = this;
16354
+ this._lstns.forEach(function (listener) {
16355
+ return _this4.broadcastChannel.removeEventListener('internal', listener);
16356
+ });
16357
+ this._lstns = [];
16358
+ this._unl.forEach(function (uFn) {
16359
+ return uFn.remove();
16360
+ });
16361
+ this._unl = [];
16362
+ if (this.isLeader) {
16363
+ this.isLeader = false;
16364
+ }
16365
+ this.isDead = true;
16366
+ if (this._wKMC.res) {
16367
+ this._wKMC.res();
16368
+ }
16369
+ if (this._wKMC.c) {
16370
+ this._wKMC.c.abort('LeaderElectionWebLock.die() called');
16371
+ }
16372
+ return sendLeaderMessage(this, 'death');
16373
+ }
16374
+ };
16375
+
16376
+ var LeaderElection = function LeaderElection(broadcastChannel, options) {
16377
+ var _this = this;
16378
+ this.broadcastChannel = broadcastChannel;
16379
+ this._options = options;
16380
+ this.isLeader = false;
16381
+ this._hasLeader = false;
16382
+ this.isDead = false;
16383
+ this.token = randomToken();
16384
+
16385
+ /**
16386
+ * Apply Queue,
16387
+ * used to ensure we do not run applyOnce()
16388
+ * in parallel.
16389
+ */
16390
+ this._aplQ = PROMISE_RESOLVED_VOID;
16391
+ // amount of unfinished applyOnce() calls
16392
+ this._aplQC = 0;
16393
+
16394
+ // things to clean up
16395
+ this._unl = []; // _unloads
16396
+ this._lstns = []; // _listeners
16397
+ this._dpL = function () {}; // onduplicate listener
16398
+ this._dpLC = false; // true when onduplicate called
16399
+
16400
+ /**
16401
+ * Even when the own instance is not applying,
16402
+ * we still listen to messages to ensure the hasLeader flag
16403
+ * is set correctly.
16404
+ */
16405
+ var hasLeaderListener = function hasLeaderListener(msg) {
16406
+ if (msg.context === 'leader') {
16407
+ if (msg.action === 'death') {
16408
+ _this._hasLeader = false;
16409
+ }
16410
+ if (msg.action === 'tell') {
16411
+ _this._hasLeader = true;
16412
+ }
16413
+ }
16414
+ };
16415
+ this.broadcastChannel.addEventListener('internal', hasLeaderListener);
16416
+ this._lstns.push(hasLeaderListener);
16417
+ };
16418
+ LeaderElection.prototype = {
16419
+ hasLeader: function hasLeader() {
16420
+ return Promise.resolve(this._hasLeader);
16421
+ },
16422
+ /**
16423
+ * Returns true if the instance is leader,
16424
+ * false if not.
16425
+ * @async
16426
+ */
16427
+ applyOnce: function applyOnce(
16428
+ // true if the applyOnce() call came from the fallbackInterval cycle
16429
+ isFromFallbackInterval) {
16430
+ var _this2 = this;
16431
+ if (this.isLeader) {
16432
+ return sleep(0, true);
16433
+ }
16434
+ if (this.isDead) {
16435
+ return sleep(0, false);
16436
+ }
16437
+
16438
+ /**
16439
+ * Already applying more than once,
16440
+ * -> wait for the apply queue to be finished.
16441
+ */
16442
+ if (this._aplQC > 1) {
16443
+ return this._aplQ;
16444
+ }
16445
+
16446
+ /**
16447
+ * Add a new apply-run
16448
+ */
16449
+ var applyRun = function applyRun() {
16450
+ /**
16451
+ * Optimization shortcuts.
16452
+ * Directly return if a previous run
16453
+ * has already elected a leader.
16454
+ */
16455
+ if (_this2.isLeader) {
16456
+ return PROMISE_RESOLVED_TRUE;
16457
+ }
16458
+ var stopCriteria = false;
16459
+ var stopCriteriaPromiseResolve;
16460
+ /**
16461
+ * Resolves when a stop criteria is reached.
16462
+ * Uses as a performance shortcut so we do not
16463
+ * have to await the responseTime when it is already clear
16464
+ * that the election failed.
16465
+ */
16466
+ var stopCriteriaPromise = new Promise(function (res) {
16467
+ stopCriteriaPromiseResolve = function stopCriteriaPromiseResolve() {
16468
+ stopCriteria = true;
16469
+ res();
16470
+ };
16471
+ });
16472
+ var handleMessage = function handleMessage(msg) {
16473
+ if (msg.context === 'leader' && msg.token != _this2.token) {
16474
+ if (msg.action === 'apply') {
16475
+ // other is applying
16476
+ if (msg.token > _this2.token) {
16477
+ /**
16478
+ * other has higher token
16479
+ * -> stop applying and let other become leader.
16480
+ */
16481
+ stopCriteriaPromiseResolve();
16482
+ }
16483
+ }
16484
+ if (msg.action === 'tell') {
16485
+ // other is already leader
16486
+ stopCriteriaPromiseResolve();
16487
+ _this2._hasLeader = true;
16488
+ }
16489
+ }
16490
+ };
16491
+ _this2.broadcastChannel.addEventListener('internal', handleMessage);
16492
+
16493
+ /**
16494
+ * If the applyOnce() call came from the fallbackInterval,
16495
+ * we can assume that the election runs in the background and
16496
+ * not critical process is waiting for it.
16497
+ * When this is true, we give the other instances
16498
+ * more time to answer to messages in the election cycle.
16499
+ * This makes it less likely to elect duplicate leaders.
16500
+ * But also it takes longer which is not a problem because we anyway
16501
+ * run in the background.
16502
+ */
16503
+ var waitForAnswerTime = isFromFallbackInterval ? _this2._options.responseTime * 4 : _this2._options.responseTime;
16504
+ return sendLeaderMessage(_this2, 'apply') // send out that this one is applying
16505
+ .then(function () {
16506
+ return Promise.race([sleep(waitForAnswerTime), stopCriteriaPromise.then(function () {
16507
+ return Promise.reject(new Error());
16508
+ })]);
16509
+ })
16510
+ // send again in case another instance was just created
16511
+ .then(function () {
16512
+ return sendLeaderMessage(_this2, 'apply');
16513
+ })
16514
+ // let others time to respond
16515
+ .then(function () {
16516
+ return Promise.race([sleep(waitForAnswerTime), stopCriteriaPromise.then(function () {
16517
+ return Promise.reject(new Error());
16518
+ })]);
16519
+ })["catch"](function () {}).then(function () {
16520
+ _this2.broadcastChannel.removeEventListener('internal', handleMessage);
16521
+ if (!stopCriteria) {
16522
+ // no stop criteria -> own is leader
16523
+ return beLeader(_this2).then(function () {
16524
+ return true;
16525
+ });
16526
+ } else {
16527
+ // other is leader
16528
+ return false;
16529
+ }
16530
+ });
16531
+ };
16532
+ this._aplQC = this._aplQC + 1;
16533
+ this._aplQ = this._aplQ.then(function () {
16534
+ return applyRun();
16535
+ }).then(function () {
16536
+ _this2._aplQC = _this2._aplQC - 1;
16537
+ });
16538
+ return this._aplQ.then(function () {
16539
+ return _this2.isLeader;
16540
+ });
16541
+ },
16542
+ awaitLeadership: function awaitLeadership() {
16543
+ if ( /* _awaitLeadershipPromise */
16544
+ !this._aLP) {
16545
+ this._aLP = _awaitLeadershipOnce(this);
16546
+ }
16547
+ return this._aLP;
16548
+ },
16549
+ set onduplicate(fn) {
16550
+ this._dpL = fn;
16551
+ },
16552
+ die: function die() {
16553
+ var _this3 = this;
16554
+ this._lstns.forEach(function (listener) {
16555
+ return _this3.broadcastChannel.removeEventListener('internal', listener);
16556
+ });
16557
+ this._lstns = [];
16558
+ this._unl.forEach(function (uFn) {
16559
+ return uFn.remove();
16560
+ });
16561
+ this._unl = [];
16562
+ if (this.isLeader) {
16563
+ this._hasLeader = false;
16564
+ this.isLeader = false;
16565
+ }
16566
+ this.isDead = true;
16567
+ return sendLeaderMessage(this, 'death');
16568
+ }
16569
+ };
16570
+
16571
+ /**
16572
+ * @param leaderElector {LeaderElector}
16573
+ */
16574
+ function _awaitLeadershipOnce(leaderElector) {
16575
+ if (leaderElector.isLeader) {
16576
+ return PROMISE_RESOLVED_VOID;
16577
+ }
16578
+ return new Promise(function (res) {
16579
+ var resolved = false;
16580
+ function finish() {
16581
+ if (resolved) {
16582
+ return;
16583
+ }
16584
+ resolved = true;
16585
+ leaderElector.broadcastChannel.removeEventListener('internal', whenDeathListener);
16586
+ res(true);
16587
+ }
16588
+
16589
+ // try once now
16590
+ leaderElector.applyOnce().then(function () {
16591
+ if (leaderElector.isLeader) {
16592
+ finish();
16593
+ }
16594
+ });
16595
+
16596
+ /**
16597
+ * Try on fallbackInterval
16598
+ * @recursive
16599
+ */
16600
+ var tryOnFallBack = function tryOnFallBack() {
16601
+ return sleep(leaderElector._options.fallbackInterval).then(function () {
16602
+ if (leaderElector.isDead || resolved) {
16603
+ return;
16604
+ }
16605
+ if (leaderElector.isLeader) {
16606
+ finish();
16607
+ } else {
16608
+ return leaderElector.applyOnce(true).then(function () {
16609
+ if (leaderElector.isLeader) {
16610
+ finish();
16611
+ } else {
16612
+ tryOnFallBack();
16613
+ }
16614
+ });
16615
+ }
16616
+ });
16617
+ };
16618
+ tryOnFallBack();
16619
+
16620
+ // try when other leader dies
16621
+ var whenDeathListener = function whenDeathListener(msg) {
16622
+ if (msg.context === 'leader' && msg.action === 'death') {
16623
+ leaderElector._hasLeader = false;
16624
+ leaderElector.applyOnce().then(function () {
16625
+ if (leaderElector.isLeader) {
16626
+ finish();
16627
+ }
16628
+ });
16629
+ }
16630
+ };
16631
+ leaderElector.broadcastChannel.addEventListener('internal', whenDeathListener);
16632
+ leaderElector._lstns.push(whenDeathListener);
16633
+ });
16634
+ }
16635
+ function fillOptionsWithDefaults(options, channel) {
16636
+ if (!options) options = {};
16637
+ options = JSON.parse(JSON.stringify(options));
16638
+ if (!options.fallbackInterval) {
16639
+ options.fallbackInterval = 3000;
16640
+ }
16641
+ if (!options.responseTime) {
16642
+ options.responseTime = channel.method.averageResponseTime(channel.options);
16643
+ }
16644
+ return options;
16645
+ }
16646
+ function createLeaderElection(channel, options) {
16647
+ if (channel._leaderElector) {
16648
+ throw new Error('BroadcastChannel already has a leader-elector');
16649
+ }
16650
+ options = fillOptionsWithDefaults(options, channel);
16651
+ var elector = supportsWebLockAPI() ? new LeaderElectionWebLock(channel, options) : new LeaderElection(channel, options);
16652
+ channel._befC.push(function () {
16653
+ return elector.die();
16654
+ });
16655
+ channel._leaderElector = elector;
16656
+ return elector;
16657
+ }
16658
+
16659
+ var index = /*#__PURE__*/Object.freeze({
16660
+ __proto__: null,
16661
+ BroadcastChannel: BroadcastChannel$1,
16662
+ clearNodeFolder: clearNodeFolder,
16663
+ enforceOptions: enforceOptions,
16664
+ OPEN_BROADCAST_CHANNELS: OPEN_BROADCAST_CHANNELS,
16665
+ createLeaderElection: createLeaderElection,
16666
+ beLeader: beLeader
16667
+ });
16668
+
15016
16669
  exports.BrickAsComponent = BrickAsComponent;
15017
16670
  exports.BrickAsComponentFactory = BrickAsComponentFactory;
15018
16671
  exports.BrickWrapper = BrickWrapper;