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