reactant-share
Version:
A framework for building shared web applications with Reactant
116 lines (113 loc) • 4.58 kB
JavaScript
import { __assign, __read } from './node_modules/tslib/tslib.es6.js';
import { containerKey, identifierKey } from 'reactant';
import { proxyExecutorKey, proxyClientActionName } from './constants.js';
import { PortDetector } from './modules/portDetector.js';
/**
* Proxy execute On the server side.
*
* ## Description
*
* `delegate()` is very similar to the actor model,
* which transfers the corresponding module method to the server thread for execution and returns the result as response.
*
* Note: It does not create new threads, it always runs on the server thread that has already been created.
*
* ## Example
*
* ```tsx
* import React from 'react';
* import { ViewModule, createApp, injectable, useConnector, action, state } from 'reactant';
* import { delegate } from 'reactant-share';
*
* @injectable({ name: 'counter'})
* class Counter {
* @state
* count = 0;
*
* @action
* increase() {
* this.count += 1;
* }
* }
*
* @injectable()
* export class AppView extends ViewModule {
* constructor(public counter: Counter) {
* super();
* }
*
* component() {
* const count = useConnector(() => this.counter.count);
* return (
* <button type="button" onClick={() => delegate(this.counter, 'increase', [])}>
* {count}
* </button>
* );
* }
* }
* ```
* reference: https://en.wikipedia.org/wiki/Actor_model
*/
var delegate = (function (module, key, args, options) {
var _a, _b;
if (options === void 0) { options = {}; }
var method = module[key];
var _args = args !== null && args !== void 0 ? args : [];
if (typeof key !== 'string') {
throw new Error("'delegate()' is valid only for method name with string type.");
}
if (typeof method !== 'function') {
throw new Error("The property '".concat(key, "'' must be a method in class '").concat(module.constructor.name, "'."));
}
if (!Array.isArray(_args)) {
throw new Error("The parameters of the method '".concat(key, "' must be an array."));
}
var target = module;
if ((_a = target[containerKey]) === null || _a === void 0 ? void 0 : _a.isBound(PortDetector)) {
var portDetector_1 = target[containerKey].get(PortDetector);
if (process.env.NODE_ENV !== 'production') {
var moduleName = target.constructor.name;
if (/^@@reactant/.test(target[identifierKey])) {
throw new Error("The identifier '".concat(target[identifierKey], "' is a temporary string, please set 'provide' for the module '").concat(moduleName, "' or the 'name' field of the module '").concat(moduleName, "'."));
}
}
// If the method in main thread and use coworker, it should be proxied for execution to a coworker thread.
if (target[proxyExecutorKey]) {
return target[proxyExecutorKey]({
module: target[identifierKey],
method: key,
args: _args,
});
}
// If the port is not a client, it just run the method in server port.
if (portDetector_1.isClient) {
if (!portDetector_1.transports.client) {
return Promise.reject(new Error("Detected that the current client transport does not exist."));
}
return portDetector_1.transports.client
.emit(__assign(__assign({}, options), { name: proxyClientActionName }), {
module: target[identifierKey],
method: key,
args: _args,
hook: (_b = options._extra) === null || _b === void 0 ? void 0 : _b.serverHook,
})
.then(function (response) {
// If the response is undefined, it means that the method is not executed.
if (!response)
return;
var _a = __read(response, 2), sequence = _a[0], result = _a[1];
if (portDetector_1.lastAction.sequence >= sequence) {
return result;
}
if (process.env.NODE_ENV !== 'production') {
console.warn("The sequence of the action is not consistent in ".concat(target[identifierKey], ".").concat(key, "."), sequence, portDetector_1.lastAction.sequence);
}
return portDetector_1
.syncFullState({ forceSync: false })
.then(function () { return result; });
});
}
}
return method.apply(target, _args);
});
export { delegate };