reactant-share
Version:
A framework for building shared web applications with Reactant
1,102 lines (1,074 loc) • 123 kB
JavaScript
import { identifierKey, storeKey, injectable, inject, actionIdentifier, optional, PluginModule, modulesKey, containerKey, applyPatches, ModuleRef, createApp, watch, stateKey, state, action, getRef, nameKey } from 'reactant';
export * from 'reactant';
import { createTransport, mockPorts } from 'data-transport';
export * from 'data-transport';
import { LastAction, LastActionOptions } from 'reactant-last-action';
export * from 'reactant-last-action';
import { LOCATION_CHANGE, RouterOptions, Router } from 'reactant-router';
export { RouterOptions, createBrowserHistory, createHashHistory, createMemoryHistory } from 'reactant-router';
import { StorageOptions, Storage } from 'reactant-storage';
export { REHYDRATE, StorageOptions, getRehydrated } from 'reactant-storage';
import { BroadcastChannel } from 'broadcast-channel';
// Client to Server
var proxyClientActionName = '@@reactant:proxyClient';
var preloadedStateActionName = '@@reactant:preloadedState';
var isClientName = '@@reactant:isClient';
var loadFullStateActionName = '@@reactant:loadFullState';
var syncRouterName = '@@reactant:syncRouter';
var syncClientIdToServerName = '@@reactant:syncClientIdToServer';
var removeClientIdToServerName = '@@reactant:removeClientIdToServer';
// Server to Client
var proxyServerActionName = '@@reactant:proxyServer';
var lastActionName = '@@reactant:lastAction';
var syncToClientsName = '@@reactant:syncToClients';
var syncWorkerRouterName = '@@reactant:syncWorkerRouter';
var syncClientIdsFromClientsName = '@@reactant:syncClientIdsFromClients';
var SharedAppOptions = Symbol('SharedAppOptions');
var storageModuleName = 'Storage';
var routerModuleName = 'Router';
// Coworker
var proxyExecutorKey = Symbol('proxyExecutor');
var proxyWorkerExecuteName = '@@reactant:coworkerProxyWorkerExecute';
var syncStateName = '@@reactant:coworkerSyncState';
var requestSyncAllStateName = '@@reactant:coworkerRequestSyncAllState';
var pushAllStateName = '@@reactant:coworkerPushAllState';
var coworkerKey = Symbol('coworker');
// Redux action types
var syncStateActionName = '@@reactant:syncState';
var syncModuleStateActionName = '@@reactant:syncModuleState';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function __decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function __param(paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
}
function __metadata(metadataKey, metadataValue) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
}
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
function __values(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
}
function __read(o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
}
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
var createId = function () { return Math.random().toString(36).slice(2); };
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
var isSharedWorker = !!globalThis.SharedWorkerGlobalScope;
/**
* Port Detector
*
* It provides port detection and client/server port switching functions.
*/
var PortDetector = /** @class */ (function () {
function PortDetector(sharedAppOptions, lastAction) {
var _this = this;
this.sharedAppOptions = sharedAppOptions;
this.lastAction = lastAction;
this.serverCallbacks = new Set();
this.clientCallbacks = new Set();
this.clientDestroyCallbacks = new Set();
/**
* client id, it will be generated when the port is client, it is null in server port.
*/
this.clientId = null;
/**
* allow Disable Sync
*/
this.allowDisableSync = function () { return true; };
/**
* client ids, it will collect all the client ids when the port is server, it is an empty array in client port.
*/
this.clientIds = [];
/**
* server hooks for delegate(this, key, args, { _extra: { serverHook: '$hookName' } }) method
*/
this.serverHooks = {};
this.isolatedModules = [];
/**
* onServer
*
* When the port is server, this hook will execute.
* And allow to return a function that will be executed when the current port is switched to client.
*/
this.onServer = function (callback) {
if (typeof callback !== 'function') {
throw new Error("'onServer' argument should be a function.");
}
_this.serverCallbacks.add(callback);
if (_this.lastHooks &&
_this.lastHooks.size > 0 &&
_this.isServer &&
_this.transport) {
try {
var hook = callback(_this.transport);
_this.lastHooks.add(hook);
}
catch (e) {
console.error(e);
}
}
return function () {
_this.serverCallbacks.delete(callback);
};
};
/**
* onClient
*
* When the port is client, this hook will execute.
* And allow to return a function that will be executed when the current port is switched to server.
*/
this.onClient = function (callback) {
if (typeof callback !== 'function') {
throw new Error("'onClient' argument should be a function.");
}
_this.clientCallbacks.add(callback);
if (_this.lastHooks &&
_this.lastHooks.size > 0 &&
_this.isClient &&
_this.transport) {
try {
var hook = callback(_this.transport);
_this.lastHooks.add(hook);
}
catch (e) {
console.error(e);
}
}
return function () {
_this.clientCallbacks.delete(callback);
};
};
/**
* emit client destroy event with clientId
*/
this.onClientDestroy = function (callback) {
if (typeof callback !== 'function') {
throw new Error("'onClientDestroy' argument should be a function.");
}
_this.clientDestroyCallbacks.add(callback);
return function () {
_this.clientDestroyCallbacks.delete(callback);
};
};
this.onClient(function (transport) {
_this.clientId = createId();
_this.clientIds = [];
_this.syncFullState({ forceSync: false });
var disposeSyncToClients = transport.listen(syncToClientsName, function (fullState) { return __awaiter(_this, void 0, void 0, function () {
var store;
return __generator(this, function (_a) {
if (!fullState)
return [2 /*return*/];
store = this[storeKey];
store.dispatch({
type: "".concat(actionIdentifier, "_").concat(loadFullStateActionName),
state: this.getNextState(fullState),
_reactant: actionIdentifier,
});
this.lastAction.sequence =
fullState[this.lastAction.stateKey]._sequence;
return [2 /*return*/];
});
}); });
transport.emit({ name: syncClientIdToServerName, respond: false }, _this.clientId);
var disposeSyncClientIds = transport.listen(syncClientIdsFromClientsName, function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
if (this.clientId) {
// for all clients send current client id to server
transport.emit({ name: syncClientIdToServerName, respond: false }, this.clientId);
}
return [2 /*return*/];
});
}); });
var removeClientIdToServer = function () {
transport.emit({ name: removeClientIdToServerName, respond: false }, _this.clientId);
};
// do not use `unload` event
// https://developer.chrome.com/docs/web-platform/deprecating-unload
// the pagehide event is just only triggered in shared worker mode
window.addEventListener('pagehide', removeClientIdToServer);
return function () {
_this.previousPort = 'client';
disposeSyncToClients === null || disposeSyncToClients === void 0 ? void 0 : disposeSyncToClients();
disposeSyncClientIds === null || disposeSyncClientIds === void 0 ? void 0 : disposeSyncClientIds();
window.removeEventListener('pagehide', removeClientIdToServer);
};
});
this.onServer(function (transport) {
_this.clientId = null;
transport.emit({ name: syncClientIdsFromClientsName, respond: false });
var disposeSyncClientId = transport.listen(syncClientIdToServerName, function (clientId) {
if (!_this.clientIds.includes(clientId)) {
_this.clientIds.push(clientId);
}
});
var disposeRemoveClientId = transport.listen(removeClientIdToServerName, function (clientId) {
var e_1, _a;
var index = _this.clientIds.findIndex(function (id) { return id === clientId; });
if (index !== -1) {
_this.clientIds.splice(index, 1);
var callbacks = _this.clientDestroyCallbacks;
try {
for (var callbacks_1 = __values(callbacks), callbacks_1_1 = callbacks_1.next(); !callbacks_1_1.done; callbacks_1_1 = callbacks_1.next()) {
var callback = callbacks_1_1.value;
try {
callback(clientId);
}
catch (e) {
console.error(e);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (callbacks_1_1 && !callbacks_1_1.done && (_a = callbacks_1.return)) _a.call(callbacks_1);
}
finally { if (e_1) throw e_1.error; }
}
}
});
return function () {
_this.previousPort = 'server';
disposeSyncClientId === null || disposeSyncClientId === void 0 ? void 0 : disposeSyncClientId();
disposeRemoveClientId === null || disposeRemoveClientId === void 0 ? void 0 : disposeRemoveClientId();
};
});
}
/**
* all isolated instances state will not be sync to other clients or server.
*/
PortDetector.prototype.disableShare = function (instance) {
if (process.env.NODE_ENV !== 'production') {
if (!this.shared) {
console.warn("The app is not shared, so it cannot be isolated.");
}
if (this.isolatedModules.includes(instance)) {
console.warn("This module \"".concat(instance.constructor.name, "\" has been disabled for state sharing."));
}
}
this.isolatedModules = this.isolatedModules.concat(instance);
};
Object.defineProperty(PortDetector.prototype, "isolatedInstanceKeys", {
get: function () {
var _a;
if (this.lastIsolatedInstances !== this.isolatedModules) {
this.lastIsolatedInstanceKeys = this.isolatedModules.map(function (instance) { return instance[identifierKey]; });
}
return (_a = this.lastIsolatedInstanceKeys) !== null && _a !== void 0 ? _a : [];
},
enumerable: false,
configurable: true
});
PortDetector.prototype.hasIsolatedState = function (key) {
return this.isolatedInstanceKeys.includes(key);
};
Object.defineProperty(PortDetector.prototype, "id", {
get: function () {
var _a;
return (_a = this.clientId) !== null && _a !== void 0 ? _a : '__SERVER__';
},
enumerable: false,
configurable: true
});
Object.defineProperty(PortDetector.prototype, "shared", {
get: function () {
return !!(this.sharedAppOptions.port && this.sharedAppOptions.type);
},
enumerable: false,
configurable: true
});
Object.defineProperty(PortDetector.prototype, "name", {
get: function () {
var _a;
return (_a = this.sharedAppOptions.portName) !== null && _a !== void 0 ? _a : 'default';
},
enumerable: false,
configurable: true
});
Object.defineProperty(PortDetector.prototype, "disableSyncClient", {
get: function () {
return (document.visibilityState === 'hidden' &&
!this.sharedAppOptions.forcedSyncClient &&
this.allowDisableSync());
},
enumerable: false,
configurable: true
});
PortDetector.prototype.detectPort = function (port) {
var _a;
return (_a = this.portApp) === null || _a === void 0 ? void 0 : _a[port];
};
Object.defineProperty(PortDetector.prototype, "isWorkerMode", {
get: function () {
return this.sharedAppOptions.type === 'SharedWorker';
},
enumerable: false,
configurable: true
});
Object.defineProperty(PortDetector.prototype, "isServerWorker", {
get: function () {
return this.isWorkerMode && this.isServer;
},
enumerable: false,
configurable: true
});
Object.defineProperty(PortDetector.prototype, "isServer", {
get: function () {
return !!this.detectPort('server');
},
enumerable: false,
configurable: true
});
Object.defineProperty(PortDetector.prototype, "isClient", {
get: function () {
return !!this.detectPort('client');
},
enumerable: false,
configurable: true
});
Object.defineProperty(PortDetector.prototype, "transports", {
get: function () {
var _a;
return (_a = this.sharedAppOptions.transports) !== null && _a !== void 0 ? _a : {};
},
enumerable: false,
configurable: true
});
PortDetector.prototype.setPort = function (currentPortApp, transport) {
var e_2, _a, e_3, _b;
this.transport = transport;
if (this.lastHooks) {
try {
for (var _c = __values(this.lastHooks), _d = _c.next(); !_d.done; _d = _c.next()) {
var hook = _d.value;
try {
hook === null || hook === void 0 ? void 0 : hook();
}
catch (e) {
console.error(e);
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_2) throw e_2.error; }
}
}
this.lastHooks = new Set();
this.portApp = currentPortApp;
var callbacks = this.isClient
? this.clientCallbacks
: this.serverCallbacks;
try {
for (var callbacks_2 = __values(callbacks), callbacks_2_1 = callbacks_2.next(); !callbacks_2_1.done; callbacks_2_1 = callbacks_2.next()) {
var callback = callbacks_2_1.value;
try {
var hook = callback(transport);
this.lastHooks.add(hook);
}
catch (e) {
console.error(e);
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (callbacks_2_1 && !callbacks_2_1.done && (_b = callbacks_2.return)) _b.call(callbacks_2);
}
finally { if (e_3) throw e_3.error; }
}
};
PortDetector.prototype.syncToClients = function () {
var _a;
var store = this[storeKey];
if (this.transports.server) {
(_a = this.transports.server) === null || _a === void 0 ? void 0 : _a.emit({ name: syncToClientsName, respond: false }, store.getState());
}
else {
throw new Error("Failed to 'syncToClients()', 'transports.server' does not exist.");
}
};
PortDetector.prototype.syncFullState = function () {
return __awaiter(this, arguments, void 0, function (_a) {
var fullState, store;
var _b = _a === void 0 ? {} : _a, _c = _b.forceSync, forceSync = _c === void 0 ? true : _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
if (forceSync) {
this.syncFullStatePromise = undefined;
}
if (!this.syncFullStatePromise) return [3 /*break*/, 2];
return [4 /*yield*/, this.syncFullStatePromise];
case 1:
_d.sent();
return [2 /*return*/];
case 2:
if (typeof this.transports.client === 'undefined') {
throw new Error("The current client transport does not exist.");
}
this.syncFullStatePromise = this.transports.client.emit(loadFullStateActionName, !forceSync ? this.lastAction.sequence : -1);
return [4 /*yield*/, this.syncFullStatePromise];
case 3:
fullState = _d.sent();
this.syncFullStatePromise = undefined;
if (typeof fullState === 'undefined') {
throw new Error("Failed to sync full state from server port.");
}
if (fullState === null ||
(!forceSync &&
this.lastAction.sequence >
fullState[this.lastAction.stateKey]._sequence))
return [2 /*return*/];
store = this[storeKey];
if (process.env.NODE_ENV !== 'production') {
console.log('[syncFullState]', 'old sequence:', this.lastAction.sequence, 'new sequence:', fullState[this.lastAction.stateKey]._sequence);
}
store.dispatch({
type: "".concat(actionIdentifier, "_").concat(loadFullStateActionName),
state: this.getNextState(fullState),
_reactant: actionIdentifier,
});
this.lastAction.sequence = fullState[this.lastAction.stateKey]._sequence;
return [2 /*return*/];
}
});
});
};
/**
* ignore router state and isolated state sync for last action
*/
PortDetector.prototype.getNextState = function (fullState) {
var store = this[storeKey];
var currentFullState = store.getState();
var nextState = __assign(__assign({}, fullState), { router: currentFullState.router });
if (this.isolatedInstanceKeys.length) {
this.isolatedInstanceKeys.forEach(function (key) {
if (key) {
nextState[key] = currentFullState[key];
}
});
}
return nextState;
};
/**
* transform port with new transport
*/
PortDetector.prototype.transform = function (port, transport) {
if (port !== 'server' && port !== 'client') {
throw new Error("The port '".concat(port, "' is not supported."));
}
this.sharedAppOptions.transports[port] =
transport !== null && transport !== void 0 ? transport : this.sharedAppOptions.transports[port];
this.sharedAppOptions.transform(port);
};
var _a;
PortDetector = __decorate([
injectable(),
__param(0, inject(SharedAppOptions)),
__metadata("design:paramtypes", [Object, typeof (_a = typeof LastAction !== "undefined" && LastAction) === "function" ? _a : Object])
], PortDetector);
return PortDetector;
}());
var ReactantStorage = /** @class */ (function (_super) {
__extends(ReactantStorage, _super);
function ReactantStorage(portDetector, options) {
var _this = _super.call(this, options) || this;
_this.portDetector = portDetector;
_this.options = options;
_this.onRehydrated(function () {
_this.portDetector.onServer(function () {
_this.persist();
});
_this.portDetector.onClient(function () {
_this.pause();
});
if (_this.portDetector.isServer) {
_this.portDetector.syncToClients();
}
});
return _this;
}
/**
* set module to storage persistent
*/
ReactantStorage.prototype.setStorage = function (target, options) {
return _super.prototype.setStorage.call(this, target, this.options.disableClientRehydrated && this.portDetector.isClient
? __assign(__assign({}, options), { blacklist: [], whitelist: [] }) : options);
};
ReactantStorage = __decorate([
injectable({
name: storageModuleName,
}),
__param(1, inject(StorageOptions)),
__metadata("design:paramtypes", [PortDetector, Object])
], ReactantStorage);
return ReactantStorage;
}(Storage));
var PatchesChecker = /** @class */ (function (_super) {
__extends(PatchesChecker, _super);
function PatchesChecker(portDetector, storage) {
var _this = _super.call(this) || this;
_this.portDetector = portDetector;
_this.storage = storage;
_this.middleware = function (store) { return function (next) { return function (_action) {
var _patches = _action._patches, type = _action.type, method = _action.method;
var hasIsolatedState;
_patches === null || _patches === void 0 ? void 0 : _patches.forEach(function (_a, index) {
var path = _a.path; _a.op; _a.value;
var _hasIsolatedState = _this.portDetector.hasIsolatedState("".concat(path[0]));
// ignore first patch
if (!index) {
hasIsolatedState = _hasIsolatedState;
}
else if (hasIsolatedState !== _hasIsolatedState) {
var methodName = "".concat(type, ".").concat(method);
throw new Error("Update state error: Mixed update of shared state and isolated state is not supported, please check method '".concat(methodName, "'."));
}
});
return next(_action);
}; }; };
if (process.env.NODE_ENV !== 'production' && _this.storage) {
_this.afterCreateStore = function (store) {
_this.storage.storageSettingMap.forEach(function (_, module) {
if (_this.portDetector.isolatedModules.includes(module)) {
throw new Error("The module \"".concat(module.constructor.name, "\" has been disabled for state sharing, its module state cannot be enabled for storage."));
}
});
return store;
};
}
return _this;
}
PatchesChecker.prototype.checkPatches = function (oldStateTree, options) {
options._patches.forEach(function (_a) {
var op = _a.op, path = _a.path, value = _a.value;
if (op === 'replace' &&
(toString.call(value) === '[object Object]' || Array.isArray(value))) {
var oldState = path.reduce(function (state, _path) { return state === null || state === void 0 ? void 0 : state[_path]; }, oldStateTree);
if (oldState &&
typeof oldState === 'object' &&
path[0] !== routerModuleName) {
var state = path.join('.');
console.warn("The state '".concat(state, "' operation in the method '").concat(options.method, "' of the module '").concat(String(options.type), "' is a replacement update operation, be sure to check the state '").concat(state, "' update operation and use mutation updates to ensure the minimum set of update patches."));
}
}
});
};
PatchesChecker = __decorate([
injectable(),
__param(1, optional()),
__metadata("design:paramtypes", [PortDetector,
ReactantStorage])
], PatchesChecker);
return PatchesChecker;
}(PluginModule));
var applyMethod = function (app, options) {
var module = app.instance[modulesKey][options.module];
if (!module) {
throw new Error("The module '".concat(options.module, "' is not a multiple instances injected module, and it does not exist."));
}
var method = module[options.method];
if (typeof method !== 'function') {
throw new Error("The '".concat(options.method, "' method for module '").concat(options.module, "' does not exist."));
}
// If the method in main thread and use coworker, it should be proxied for execution to a coworker thread.
if (module[proxyExecutorKey]) {
return module[proxyExecutorKey](options);
}
return method.apply(module, options.args);
};
var handleServer = function (_a) {
var _b;
var app = _a.app, transport = _a.transport, disposeServer = _a.disposeServer, disposeClient = _a.disposeClient, enablePatchesChecker = _a.enablePatchesChecker;
if (!transport) {
throw new Error("The server transport does not exist.");
}
disposeServer === null || disposeServer === void 0 ? void 0 : disposeServer();
disposeClient === null || disposeClient === void 0 ? void 0 : disposeClient();
var container = app.instance[containerKey];
var lastAction = container.get(LastAction);
var portDetector = container.get(PortDetector);
var patchesChecker = enablePatchesChecker
? container.get(PatchesChecker)
: null;
if (isSharedWorker) {
var executed_1 = false;
// before any other event, it should be connected with the first client
globalThis.addEventListener('connect', function () {
if (executed_1)
return;
executed_1 = true;
portDetector.setPort({ server: app }, transport);
});
}
else {
portDetector.setPort({ server: app }, transport);
}
var disposeListeners = [];
disposeListeners.push(transport.listen(isClientName, function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, true];
}); }); }));
disposeListeners.push(transport.listen(loadFullStateActionName, function (sequence) { return __awaiter(void 0, void 0, void 0, function () { var _a; return __generator(this, function (_b) {
return [2 /*return*/, lastAction.sequence > sequence ? (_a = app.store) === null || _a === void 0 ? void 0 : _a.getState() : null];
}); }); }));
disposeListeners.push(transport.listen(proxyClientActionName, function (options) { return __awaiter(void 0, void 0, void 0, function () {
var hook, result_1, sequence_1, result, sequence;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!options.hook) return [3 /*break*/, 2];
hook = portDetector.serverHooks[options.hook];
if (!(typeof hook === 'function')) return [3 /*break*/, 2];
return [4 /*yield*/, hook(options)];
case 1:
result_1 = _a.sent();
sequence_1 = portDetector.lastAction.sequence;
return [2 /*return*/, [sequence_1, result_1]];
case 2: return [4 /*yield*/, applyMethod(app, options)];
case 3:
result = _a.sent();
sequence = portDetector.lastAction.sequence;
return [2 /*return*/, [sequence, result]];
}
});
}); }));
disposeListeners.push(function () { return transport.dispose(); });
var oldStateTree;
if (process.env.NODE_ENV !== 'production') {
oldStateTree = app.store.getState();
}
disposeListeners.push((_b = app.store) === null || _b === void 0 ? void 0 : _b.subscribe(function () {
var _a, _b;
try {
if (lastAction.action) {
var action = lastAction.action;
if (!((_b = (_a = portDetector.lastAction.options) === null || _a === void 0 ? void 0 : _a.ignoreAction) === null || _b === void 0 ? void 0 : _b.call(_a, action))) {
if (process.env.NODE_ENV !== 'production' &&
enablePatchesChecker &&
patchesChecker &&
(action === null || action === void 0 ? void 0 : action._reactant) === actionIdentifier &&
action._patches) {
patchesChecker.checkPatches(oldStateTree, action);
}
transport.emit({ name: lastActionName, respond: false }, action);
}
}
}
finally {
if (process.env.NODE_ENV !== 'production') {
oldStateTree = app.store.getState();
}
}
}));
// app synchronizes state to all clients immediately after switching server port
if (portDetector.previousPort === 'client') {
portDetector.syncToClients();
}
return function () {
var e_1, _a;
try {
for (var disposeListeners_1 = __values(disposeListeners), disposeListeners_1_1 = disposeListeners_1.next(); !disposeListeners_1_1.done; disposeListeners_1_1 = disposeListeners_1.next()) {
var dispose = disposeListeners_1_1.value;
dispose === null || dispose === void 0 ? void 0 : dispose();
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (disposeListeners_1_1 && !disposeListeners_1_1.done && (_a = disposeListeners_1.return)) _a.call(disposeListeners_1);
}
finally { if (e_1) throw e_1.error; }
}
};
};
var handleClient = function (_a) {
var app = _a.app, transport = _a.transport, disposeServer = _a.disposeServer, disposeClient = _a.disposeClient, enablePatchesFilter = _a.enablePatchesFilter, preloadedState = _a.preloadedState;
if (!transport) {
throw new Error("The client transport does not exist.");
}
disposeServer === null || disposeServer === void 0 ? void 0 : disposeServer();
disposeClient === null || disposeClient === void 0 ? void 0 : disposeClient();
var container = app.instance[containerKey];
var lastAction = container.get(LastAction);
var portDetector = container.get(PortDetector);
portDetector.setPort({ client: app }, transport);
var disposeListeners = [];
if (preloadedState) {
lastAction.sequence = preloadedState[lastAction.stateKey]._sequence;
}
disposeListeners.push(transport.listen(lastActionName, function (action) { return __awaiter(void 0, void 0, void 0, function () {
var currentState_1, _patches, patches, state;
var _a;
return __generator(this, function (_b) {
if (portDetector.disableSyncClient)
return [2 /*return*/];
if (action._sequence && action._sequence === lastAction.sequence + 1) {
if (action._reactant === actionIdentifier) {
currentState_1 = app.store.getState();
_patches = (_a = action._patches) !== null && _a !== void 0 ? _a : [];
patches = enablePatchesFilter
? _patches.filter(function (item) { return currentState_1[item.path[0]]; })
: _patches;
state = applyPatches(currentState_1, patches);
app.store.dispatch(__assign(__assign({}, action), { state: state }));
}
else {
app.store.dispatch(action);
}
lastAction.sequence = action._sequence;
}
else {
portDetector.syncFullState({ forceSync: false });
}
return [2 /*return*/];
});
}); }));
disposeListeners.push(transport.listen(proxyServerActionName, function (_a) { return __awaiter(void 0, void 0, void 0, function () {
var result;
var clientIds = _a.clientIds, portName = _a.portName, options = __rest(_a, ["clientIds", "portName"]);
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
// ignore non-specified id or name of client
if ((Array.isArray(clientIds) &&
portDetector.clientId &&
!clientIds.includes(portDetector.clientId)) ||
(portName && portName !== portDetector.name))
return [2 /*return*/, new Promise(function () {
// never resolve
})];
return [4 /*yield*/, applyMethod(app, options)];
case 1:
result = _b.sent();
return [2 /*return*/, result];
}
});
}); }));
disposeListeners.push(function () { return transport.dispose(); });
return function () {
var e_1, _a;
try {
for (var disposeListeners_1 = __values(disposeListeners), disposeListeners_1_1 = disposeListeners_1.next(); !disposeListeners_1_1.done; disposeListeners_1_1 = disposeListeners_1.next()) {
var dispose = disposeListeners_1_1.value;
dispose === null || dispose === void 0 ? void 0 : dispose();
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (disposeListeners_1_1 && !disposeListeners_1_1.done && (_a = disposeListeners_1.return)) _a.call(disposeListeners_1);
}
finally { if (e_1) throw e_1.error; }
}
};
};
var createBroadcastTransport = function (name, verbose, logger) {
var broadcastChannel = new BroadcastChannel("reactant-share-channel:".concat(name));
var transport = createTransport('Base', {
listener: function (callback) {
broadcastChannel.onmessage = function (data) {
callback(JSON.parse(data));
};
return function () {
broadcastChannel.onmessage = null;
return broadcastChannel.close();
};
},
sender: function (message) { return broadcastChannel.postMessage(JSON.stringify(message)); },
prefix: "reactant-share:".concat(name),
verbose: verbose,
logger: logger,
});
return transport;
};
var lockMap = new Map();
var tabId = createId();
var lockStorageKey = 'reactant:lock';
var tabStorageKey = 'reactant:tab';
var heartbeatTimer;
var isListenUnload = false;
var storage;
var clearTabLocks = function (tabIds, _storage) {
if (tabIds.length === 0)
return;
Object.keys(localStorage).forEach(function (key) {
var _a;
if (!key.indexOf(lockStorageKey)) {
var lockQueue = JSON.parse((_a = localStorage.getItem(key)) !== null && _a !== void 0 ? _a : '[]');
var newValue = JSON.stringify(lockQueue.filter(function (item) { return tabIds.indexOf(item.tabId) === -1; }
// && localStorage.getItem(`${tabStorageKey}:${item.tabId}`)
));
_storage.setItem(key, newValue);
}
});
tabIds.forEach(function (_tabId) { return _storage.removeItem("".concat(tabStorageKey, ":").concat(_tabId)); });
};
var addUnloadListener = function () {
if (isListenUnload)
return;
isListenUnload = true;
/**
* After unload event, It is known that the clear of localStorage in some Firefox scenarios and Safari v10 does not execute properly.
*/
window.addEventListener('pagehide', function () {
// do not use `unload` event
// https://developer.chrome.com/docs/web-platform/deprecating-unload
clearTabLocks([tabId], localStorage);
});
};
var filterInvalidTabs = function () {
var invalidTabIds = [];
Object.keys(localStorage).forEach(function (key) {
var _a;
if (!key.indexOf(tabStorageKey)) {
var timestamp = localStorage.getItem(key);
// TODO: think about Wakeup
// Maximum is thread lock for 1 second + 1.99 second.
if (timestamp && Date.now() - Number(timestamp) > 2999) {
var expiredTabId = key.replace("".concat(tabStorageKey, ":"), '');
invalidTabIds.push(expiredTabId);
}
}
else if (!key.indexOf(lockStorageKey)) {
var lockQueue = JSON.parse((_a = localStorage.getItem(key)) !== null && _a !== void 0 ? _a : '[]');
lockQueue.forEach(function (item) {
if (!localStorage.getItem("".concat(tabStorageKey, ":").concat(item.tabId))) {
invalidTabIds.push(item.tabId);
}
});
}
});
return invalidTabIds;
};
var heartbeat = function () {
if (typeof heartbeatTimer === 'undefined') {
var tabHeartbeatKey_1 = "".concat(tabStorageKey, ":").concat(tabId);
storage.setItem(tabHeartbeatKey_1, Date.now().toString());
heartbeatTimer = window.setInterval(function () { return storage.setItem(tabHeartbeatKey_1, Date.now().toString()); }, 1000);
}
};
var createFrameStorage = function () {
var iframe = document.createElement('iframe');
iframe.src = 'about:blank';
iframe.setAttribute('style', 'display: none;');
document.body.appendChild(iframe);
storage = iframe.contentWindow.localStorage;
};
var simpleLock = function (name, callback) {
createFrameStorage();
addUnloadListener();
heartbeat();
return new Promise(function (resolve, reject) {
var _a;
var lockId = createId();
lockMap.set(name, (_a = lockMap.get(name)) !== null && _a !== void 0 ? _a : new Map());
lockMap.get(name).set(lockId, callback);
var storageKey = "".concat(lockStorageKey, ":").concat(name);
var oldStorageValue = localStorage.getItem(storageKey);
var lockQueue = JSON.parse(oldStorageValue !== null && oldStorageValue !== void 0 ? oldStorageValue : '[]');
lockQueue.push({ tabId: tabId, lockId: lockId });
var listener = function (event) { return __awaiter(void 0, void 0, void 0, function () {
var _a, lock, result, e_1, currentLockQueue;
var _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!(event.key === storageKey && event.newValue)) return [3 /*break*/, 5];
_a = __read(JSON.parse(event.newValue), 1), lock = _a[0];
if (!((lock === null || lock === void 0 ? void 0 : lock.tabId) === tabId && (lock === null || lock === void 0 ? void 0 : lock.lockId) === lockId)) return [3 /*break*/, 5];
window.removeEventListener('storage', listener);
_c.label = 1;
case 1:
_c.trys.push([1, 3, , 4]);
return [4 /*yield*/, lockMap.get(name).get(lockId)({
name: name,
mode: 'exclusive',
})];
case 2:
result = _c.sent();
resolve(result);
return [3 /*break*/, 4];
case 3:
e_1 = _c.sent();
reject(e_1);
return [3 /*break*/, 4];
case 4:
lockMap.get(name).delete(lockId);
currentLockQueue = JSON.parse((_b = localStorage.getItem(storageKey)) !== null && _b !== void 0 ? _b : '[]');
currentLockQueue.splice(0, 1);
storage.setItem(storageKey, JSON.stringify(currentLockQueue));
_c.label = 5;
case 5: return [2 /*return*/];
}
});
}); };
window.addEventListener('storage', listener);
storage.setItem(storageKey, JSON.stringify(lockQueue));
clearTabLocks(filterInvalidTabs(), storage);
});
};
var useLock = function (name, callback) {
var _a;
var isPrimitiveLock = !!((_a = navigator.locks) === null || _a === void 0 ? void 0 : _a.request);
if (isPrimitiveLock) {
return navigator.locks.request(name, callback).catch(function (e) {
if (e.code === 18 && e.toString().startsWith('SecurityError')) {
return simpleLock(name, callback);
}
return e;
});
}
return simpleLock(name, callback);
};
var IdentifierChecker = /** @class */ (function (_super) {
__extends(IdentifierChecker, _super);
function IdentifierChecker() {
var _this = _super.apply(this, __spreadArray([], __read(arguments), false)) || this;
_this.beforeCombineRootReducers = function (reducers) {
Object.keys(reducers).forEach(function (key) {
var _a = __read(key.split('/'), 2), prefix = _a[0], className = _a[1];
if (prefix === '@@reactant') {
console.error("The decorator for class ".concat(className, " should set \"@injectable({ name: '").concat(className, "' })\"."));
}
});
return reducers;
};
return _this;
}
IdentifierChecker = __decorate([
injectable()
], IdentifierChecker);
return IdentifierChecker;
}(PluginModule));
var createBaseApp = function (_a) {
var _b, _c, _d, _e;
var share = _a.share, options = __rest(_a, ["share"]);
(_b = options.modules) !== null && _b !== void 0 ? _b : (options.modules = []);
(_c = options.devOptions