@edirect/form-engine
Version:
Achieve form logic reusage with forms expressed in json format.
210 lines (208 loc) • 8.03 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Observer = exports.EXTRACT_EVENT_NAMESPACE = exports.BUILD_EVENT = exports.ALL_NAMESPACE_EVENTS = void 0;
var _index = require("../managers/index.js");
var _eventsTypes = require("./events.types.js");
var _ObserverError = require("./ObserverError.js");
var __awaiter = void 0 && (void 0).__awaiter || function (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());
});
};
var __classPrivateFieldGet = void 0 && (void 0).__classPrivateFieldGet || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _Observer_buildEventPayload;
/* eslint-disable @typescript-eslint/no-explicit-any */
const EXTRACT_EVENT_NAMESPACE = event => event.split('/')[1] || '';
exports.EXTRACT_EVENT_NAMESPACE = EXTRACT_EVENT_NAMESPACE;
const EXTRACT_CORE_NAMESPACE = event => Array.isArray(event.split('/')) ? event.split('/')[0] : event;
const BUILD_EVENT = (event, namespace, key) => {
if (!namespace) return event;
return event + '/' + namespace + (key ? '/' + key : '');
};
exports.BUILD_EVENT = BUILD_EVENT;
const ALL_NAMESPACE_EVENTS = event => new RegExp(`^(${event}).*$`, 'g');
exports.ALL_NAMESPACE_EVENTS = ALL_NAMESPACE_EVENTS;
class Observer {
constructor(namespace, enableLogging = false) {
this.regexBasedEvents = {};
this.events = {};
this.history = {};
_Observer_buildEventPayload.set(this, ({
event,
data,
payload
}) => {
const eventFieldName = EXTRACT_EVENT_NAMESPACE(event);
const form = (0, _index.getFormInstance)(this.namespace, data === null || data === void 0 ? void 0 : data.opts);
const field = form.fields[eventFieldName];
const coreEvent = EXTRACT_CORE_NAMESPACE(event);
return {
eventReducedSchema: field && field.eventReducedSchema(coreEvent),
formEventDirectives: form && form.eventReducedSchema(coreEvent),
namespace: this.namespace,
coreEvent,
form,
field,
event,
data,
payload
};
});
this.namespace = namespace;
this.enableLogging = enableLogging;
}
runForRegexBasedEvent(eventName, cb) {
let isMatchEvent = false;
for (const event of Object.keys(this.events)) {
isMatchEvent = eventName.test(event);
isMatchEvent && cb(event);
}
return isMatchEvent;
}
handleRegexSubscription(eventName, handler) {
const foundEvent = this.runForRegexBasedEvent(eventName, event => this.subscribe(event, handler));
if (!foundEvent) {
this.regexBasedEvents[eventName] = {
regex: eventName,
handlers: Array.isArray(this.events[eventName]) ? [...this.events[eventName], handler] : [handler]
};
}
}
/**
* This function lets you subscribe to a given event and register one callback to be called when someone published in it
*
* The callback you redister will, return you the published data and one function to unregister your callback from that event
*
*/
subscribe(eventName, handler) {
if (typeof eventName === 'object') {
this.handleRegexSubscription(eventName, handler);
return () => {};
}
this.events = Object.assign(Object.assign({}, this.events), {
[eventName]: Array.isArray(this.events[eventName]) ? [...this.events[eventName], handler] : [handler]
});
return () => {
this.unsubscribe(eventName, handler);
};
}
unsubscribe(eventName, handler) {
this.events = Object.assign(Object.assign({}, this.events), {
[eventName]: this.events[eventName].filter(eventFn => eventFn !== handler)
});
}
isAsyncFunction(fn) {
const string = fn.toString().trim();
return !!(fn[Symbol.toStringTag] === 'AsyncFunction' || fn.constructor.name == 'AsyncFunction' || string.match(/^async /) || string.match(/return _ref[^\\.]*\.apply/) || fn instanceof Promise);
}
/**
* Allows to publish data to a given event name
*
* Will iterate the subscriptions and call their handlers.
*
* When calling the handler, will also inject the unsubscribe function
*
* This methods also accepts one regex and will find the matchin events and
* publish in them
*
*/
publish(eventName, data) {
return __awaiter(this, void 0, void 0, function* () {
if ((data === null || data === void 0 ? void 0 : data.checksum) && this.history[eventName] === (data === null || data === void 0 ? void 0 : data.checksum)) return;
this.history[eventName] = data === null || data === void 0 ? void 0 : data.checksum;
if (typeof eventName === 'object') {
this.runForRegexBasedEvent(eventName, event => this.publish(event, data));
return;
}
for (const regexEvent of Object.keys(this.regexBasedEvents)) {
const isMatchEvent = new RegExp(this.regexBasedEvents[regexEvent].regex).test(eventName);
if (isMatchEvent) {
yield this.publishForEvents(eventName, data, {
[eventName]: this.regexBasedEvents[regexEvent].handlers
});
}
}
return this.publishForEvents(eventName, data, this.events);
});
}
publishForEvents(eventName, data = {}, events) {
return __awaiter(this, void 0, void 0, function* () {
if (!events[eventName]) return {};
let lastCallbackResult = {};
for (const fn of events[eventName]) {
try {
if (!fn) return;
const payload = __classPrivateFieldGet(this, _Observer_buildEventPayload, "f").call(this, {
data: Object.assign(Object.assign({}, data), lastCallbackResult),
payload: data,
event: eventName,
namespace: this.namespace
});
const isAsync = this.isAsyncFunction(fn);
const handlerRes = isAsync ? yield fn(payload, () => this.unsubscribe(eventName, fn)) : fn(payload, () => this.unsubscribe(eventName, fn));
lastCallbackResult = Object.assign(Object.assign({}, lastCallbackResult), handlerRes);
} catch (e) {
if (e instanceof _ObserverError.ObserverError && e.breaksObservingChain) {
this.logError(_eventsTypes.EFlowLogging.OBSERVER, eventName, 'publish', e);
return {};
}
throw e;
}
}
return Object.assign(Object.assign({}, lastCallbackResult), (0, _index.getFormInstance)(this.namespace).formData);
});
}
logError(flow, event, method, error) {
if (!this.enableLogging) return;
event !== "LOG" /* EEVents.LOG */ && this.publish("LOG" /* EEVents.LOG */, {
level: 'error',
data: {
event,
error
},
action: method,
flow
});
}
logInfo(flow, event, method, extraData) {
if (!this.enableLogging) return;
event !== "LOG" /* EEVents.LOG */ && this.publish("LOG" /* EEVents.LOG */, {
level: 'info',
data: Object.assign({
event
}, extraData),
action: method,
flow
});
}
}
exports.Observer = Observer;
_Observer_buildEventPayload = new WeakMap();