🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@kuindji/reactive

Package Overview
Dependencies
Maintainers
1
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@kuindji/reactive - npm Package Compare versions

Comparing version
1.3.0
to
1.3.1
+40
-6
dist/action.js

@@ -17,3 +17,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

let actionFn = action;
const { trigger, addListener, removeAllListeners, removeListener, updateListenerOptions, promise, destroy: destroyResponseEvent, } = createEvent();
const { trigger, addListener, removeAllListeners, removeListener, updateListenerOptions, promise, addErrorListener: addResponseErrorListener, destroy: destroyResponseEvent, } = createEvent();
const { all: triggerBeforeAction, addListener: addBeforeActionListener, removeAllListeners: removeAllBeforeActionListeners, removeListener: removeBeforeActionListener, promise: beforeActionPromise, destroy: destroyBeforeEvent, } = createEvent();

@@ -86,2 +86,36 @@ const { trigger: triggerError, addListener: addErrorListener, removeAllListeners: removeAllErrorListeners, removeListener: removeErrorListener, promise: errorPromise, hasListener: hasErrorListeners, destroy: destroyErrorEvent, } = createEvent();

const getStatus = () => currentStatus;
// Emit onto the action error event without letting a throwing error
// listener escape into the invoke lifecycle: an un-isolated throw here
// would (depending on the call site) reject an already-settled invoke with
// the wrong error, or skip the inFlight decrement and strand pending:true.
// Mirrors the isolation already applied to status-listener failures.
const reportError = (error, args, type = "action") => {
if (destroyed) {
return;
}
try {
triggerError({
error: error instanceof Error ? error : new Error(String(error)),
args,
type,
});
}
catch (_a) {
// A throwing error listener must not corrupt the invoke lifecycle;
// there is nothing left to route it to.
}
};
// A throwing or rejecting response (result) listener must not corrupt the
// invoke that emitted it: a successful invocation must still resolve with
// its result, and an async rejection must not surface as an unhandled
// rejection. Registering an error listener on the response event makes the
// event catch its listeners' failures (instead of re-throwing synchronously
// or leaving a floating rejected promise) and routes them to the action
// error event with a distinct "action-listener" type so consumers can tell
// a failed result listener apart from a failed action.
addResponseErrorListener((errorResponse) => {
var _a;
const settled = errorResponse.args[0];
reportError(errorResponse.error, (_a = settled === null || settled === void 0 ? void 0 : settled.args) !== null && _a !== void 0 ? _a : [], "action-listener");
});
const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {

@@ -168,7 +202,7 @@ if (destroyed) {

trigger(response);
triggerError({
error: lastError,
args: args,
type: "action",
});
// Isolated: a throwing error listener must not re-escape here
// (the failure is already handled, so invoke must resolve with
// the error response rather than reject with the listener's
// error). reportError swallows a throwing error listener.
reportError(lastError, args, "action");
}

@@ -175,0 +209,0 @@ return response;

+4
-3

@@ -148,4 +148,2 @@ import { createAction } from "./action.js";

}
options = options || {};
options.limit = 1;
const action = get(name);

@@ -155,3 +153,6 @@ if (!action) {

}
return action.addListener(handler, options);
// Spread rather than mutate: assigning limit onto the caller's options
// object would leak `limit: 1` into a shared options object reused for
// other subscriptions. Mirrors once() in event.ts / eventBus.ts.
return action.addListener(handler, Object.assign(Object.assign({}, (options || {})), { limit: 1 }));
};

@@ -158,0 +159,0 @@ const un = (name, handler, context, tag) => {

@@ -47,4 +47,4 @@ export type MapKey = string;

name?: MapKey;
type: "action" | "action-status" | "event" | "store-change" | "store-pipe" | "store-control";
type: "action" | "action-status" | "action-listener" | "event" | "store-change" | "store-pipe" | "store-control";
};
export type ErrorListenerSignature<Arguments extends any[] = any[]> = (errorResponse: ErrorResponse<Arguments>) => void;

@@ -27,2 +27,10 @@ import { useCallback, useEffect, useRef } from "react";

subscribe: (opts) => {
// Unlike an eventBus/store target (which auto-creates on access), an
// ActionBus action can be absent — never registered, or removed at
// runtime while this component stays mounted. addListener/get would
// throw ("Action <name> not found" / a TypeError on undefined) out
// of the commit-phase effect. Skip when absent instead of crashing.
if (!actionBus.has(actionName)) {
return;
}
actionBus.addListener(actionName, genericHandler, opts !== null && opts !== void 0 ? opts : undefined);

@@ -32,2 +40,8 @@ actionBus.get(actionName).addBeforeActionListener(genericBeforeActionHandler);

unsubscribe: (ctx) => {
// The action may have been removed while mounted; its listeners were
// dropped with it, so there is nothing to detach and get() would
// throw. Skip when absent (matches the subscribe guard above).
if (!actionBus.has(actionName)) {
return;
}
actionBus.removeListener(actionName, genericHandler, ctx);

@@ -34,0 +48,0 @@ actionBus.get(actionName).removeBeforeActionListener(genericBeforeActionHandler);

@@ -32,7 +32,5 @@ import { useCallback, useSyncExternalStore } from "react";

if (typeof value === "function") {
// The cast is required by tsc (the typeof-narrowed `value` is
// `Setter | (ValueType & Function)`, not all callable), even
// though no-unnecessary-type-assertion disagrees on this TS
// version.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
// The cast is required by tsc: the typeof-narrowed `value` is
// `Setter | (ValueType & Function)`, which is not uniformly
// callable without narrowing to Setter.
store.set(key, value(store.get(key)));

@@ -39,0 +37,0 @@ }

@@ -125,2 +125,12 @@ import { createEventBus } from "./eventBus.js";

];
// A throwing onChange listener must NOT abort the computed recompute
// cascade below: aborting would leave a computed derived from `name`
// stale and no longer equal to fn(deps), while `data` already holds
// the new value. Route the error to the error event and, when
// unhandled, defer the throw until after the cascade + control
// change have run so derived state is consistent before it
// propagates. (Previously this returned/threw here, before the
// cascade, silently stranding dependents.)
let deferredChangeError = null;
let hasDeferredChangeError = false;
try {

@@ -138,7 +148,6 @@ changes.trigger(name, ...changeArgs);

});
if ((_b = control.get(ErrorEventName)) === null || _b === void 0 ? void 0 : _b.hasListener()) {
effectKeys = [];
return true;
if (!((_b = control.get(ErrorEventName)) === null || _b === void 0 ? void 0 : _b.hasListener())) {
deferredChangeError = error;
hasDeferredChangeError = true;
}
throw error;
}

@@ -206,2 +215,7 @@ if ((_c = control.get(EffectEventName)) === null || _c === void 0 ? void 0 : _c.hasListener()) {

}
// Propagate an unhandled onChange-listener error now that the
// cascade and control change have run and derived state is settled.
if (hasDeferredChangeError) {
throw deferredChangeError;
}
return true;

@@ -208,0 +222,0 @@ }

{
"name": "@kuindji/reactive",
"version": "1.3.0",
"version": "1.3.1",
"author": "Ivan Kuindzhi",

@@ -5,0 +5,0 @@ "type": "module",