patronum
Version:
☄️ Effector utility library delivering modularity and convenience
434 lines (426 loc) • 13.3 kB
JavaScript
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
import { is, createNode, step, clearNode } from 'effector';
var defaultConfig = {
trace: false,
// default logger to console.info
handler: context => {
if (isEffectChild(context.node) && context.node.meta.named === 'finally') {
// skip effect.finally logs, it can be useful for other custom handlers
// but not for default console.info logger
return;
}
var {
scope,
scopeName,
name,
kind,
value,
loc,
trace,
node,
logType
} = context;
var scopeLog = scope ? " (scope: ".concat(scopeName, ")") : '';
var logName = name !== null && name !== void 0 ? name : loc ? "".concat(loc.file, ":").concat(loc.line, ":").concat(loc.column) : '';
var logPrintType = logType === 'initial' ? ' [getState]' : '';
console.info("[".concat(kind, "]").concat(scopeLog, " ").concat(logName).concat(logPrintType), value);
if (
// logging trace only if there is something to log
trace && trace.length > 0 &&
// do not log trace for effect children, as it is always the same effect internals
!isEffectChild(node)) {
console.groupCollapsed("[".concat(kind, "]").concat(scopeLog, " ").concat(logName, " trace"));
trace.forEach(update => {
var {
name: traceName,
kind,
value,
loc
} = update;
var logTraceName = traceName !== null && traceName !== void 0 ? traceName : loc ? "".concat(loc.file, ":").concat(loc.line, ":").concat(loc.column) : '';
console.info("<- [".concat(kind, "] ").concat(logTraceName), value);
});
console.groupEnd();
}
}
};
export function debug() {
var {
config,
units
} = resolveParams(...arguments);
units.forEach(unit => {
if (is.store(unit, {
sid: "-foynff"
}) || is.event(unit, {
sid: "apy28p"
}) || is.effect(unit, {
sid: "apy3od"
})) {
watchUnit(unit, config);
} else if (is.domain(unit, {
sid: "-rsqe9t"
})) {
watchDomain(unit, config);
} else {
/**
* Let unknown stuff pass through as noop
*
* It's useful for debug of custom entities:
* debug(myFarfetchedQuery)
*/
}
});
}
// Log node
function watchDomain(domain, config) {
domain.onCreateStore(store => watchUnit(store, config));
domain.onCreateEvent(event => watchUnit(event, config));
domain.onCreateEffect(effect => watchUnit(effect, config));
domain.onCreateDomain(domain => watchDomain(domain, config));
}
function watchUnit(unit, config) {
if (is.store(unit, {
sid: "w7ds2s"
})) {
// store has its initial/current value - we can log it right away
watchStoreInit(unit, config);
watch(unit, config);
} else if (is.event(unit, {
sid: "fretyd"
})) {
watch(unit, config);
} else if (is.effect(unit, {
sid: "gpi2qr"
})) {
watch(unit, config);
watch(unit.finally, config);
watch(unit.done, config);
watch(unit.fail, config);
}
}
function watch(unit, config) {
var watcher = createNode({
parent: [unit],
// debug watchers should behave like normal watchers
meta: {
op: 'watch'
},
family: {
owners: unit
},
regional: true,
// node only gets all required data
node: [step.run({
fn(value, _internal, stack) {
var _stack$scope;
var scope = (_stack$scope = stack === null || stack === void 0 ? void 0 : stack.scope) !== null && _stack$scope !== void 0 ? _stack$scope : null;
var context = {
logType: 'update',
scope,
scopeName: getScopeName(scope),
node: getNode(unit),
kind: getType(unit),
value,
name: getName(unit),
loc: getLoc(unit),
// Use stack meta of actual unit, not of debug node
stackMeta: getStackMeta(stack.parent),
trace: config.trace ? collectTrace(stack) : []
};
if (!config.handler) {
throw Error('patronum/debug must have the handler');
}
config.handler(context);
}
})]
});
return () => clearNode(watcher);
}
function collectTrace(stack) {
var trace = [];
var parent = stack === null || stack === void 0 ? void 0 : stack.parent;
while (parent) {
var {
node,
value
} = parent;
var entry = {
node,
value,
name: getName(node),
loc: getLoc(node),
kind: getType(node),
stackMeta: getStackMeta(parent)
};
trace.push(entry);
parent = parent.parent;
}
return trace;
}
function watchStoreInit(store, config) {
if (!config.handler) {
throw Error('patronum/debug must have the handler');
}
var node = getNode(store);
// current state
var context = {
logType: 'initial',
scope: null,
scopeName: null,
node,
kind: getType(store),
value: store.getState(),
name: getName(store),
loc: getLoc(store),
// nothing to trace for store.getState() - it is one-step call
trace: [],
// no stackMeta for initial state
stackMeta: {}
};
config.handler(context);
// current state in every known scope
scopes.forEach(scope => watchStoreInitInScope(store, config, scope));
// subscribe to new scopes
watchScopeRegister(newScope => watchStoreInitInScope(store, config, newScope));
}
function watchStoreInitInScope(store, config, scope) {
if (!config.handler) {
throw Error('patronum/debug must have the handler');
}
var node = getNode(store);
// current state
var context = {
logType: 'initial',
scope,
scopeName: getScopeName(scope),
node,
kind: getType(store),
value: scope.getState(store),
name: getName(store),
loc: getLoc(store),
// nothing to trace for scope.getState(store) - it is one-step call
trace: [],
// no stackMeta for initial state
stackMeta: {}
};
config.handler(context);
}
// Config
function resolveParams() {
var config = defaultConfig;
for (var _len = arguments.length, entry = new Array(_len), _key = 0; _key < _len; _key++) {
entry[_key] = arguments[_key];
}
var [maybeConfig, ...restUnits] = entry;
var units = [];
if (isConfig(maybeConfig)) {
config = _objectSpread(_objectSpread({}, defaultConfig), maybeConfig);
} else if (!is.unit(maybeConfig)) {
for (var [name, unit] of Object.entries(maybeConfig)) {
customNames.set(getGraph(unit).id, name);
units.push(unit);
}
} else {
units.push(maybeConfig);
}
for (var maybeUnit of restUnits) {
if (is.unit(maybeUnit)) {
units.push(maybeUnit);
} else {
for (var [_name, _unit] of Object.entries(maybeUnit)) {
customNames.set(getGraph(_unit).id, _name);
units.push(_unit);
}
}
}
return {
config,
units
};
}
function isConfig(maybeConfig) {
if (!is.unit(maybeConfig)) {
return !Object.values(maybeConfig).every(is.unit);
}
return false;
}
// Scopes
var watchers = new Set();
var watchScopeRegister = cb => {
watchers.add(cb);
return () => {
watchers.delete(cb);
};
};
function registerScope(scope, config) {
scopes.save(scope, {
name: config.name
});
watchers.forEach(cb => cb(scope));
return () => {
scopes.delete(scope);
};
}
function unregisterAllScopes() {
scopes.clear();
}
var unknownScopes = 0;
function getDefaultScopeName() {
unknownScopes += 1;
return "unknown_".concat(unknownScopes);
}
var cache = new Map();
var scopes = {
save(scope, meta) {
if (!scopes.get(scope)) {
cache.set(scope, meta);
}
},
get(scope) {
var _cache$get;
if (!scope) return null;
return (_cache$get = cache.get(scope)) !== null && _cache$get !== void 0 ? _cache$get : null;
},
delete(scope) {
cache.delete(scope);
},
forEach(callback) {
cache.forEach((meta, scope) => callback(scope, meta));
},
clear() {
cache.clear();
}
};
debug.registerScope = registerScope;
debug.unregisterAllScopes = unregisterAllScopes;
function getScopeName(scope) {
if (!scope) return null;
var meta = scopes.get(scope);
if (!meta) {
// @ts-expect-error
var fallbackId = scope._debugId || (scope._debugId = getDefaultScopeName());
return fallbackId;
}
return meta.name;
}
// Utils
function isEffectChild(node) {
var actualNode = getNode(node);
var {
sid,
named
} = actualNode.meta;
return Boolean(!sid && (named === 'finally' || named === 'done' || named === 'doneData' || named === 'fail' || named === 'failData' || named === 'inFlight' || named === 'pending'));
}
function isStoreOn(node) {
var actualNode = getNode(node);
var {
op
} = actualNode.meta;
if (op === 'on') return true;
return false;
}
function getType(unit) {
if (is.store(unit, {
sid: "4o7s04"
})) {
return 'store';
}
if (is.effect(unit, {
sid: "4pv5s7"
}) || isEffectChild(unit)) {
return 'effect';
}
if (is.event(unit, {
sid: "4rijka"
})) {
return 'event';
}
if (is.domain(unit, {
sid: "54plsy"
})) {
return 'domain';
}
if (is.unit(unit)) {
return 'unit';
}
var node = getNode(unit);
if (node.meta.op) {
return node.meta.op;
}
return 'unknown';
}
var getGraph = graph => graph.graphite || graph;
var customNames = new Map();
function getName(unit) {
var _getNode, _getNode$meta;
var custom = customNames.get(getGraph(unit).id);
if (custom) {
return custom;
}
if (isEffectChild(unit)) {
var node = getNode(unit);
var parentEffect = node.family.owners.find(n => n.meta.op === 'effect');
if (parentEffect) {
var closestParentDomainName = getOwningDomainName(parentEffect);
var formattedDomainName = closestParentDomainName ? "".concat(closestParentDomainName, "/") : '';
return "".concat(formattedDomainName).concat(getName(parentEffect), ".").concat(node.meta.named);
}
return node.meta.named;
}
if (isStoreOn(unit)) {
var _node = getNode(unit);
var targetStoreName = getName(_node.next[0]);
var triggerEventName = getName(_node.family.owners[0]);
return "".concat(targetStoreName, ".on(").concat(triggerEventName, ")");
}
if (is.unit(unit)) {
var _compositeName;
if (unit !== null && unit !== void 0 && (_compositeName = unit.compositeName) !== null && _compositeName !== void 0 && _compositeName.fullName) {
return unit.compositeName.fullName;
}
var _closestParentDomainName = getOwningDomainName(unit);
var _formattedDomainName = _closestParentDomainName ? "".concat(_closestParentDomainName, "/") : '';
if (unit !== null && unit !== void 0 && unit.shortName) {
return "".concat(_formattedDomainName).concat(unit.shortName);
}
if (unit !== null && unit !== void 0 && unit.name) {
return "".concat(_formattedDomainName).concat(unit.name);
}
}
if ((_getNode = getNode(unit)) !== null && _getNode !== void 0 && (_getNode$meta = _getNode.meta) !== null && _getNode$meta !== void 0 && _getNode$meta.name) {
return getNode(unit).meta.name;
}
return null;
}
function getOwningDomainName(unit) {
var closestParentDomain = getNode(unit).family.owners.find(n => n.meta.op === 'domain');
if (!closestParentDomain) return null;
return getName(closestParentDomain);
}
function readLoc(_ref) {
var {
meta
} = _ref;
var loc = 'config' in meta ? meta.config.loc : meta.loc;
return loc;
}
function getLoc(unit) {
var loc = readLoc(getNode(unit));
if (!loc) return undefined;
return loc;
}
function getNode(node) {
var actualNode = 'graphite' in node ? node.graphite : node;
return actualNode;
}
function getStackMeta(stack) {
if (!stack) return {};
var meta = stack.meta || {};
return meta;
}