Socket
Socket
Sign inDemoInstall

@rx-signals/store

Package Overview
Dependencies
Maintainers
1
Versions
94
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@rx-signals/store - npm Package Compare versions

Comparing version 3.0.0-rc43 to 3.0.0-rc44

dist/cjs/effect-result.js

94

dist/cjs/effect-signals-factory.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getEffectSignalsFactory = exports.isCombinedEffectResultInSuccessState = exports.isCombinedEffectResultInErrorState = void 0;
exports.getEffectSignalsFactory = exports.isCompletedResultEvent = exports.isCombinedEffectResultInCompletedSuccessState = exports.isCombinedEffectResultInSuccessState = exports.isCombinedEffectResultInErrorState = void 0;
const rxjs_1 = require("rxjs");
const effect_result_1 = require("./effect-result");
const signals_factory_1 = require("./signals-factory");

@@ -10,6 +11,3 @@ const store_utils_1 = require("./store-utils");

*/
const isCombinedEffectResultInErrorState = (cer) => !!cer.resultError &&
!cer.resultPending &&
(0, store_utils_1.isNoValueType)(cer.result) &&
(0, store_utils_1.isNotNoValueType)(cer.resultInput);
const isCombinedEffectResultInErrorState = (cer) => (0, effect_result_1.isEffectError)(cer.result);
exports.isCombinedEffectResultInErrorState = isCombinedEffectResultInErrorState;

@@ -19,5 +17,14 @@ /**

*/
const isCombinedEffectResultInSuccessState = (cer) => !cer.resultError && !cer.resultPending && (0, store_utils_1.isNotNoValueType)(cer.result);
const isCombinedEffectResultInSuccessState = (cer) => (0, effect_result_1.isNotEffectError)(cer.result);
exports.isCombinedEffectResultInSuccessState = isCombinedEffectResultInSuccessState;
const isCompletedSuccess = (value) => value.completed;
/**
* Typeguard to check if a {@link CombinedEffectResult} is a {@link CombinedEffectResultInCompletedSuccessState}
*/
const isCombinedEffectResultInCompletedSuccessState = (cer) => (0, effect_result_1.isNotEffectError)(cer.result) && !cer.resultPending;
exports.isCombinedEffectResultInCompletedSuccessState = isCombinedEffectResultInCompletedSuccessState;
/**
* Typeguard to check if a {@link EffectResultEvent} is a {@link EffectCompletedResultEvent}
*/
const isCompletedResultEvent = (value) => value.completed;
exports.isCompletedResultEvent = isCompletedResultEvent;
const getInputSignalIds = (nameExtension) => ({

@@ -30,9 +37,6 @@ input: (0, store_utils_1.getDerivedId)(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_input`),

combined: (0, store_utils_1.getDerivedId)(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_combined`),
result: (0, store_utils_1.getDerivedId)(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_result`),
pending: (0, store_utils_1.getDerivedId)(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_pending`),
errors: (0, store_utils_1.getEventId)(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_errors`),
successes: (0, store_utils_1.getEventId)(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_successes`),
completedSuccesses: (0, store_utils_1.getEventId)(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_completedSuccesses`),
results: (0, store_utils_1.getEventId)(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_results`),
completedResults: (0, store_utils_1.getEventId)(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_completedResults`),
});
const NO_VALUE_TRIGGERED_INPUT = '$INTERNAL_NV_TI$';
const NO_VALUE_TRIGGERED_INPUT = '$RXS_INTERNAL_NV_TI$';
const getIsNewInput = (effectInputEquals) => ([input, resultState, token]) => token !== resultState.resultToken ||

@@ -44,3 +48,3 @@ (0, store_utils_1.isNoValueType)(resultState.resultInput) ||

const effectId = (0, store_utils_1.getEffectId)();
const wrappedResultEffect = (input, store, previousInput, previousResult) => store.getEffect(effectId).pipe((0, rxjs_1.take)(1), (0, rxjs_1.switchMap)(effect => {
const wrappedResultEffect = (input, args) => args.store.getEffect(effectId).pipe((0, rxjs_1.take)(1), (0, rxjs_1.switchMap)(effect => {
try {

@@ -50,3 +54,3 @@ const wrappedEffect = config.wrappedEffectGetter

: effect;
return wrappedEffect(input, store, previousInput, previousResult);
return wrappedEffect(input, args);
}

@@ -57,5 +61,5 @@ catch (error) {

}));
const internalResultEffect = (input, store, previousInput, previousResult) => new rxjs_1.Observable(subscriber => {
const internalResultEffect = (input, args) => new rxjs_1.Observable(subscriber => {
let currentResult = store_utils_1.NO_VALUE;
const subscription = wrappedResultEffect(input, store, previousInput, previousResult).subscribe({
const subscription = wrappedResultEffect(input, args).subscribe({
next: result => {

@@ -79,3 +83,9 @@ currentResult = result;

error: e => {
subscriber.error(e);
subscriber.next({
result: (0, effect_result_1.toEffectError)({
unhandledError: e,
}),
completed: true,
});
subscriber.complete();
currentResult = store_utils_1.NO_VALUE;

@@ -114,3 +124,3 @@ },

store.addDerivedState(triggeredInputBehavior, store.getEventStream(triggeredInputEvent), NO_VALUE_TRIGGERED_INPUT);
store.addEventSource(outIds.completedSuccesses, store.getEventStream(outIds.successes).pipe((0, rxjs_1.filter)(isCompletedSuccess)));
store.addEventSource(outIds.completedResults, store.getEventStream(outIds.results).pipe((0, rxjs_1.filter)(exports.isCompletedResultEvent)));
// It is important to setup the combined observable as behavior,

@@ -129,3 +139,3 @@ // because a simple shareReplay (even with refCount) could create a memory leak!!!

: combined.pipe((0, rxjs_1.debounceTime)(config.effectDebounceTime));
store.add4TypedEventSource(resultEvent, triggeredInputEvent, outIds.errors, outIds.successes, eventSourceInput.pipe((0, rxjs_1.filter)(isNewInput), (0, rxjs_1.switchMap)(([input, resultState, token, triggeredInput]) => config.withTrigger && input !== triggeredInput
store.add3TypedEventSource(resultEvent, triggeredInputEvent, outIds.results, eventSourceInput.pipe((0, rxjs_1.filter)(isNewInput), (0, rxjs_1.switchMap)(([input, resultState, token, triggeredInput]) => config.withTrigger && input !== triggeredInput
? store.getEventStream(inIds.trigger).pipe((0, rxjs_1.map)(() => ({

@@ -135,3 +145,9 @@ type: triggeredInputEvent,

})))
: internalResultEffect(input, store, resultState.resultInput, (0, store_utils_1.isNotNoValueType)(resultState.result) ? resultState.result.result : store_utils_1.NO_VALUE).pipe((0, rxjs_1.switchMap)((result) => (0, rxjs_1.of)({
: internalResultEffect(input, {
store,
previousInput: resultState.resultInput,
previousResult: (0, store_utils_1.isNotNoValueType)(resultState.result)
? resultState.result.result
: store_utils_1.NO_VALUE,
}).pipe((0, rxjs_1.switchMap)((result) => (0, rxjs_1.of)({
type: resultEvent,

@@ -145,27 +161,13 @@ event: {

}, {
type: outIds.successes,
type: outIds.results,
event: {
// EffectSuccess<IT, RT>
// EffectResultEvent<IT, RT, ER>
result: result.result,
resultInput: input,
previousInput: resultState.resultInput,
previousResult: (0, store_utils_1.isNotNoValueType)(resultState.result)
? resultState.result.result
: store_utils_1.NO_VALUE,
// previousInput: resultState.resultInput,
// previousResult: isNotNoValueType(resultState.result)
// ? resultState.result.result
// : NO_VALUE,
completed: result.completed,
},
})), (0, rxjs_1.catchError)(error => (0, rxjs_1.of)({
type: outIds.errors,
event: {
error,
errorInput: input,
},
}, {
type: resultEvent,
event: {
result: store_utils_1.NO_VALUE,
resultInput: input,
resultError: error,
resultToken: token,
},
}))))), resultEvent);

@@ -189,3 +191,8 @@ const getIsPending = config.withTrigger

}
: Object.assign({ currentInput: input, result: (0, store_utils_1.isNotNoValueType)(resultState.result) ? resultState.result.result : store_utils_1.NO_VALUE, resultInput: resultState.resultInput, resultError: resultState.resultError, resultPending: false }, (resultState.resultError ? { resultError: resultState.resultError } : {})))), config.initialResultGetter
: {
currentInput: input,
result: (0, store_utils_1.isNotNoValueType)(resultState.result) ? resultState.result.result : store_utils_1.NO_VALUE,
resultInput: resultState.resultInput,
resultPending: false,
})), config.initialResultGetter
? {

@@ -198,4 +205,2 @@ currentInput: store_utils_1.NO_VALUE,

: store_utils_1.NO_VALUE);
store.addDerivedState(outIds.result, store.getBehavior(outIds.combined).pipe((0, rxjs_1.filter)(exports.isCombinedEffectResultInSuccessState)));
store.addDerivedState(outIds.pending, store.getBehavior(outIds.combined).pipe((0, rxjs_1.map)(c => c.resultPending)));
};

@@ -216,2 +221,3 @@ return {

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
* @returns {EffectSignalsFactory<Input, Result>}

@@ -218,0 +224,0 @@ */

@@ -6,2 +6,3 @@ "use strict";

const operators_1 = require("rxjs/operators");
const effect_result_1 = require("./effect-result");
const effect_signals_factory_1 = require("./effect-signals-factory");

@@ -49,3 +50,6 @@ const model_signals_factory_1 = require("./model-signals-factory");

* @template SaveOutput - specifies the output-type for the save-effect (defaults to LoadInput)
* @template ValidationErrorType - specifies the error-type for failed validations
* @template ValidationFailedType - specifies the type representing a failed validations (total or for a distinct property, hence `ModelValidationResult<Entity, ValidationFailedType>` will be used as validation result)
* @template LoadError - specifies the error type of the load-effect
* @template ValidationError - specifies the error type of the validation-effect (use never, if your validation cannot error)
* @template SaveError - specifies the error type of the result-effect
*/

@@ -55,3 +59,3 @@ const getEntityEditSignalsFactory = () => (0, effect_signals_factory_1.getEffectSignalsFactory)() // model-fetch (fetching the edit entity)

.compose((0, model_signals_factory_1.getModelSignalsFactory)()) // editing-model
.connectObservable((store, output) => store.getBehavior(output.result).pipe((0, rxjs_1.map)(result => result.result)), 'setAsDefault', true) // connecting entity model-fetch-result to editing-model
.connectObservable(({ store, output }) => store.getBehavior(output.combined).pipe((0, operators_1.filter)(effect_signals_factory_1.isCombinedEffectResultInCompletedSuccessState), (0, rxjs_1.map)(result => result.result)), 'setAsDefault', true) // connecting entity model-fetch-result to editing-model
.compose((0, validated_input_with_result_signals_factory_1.getValidatedInputWithResultSignalsFactory)()) // model validation and save

@@ -78,5 +82,11 @@ .connect('modelWithDefault', 'input', false) // connecting editing-model and vali-persist-input

.addEffectId('save', () => (0, store_utils_1.getEffectId)())
.extendSetup((store, _, output, config, effects) => {
store.addEffect(effects.result, (modelWithResult, st, prevInput, prevResult) => st.getEffect(effects.save).pipe((0, operators_1.take)(1), // without this, the effect would never complete
(0, operators_1.switchMap)(eff => eff(modelWithResult.model, st, (0, store_utils_1.isNotNoValueType)(prevInput) ? prevInput.model : store_utils_1.NO_VALUE, prevResult))));
.extendSetup(({ store, output, config, effects }) => {
store.addEffect(effects.result, (modelWithResult, args) => args.store.getEffect(effects.save).pipe((0, operators_1.take)(1), // without this, the effect would never complete
(0, operators_1.switchMap)(eff => eff(modelWithResult.model, {
store: args.store,
previousInput: (0, store_utils_1.isNotNoValueType)(args.previousInput)
? args.previousInput.model
: store_utils_1.NO_VALUE,
previousResult: args.previousResult,
}))));
store.addDerivedState(output.combinedModel, (0, rxjs_1.combineLatest)([

@@ -104,5 +114,7 @@ (0, rxjs_1.combineLatest)([

: (0, exports.shallowEquals)(edit.resultInput.model, edit.currentInput.model))),
edit.currentInput === edit.validatedInput && (0, store_utils_1.isNotNoValueType)(edit.validationResult)
edit.currentInput === edit.validatedInput &&
(0, store_utils_1.isNotNoValueType)(edit.validationResult) &&
(0, effect_result_1.isNotEffectError)(edit.validationResult)
? edit.validationResult
: null,
: store_utils_1.NO_VALUE,
]), (0, rxjs_1.distinctUntilChanged)(([aload, aedit, aloading, adisabled, avalidation], [bload, bedit, bloading, bdisabled, bvalidation]) => aloading === bloading &&

@@ -131,3 +143,3 @@ adisabled === bdisabled &&

if (config.onSaveCompletedEvent) {
store.connectObservable(store.getEventStream(output.resultCompletedSuccesses).pipe((0, rxjs_1.map)(() => undefined)), config.onSaveCompletedEvent);
store.connectObservable(store.getEventStream(output.conflicts2.completedResults).pipe((0, operators_1.filter)(e => (0, effect_result_1.isNotEffectError)(e.result)), (0, rxjs_1.map)(() => undefined)), config.onSaveCompletedEvent);
}

@@ -147,19 +159,9 @@ })

.mapOutput((ids) => ({
load: {
combined: ids.conflicts1.combined,
result: ids.conflicts1.result,
pending: ids.pending,
successes: ids.successes,
completedSuccesses: ids.completedSuccesses,
errors: ids.errors,
},
load: ids.conflicts1,
edit: {
combined: ids.conflicts2.combined,
result: ids.conflicts2.result,
validationSuccesses: ids.validationSuccesses,
validationCompletedSuccesses: ids.validationCompletedSuccesses,
validationErrors: ids.validationErrors,
resultSuccesses: ids.resultSuccesses,
resultCompletedSuccesses: ids.resultCompletedSuccesses,
resultErrors: ids.resultErrors,
validationResults: ids.validationResults,
validationCompletedResults: ids.validationCompletedResults,
results: ids.conflicts2.results,
completedResults: ids.conflicts2.completedResults,
},

@@ -166,0 +168,0 @@ model: ids.combinedModel,

@@ -18,2 +18,3 @@ "use strict";

exports.getSequence = exports.expectSequence = void 0;
__exportStar(require("./effect-result"), exports);
__exportStar(require("./effect-signals-factory"), exports);

@@ -30,3 +31,2 @@ __exportStar(require("./entity-edit-signals-factory"), exports);

__exportStar(require("./validated-input-with-result-signals-factory"), exports);
__exportStar(require("./void-effect-signals-factory"), exports);
//# sourceMappingURL=index.js.map

@@ -108,3 +108,3 @@ "use strict";

s.setup(store);
extend(store, s.input, s.output, config, s.effects);
extend({ store, input: s.input, output: s.output, config, effects: s.effects });
} });

@@ -132,3 +132,3 @@ });

connect(outputName, inputName, keepInputId) {
const fnew = this.extendSetup((store, input, output) => {
const fnew = this.extendSetup(({ store, input, output }) => {
const fromId = output[outputName];

@@ -152,3 +152,3 @@ const toId = input[inputName];

connectId(fromId, inputName, keepInputId) {
const fnew = this.extendSetup((store, input) => {
const fnew = this.extendSetup(({ store, input }) => {
const toId = input[inputName];

@@ -171,5 +171,5 @@ store.connect(fromId, toId);

connectObservable(sourceGetter, inputName, keepInputId) {
const fnew = this.extendSetup((st, ip, op, conf) => {
const toId = ip[inputName];
st.connectObservable(sourceGetter(st, op, conf), toId);
const fnew = this.extendSetup(args => {
const toId = args.input[inputName];
args.store.connectObservable(sourceGetter(args), toId);
});

@@ -326,3 +326,9 @@ const result = (keepInputId ? fnew : fnew.removeInputId(inputName));

return Object.assign(Object.assign({}, s), { setup: store => {
store.addDerivedState(newId, mapper(store.getBehavior(oldId), store, s.input, s.output, config));
store.addDerivedState(newId, mapper(store.getBehavior(oldId), {
store,
input: s.input,
output: s.output,
config,
effects: s.effects,
}));
s.setup(store);

@@ -384,3 +390,3 @@ }, output: Object.assign(Object.assign({}, s.output), { [outputName]: newId }) });

useExistingEffect(name, idGetter, keepEffectId) {
const result = this.extendSetup((store, _, _2, config, effects) => {
const result = this.extendSetup(({ store, config, effects }) => {
store

@@ -387,0 +393,0 @@ .getEffect(idGetter(config))

@@ -40,2 +40,3 @@ "use strict";

* @template ResultType - specifies the type for the corresponding effects result
* @template ErrorType - specifies the type error-type for the effect. Use `never` for effects that cannot error.
* @param {string} nameExtension - an optional extension to the symbol name (so the string representation). Usually you should not need this.

@@ -42,0 +43,0 @@ * @returns {EventId<T>}

@@ -534,4 +534,4 @@ "use strict";

*
* @param {EffectId<InputType, ResultType>} id - the unique identifier for the effect
* @returns {Observable<Effect<InputType, ResultType>>} - the effect observable
* @param {EffectId<InputType, ResultType, ErrorType>} id - the unique identifier for the effect
* @returns {Observable<Effect<InputType, ResultType, ErrorType>>} - the effect observable
*/

@@ -538,0 +538,0 @@ getEffect(id) {

@@ -8,7 +8,16 @@ "use strict";

const isResultInputGetterInput = (c) => !c.resultPending &&
c.resultInput !== store_utils_1.NO_VALUE &&
c.result !== store_utils_1.NO_VALUE &&
(0, store_utils_1.isNotNoValueType)(c.resultInput) &&
(0, store_utils_1.isNotNoValueType)(c.result) &&
c.currentInput === c.resultInput;
const resultInputGetter = (store, validationBehaviorId, isValidationResultValid) => store.getBehavior(validationBehaviorId).pipe((0, rxjs_1.filter)(isResultInputGetterInput), (0, rxjs_1.filter)(c => isValidationResultValid(c.result)), (0, rxjs_1.map)(c => c.resultInput), (0, rxjs_1.distinctUntilChanged)());
const mapBehaviors = ([v, r], isValidationResultValid) => (Object.assign(Object.assign({ currentInput: v.currentInput, validationPending: v.resultPending, validatedInput: v.resultInput, validationResult: v.result, isValid: !v.resultPending && v.result !== store_utils_1.NO_VALUE ? isValidationResultValid(v.result) : false, resultPending: r.resultPending, resultInput: r.resultInput }, (r.resultError ? { resultError: r.resultError } : {})), { result: r.result }));
const mapBehaviors = ([v, r], isValidationResultValid) => ({
currentInput: v.currentInput,
validationPending: v.resultPending,
validatedInput: v.resultInput,
validationResult: v.result,
isValid: !v.resultPending && (0, store_utils_1.isNotNoValueType)(v.result) ? isValidationResultValid(v.result) : false,
resultPending: r.resultPending,
resultInput: r.resultInput,
result: r.result,
});
const setupCombinedBehavior = (store, outIds, id, isValidationResultValid, initialResultGetter) => {

@@ -25,3 +34,3 @@ store.addDerivedState(id, (0, rxjs_1.combineLatest)([

r.currentInput === v.resultInput ||
v.result === store_utils_1.NO_VALUE ||
(0, store_utils_1.isNoValueType)(v.result) ||
!isValidationResultValid(v.result)), (0, rxjs_1.map)(pair => mapBehaviors(pair, isValidationResultValid)), (0, rxjs_1.distinctUntilChanged)((a, b) => a.currentInput === b.currentInput &&

@@ -32,3 +41,2 @@ a.isValid === b.isValid &&

a.resultPending === b.resultPending &&
a.resultError === b.resultError &&
a.validatedInput === b.validatedInput &&

@@ -62,5 +70,5 @@ a.validationPending === b.validationPending &&

})
.extendSetup((store, inIds, outIds, config) => {
.extendSetup(({ store, input, output, config }) => {
var _a;
store.connectObservable(resultInputGetter(store, outIds.conflicts1.combined, (_a = config.isValidationResultValid) !== null && _a !== void 0 ? _a : (validationResult => validationResult === null)), inIds.conflicts2.input);
store.connectObservable(resultInputGetter(store, output.conflicts1.combined, (_a = config.isValidationResultValid) !== null && _a !== void 0 ? _a : (validationResult => validationResult === null)), input.conflicts2.input);
})

@@ -71,5 +79,5 @@ .addOutputId('combined', config => {

})
.extendSetup((store, _, output, config) => {
.extendSetup(({ store, output, config }) => {
var _a;
setupCombinedBehavior(store, output, output.combined, (_a = config.isValidationResultValid) !== null && _a !== void 0 ? _a : (validationResult => validationResult === null), config.initialResultGetter);
setupCombinedBehavior(store, output, output.combined, (_a = config.isValidationResultValid) !== null && _a !== void 0 ? _a : (validationResult => (validationResult !== null && validationResult !== void 0 ? validationResult : null) === null), config.initialResultGetter);
})

@@ -84,11 +92,8 @@ .mapInput(input => ({

combined: output.combined,
result: output.conflicts2.result,
validationErrors: output.conflicts1.errors,
validationSuccesses: output.conflicts1.successes,
validationCompletedSuccesses: output.conflicts1.completedSuccesses,
resultErrors: output.conflicts2.errors,
resultSuccesses: output.conflicts2.successes,
resultCompletedSuccesses: output.conflicts2.completedSuccesses,
validationResults: output.conflicts1.results,
validationCompletedResults: output.conflicts1.completedResults,
results: output.conflicts2.results,
completedResults: output.conflicts2.completedResults,
}));
exports.getValidatedInputWithResultSignalsFactory = getValidatedInputWithResultSignalsFactory;
//# sourceMappingURL=validated-input-with-result-signals-factory.js.map

@@ -0,3 +1,4 @@

import { EffectError, ToEffectError } from './effect-result';
import { Signals, SignalsFactory } from './signals-factory';
import { Effect } from './store';
import { Effect, SafeEffectResult, UnhandledEffectError } from './store';
import { DerivedId, EffectId, EventId, NoValueType } from './store-utils';

@@ -9,4 +10,5 @@ /**

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type CombinedEffectResult<Input, Result> = {
export type CombinedEffectResult<Input, Result, Error> = {
/**

@@ -18,18 +20,15 @@ * The current input (which might differ from the resultInput),

/**
* The current result,
* The current effect-result,
* or `NO_VALUE` if no result was received yet,
* or the effect produced an error
*/
result: Result | NoValueType;
result: SafeEffectResult<Result, Error> | NoValueType;
/**
* The input that produced the current result,
* The input that produced the current effect-result,
* or `NO_VALUE`, if initial result or no result received yet */
resultInput: Input | NoValueType;
/**
* In case the effect led to an error (`result === NO_VALUE` in that case), else undefined */
resultError?: any;
/**
* Indicates whether the effect is currently running.
* In case of a factory without trigger, this will be true whenever one or multiple
* of the following conditions is met:
* of the following conditions are met:
* `currentInput !== resultInput`,

@@ -47,8 +46,8 @@ * or an invalidation event has been sent,

* @template Input - specifies the input type for the effect
* @template Error - specifies the error type of the effect
*/
export type CombinedEffectResultInErrorState<Input> = {
export type CombinedEffectResultInErrorState<Input, Error> = {
currentInput: Input;
result: NoValueType;
result: ToEffectError<Error> | EffectError<UnhandledEffectError>;
resultInput: Input;
resultError: any;
resultPending: false;

@@ -59,5 +58,5 @@ };

*/
export declare const isCombinedEffectResultInErrorState: <Input, Result>(cer: CombinedEffectResult<Input, Result>) => cer is CombinedEffectResultInErrorState<Input>;
export declare const isCombinedEffectResultInErrorState: <Input, Result, Error_1>(cer: CombinedEffectResult<Input, Result, Error_1>) => cer is CombinedEffectResultInErrorState<Input, Error_1>;
/**
* Type representing a {@link CombinedEffectResult} in it's success state (non-pending)
* Type representing a {@link CombinedEffectResult} in it's success state (pending or non-pending)
*

@@ -71,3 +70,3 @@ * @template Input - specifies the input type for the effect

resultInput: Input | NoValueType;
resultPending: false;
resultPending: boolean;
};

@@ -77,20 +76,25 @@ /**

*/
export declare const isCombinedEffectResultInSuccessState: <Input, Result>(cer: CombinedEffectResult<Input, Result>) => cer is CombinedEffectResultInSuccessState<Input, Result>;
export declare const isCombinedEffectResultInSuccessState: <Input, Result, Error_1>(cer: CombinedEffectResult<Input, Result, Error_1>) => cer is CombinedEffectResultInSuccessState<Input, Result>;
/**
* Value-type for error events produced by an `Effect<Input, any>` (unhandled effect errors).
* Type representing a {@link CombinedEffectResult} in it's success state (non-pending, hece completed effect)
*
* @template Input - specifies the input type for the effect
* @template Result - specifies the result type of the effect
*/
export type EffectError<Input> = {
/** the unhandled error thrown by an effect */
error: any;
/** the effect input that lead to the error */
errorInput: Input;
export type CombinedEffectResultInCompletedSuccessState<Input, Result> = {
currentInput: Input | NoValueType;
result: Result;
resultInput: Input | NoValueType;
resultPending: false;
};
/**
* Value-type for success events produced by {@link EffectSignals}.
* In case the effect completes after one result, two success events will
* Typeguard to check if a {@link CombinedEffectResult} is a {@link CombinedEffectResultInCompletedSuccessState}
*/
export declare const isCombinedEffectResultInCompletedSuccessState: <Input, Result, Error_1>(cer: CombinedEffectResult<Input, Result, Error_1>) => cer is CombinedEffectResultInCompletedSuccessState<Input, Result>;
/**
* Value-type for result events produced by {@link EffectSignals}.
* In case the effect completes after one result, two reult events will
* be dispatched, one with completed false and one with completed true.
* This is to handle cases where the effect might send multiple results
* before completing. Thus, if an effect never completes, all success event
* before completing. Thus, if an effect never completes, all result events
* will have completed false.

@@ -100,12 +104,11 @@ *

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectSuccess<Input, Result> = {
export type EffectResultEvent<Input, Result, Error> = {
/** the effect result */
result: Result;
result: SafeEffectResult<Result, Error>;
/** the effect input that lead to the result */
resultInput: Input;
/** the input of the previous completed result, or `NO_VALUE` */
previousInput: Input | NoValueType;
/** the previous completed result, or `NO_VALUE` */
previousResult: Result | NoValueType;
/** has the effect for the given resultInput completed */

@@ -115,4 +118,4 @@ completed: boolean;

/**
* Value-type for completed success events produced by {@link EffectSignals}.
* In contrast to {@link EffectSuccess} events with this type are only dispatched,
* Value-type for completed result events produced by {@link EffectSignals}.
* In contrast to {@link EffectResultEvent}, events with this type are only dispatched,
* if the effect has completed, hence it will never be fired for effects that never complete.

@@ -122,7 +125,12 @@ *

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectCompletedSuccess<Input, Result> = Omit<EffectSuccess<Input, Result>, 'completed'> & {
export type EffectCompletedResultEvent<Input, Result, Error> = Omit<EffectResultEvent<Input, Result, Error>, 'completed'> & {
completed: true;
};
/**
* Typeguard to check if a {@link EffectResultEvent} is a {@link EffectCompletedResultEvent}
*/
export declare const isCompletedResultEvent: <Input, Result, Error_1>(value: EffectResultEvent<Input, Result, Error_1> | EffectCompletedResultEvent<Input, Result, Error_1>) => value is EffectCompletedResultEvent<Input, Result, Error_1>;
/**
* Type specifying the input {@link EffectSignals} (the corresponding signal-sources are NOT added to the store

@@ -148,21 +156,16 @@ * by the EffectSignals-setup, but by whoever uses the signals, e.g. by extendSetup or fmap or just using dispatch).

* Type specifying the output {@link EffectSignals} (signals produced by `EffectSignals`).
* The {@link EffectSignalsFactory} takes care that subscribing error- or success-events keeps
* the effect itself lazy (hence only subscribing the combined behavior will subscribe the effect itself).
* The {@link EffectSignalsFactory} takes care that subscribing the result-events keeps
* the effect itself lazy (hence only subscribing the combined behavior will subscribe the effect itself)!
*
* @template Input - specifies the input type for the effect
* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectOutputSignals<Input, Result> = {
export type EffectOutputSignals<Input, Result, Error> = {
/** Produced combined effect result behavior */
combined: DerivedId<CombinedEffectResult<Input, Result>>;
/** Convenience behavior derived from combined-behavior, representing only completed success states */
result: DerivedId<CombinedEffectResultInSuccessState<Input, Result>>;
/** Convenience behavior derived from combined-behavior, representing only pending state */
pending: DerivedId<boolean>;
/** Produced error events */
errors: EventId<EffectError<Input>>;
combined: DerivedId<CombinedEffectResult<Input, Result, Error>>;
/** Produced success events */
successes: EventId<EffectSuccess<Input, Result>>;
results: EventId<EffectResultEvent<Input, Result, Error>>;
/** Produced success events */
completedSuccesses: EventId<EffectCompletedSuccess<Input, Result>>;
completedResults: EventId<EffectCompletedResultEvent<Input, Result, Error>>;
};

@@ -175,5 +178,6 @@ /**

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectFactoryEffects<Input, Result> = {
id: EffectId<Input, Result>;
export type EffectFactoryEffects<Input, Result, Error> = {
id: EffectId<Input, Result, Error>;
};

@@ -184,10 +188,9 @@ /**

* ```markdown
* 1.) The produced CombinedEffectResult<Input, Result> behavior must be lazy, hence, as long as it is not subscribed,
* no effect will be triggered.
* 2.) Unhandled effect errors are caught and dispatched as EffectError<Input>. A subscription of the corresponding
* errors event stream will NOT subscribe the result behavior (see requirement 1).
* 3.) In addition to the result behavior, also an event-stream for EffectSuccess<Input, Result> is provided. This is important
* in cases where an effect success should be used to trigger something else (e.g. close a popup), but you cannot use the result
* behavior, because it would mean to always subscribe the result. In contrast, subscription of the success event stream will NOT
* subscribe the result behavior. (the same holds true for the completedSuccesses event-stream)
* 1.) The produced CombinedEffectResult<Input, Result, Error> behavior must be lazy, hence, as long as it is not subscribed,
* no effect will be triggered (so subscribing just results, or completedResults will not trigger the effect).
* 2.) Unhandled effect errors are caught and will lead to an EffectError<UnhandledEffectError>.
* 3.) In addition to the combined-behavior, also event-streams for EffectResultEvent and EffectCompletedResultEvent are provided. This is important
* in cases where e.g. an effect success should be used to trigger something else (e.g. close a popup), but you cannot use the result
* behavior, because it would mean to always subscribe the result. In contrast, subscription of the an rsult-event-stream will NOT
* subscribe the effect.
* ```

@@ -199,4 +202,5 @@ *

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectSignals<Input, Result> = Signals<EffectInputSignals<Input>, EffectOutputSignals<Input, Result>, EffectFactoryEffects<Input, Result>>;
export type EffectSignals<Input, Result, Error> = Signals<EffectInputSignals<Input>, EffectOutputSignals<Input, Result, Error>, EffectFactoryEffects<Input, Result, Error>>;
/**

@@ -207,4 +211,5 @@ * This type specifies the type of the argument to {@link EffectSignalsBuild}, hence the configuration of {@link EffectSignals}.

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectConfiguration<Input, Result> = {
export type EffectConfiguration<Input, Result, Error> = {
/** Function used to determine whether a new input equals the previous one. Defaults to strict equals (`a === b`) */

@@ -219,3 +224,3 @@ effectInputEquals?: (a: Input, b: Input) => boolean;

/** Function to wrap the effect defined by effectId with a custom `Effect` */
wrappedEffectGetter?: (effect: Effect<Input, Result>) => Effect<Input, Result>;
wrappedEffectGetter?: (effect: Effect<Input, Result, Error>) => Effect<Input, Result, Error>;
/** Specifies whether the input behavior should be subscribed eagerly (defaults to false) */

@@ -231,5 +236,6 @@ eagerInputSubscription?: boolean;

* @template Result - specifies the result type of the effect
* @param {EffectConfiguration<Input, Result>} config - the configuration for the `EffectSignals`
* @template Error - specifies the error type of the effect
* @param {EffectConfiguration<Input, Result, Error>} config - the configuration for the `EffectSignals`
*/
export type EffectSignalsBuild = <Input, Result>(config: EffectConfiguration<Input, Result>) => EffectSignals<Input, Result>;
export type EffectSignalsBuild = <Input, Result, Error>(config: EffectConfiguration<Input, Result, Error>) => EffectSignals<Input, Result, Error>;
/**

@@ -240,4 +246,5 @@ * This type specifies a {@link SignalsFactory} wrapping {@link EffectSignals}.

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectSignalsFactory<Input, Result> = SignalsFactory<EffectInputSignals<Input>, EffectOutputSignals<Input, Result>, EffectConfiguration<Input, Result>, EffectFactoryEffects<Input, Result>>;
export type EffectSignalsFactory<Input, Result, Error> = SignalsFactory<EffectInputSignals<Input>, EffectOutputSignals<Input, Result, Error>, EffectConfiguration<Input, Result, Error>, EffectFactoryEffects<Input, Result, Error>>;
/**

@@ -248,4 +255,5 @@ * This function creates an {@link EffectSignalsFactory}.

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
* @returns {EffectSignalsFactory<Input, Result>}
*/
export declare const getEffectSignalsFactory: <Input, Result>() => EffectSignalsFactory<Input, Result>;
export declare const getEffectSignalsFactory: <Input, Result, Error_1>() => EffectSignalsFactory<Input, Result, Error_1>;

@@ -1,2 +0,3 @@

import { Observable, catchError, combineLatest, debounceTime, filter, map, of, switchMap, take, throwError, } from 'rxjs';
import { Observable, combineLatest, debounceTime, filter, map, of, switchMap, take, throwError, } from 'rxjs';
import { isEffectError, isNotEffectError, toEffectError, } from './effect-result';
import { SignalsFactory } from './signals-factory';

@@ -7,11 +8,15 @@ import { NO_VALUE, getDerivedId, getEffectId, getEventId, getStateId, isNoValueType, isNotNoValueType, } from './store-utils';

*/
export const isCombinedEffectResultInErrorState = (cer) => !!cer.resultError &&
!cer.resultPending &&
isNoValueType(cer.result) &&
isNotNoValueType(cer.resultInput);
export const isCombinedEffectResultInErrorState = (cer) => isEffectError(cer.result);
/**
* Typeguard to check if a {@link CombinedEffectResult} is a {@link CombinedEffectResultInSuccessState}
*/
export const isCombinedEffectResultInSuccessState = (cer) => !cer.resultError && !cer.resultPending && isNotNoValueType(cer.result);
const isCompletedSuccess = (value) => value.completed;
export const isCombinedEffectResultInSuccessState = (cer) => isNotEffectError(cer.result);
/**
* Typeguard to check if a {@link CombinedEffectResult} is a {@link CombinedEffectResultInCompletedSuccessState}
*/
export const isCombinedEffectResultInCompletedSuccessState = (cer) => isNotEffectError(cer.result) && !cer.resultPending;
/**
* Typeguard to check if a {@link EffectResultEvent} is a {@link EffectCompletedResultEvent}
*/
export const isCompletedResultEvent = (value) => value.completed;
const getInputSignalIds = (nameExtension) => ({

@@ -24,9 +29,6 @@ input: getDerivedId(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_input`),

combined: getDerivedId(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_combined`),
result: getDerivedId(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_result`),
pending: getDerivedId(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_pending`),
errors: getEventId(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_errors`),
successes: getEventId(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_successes`),
completedSuccesses: getEventId(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_completedSuccesses`),
results: getEventId(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_results`),
completedResults: getEventId(`${nameExtension !== null && nameExtension !== void 0 ? nameExtension : ''}_completedResults`),
});
const NO_VALUE_TRIGGERED_INPUT = '$INTERNAL_NV_TI$';
const NO_VALUE_TRIGGERED_INPUT = '$RXS_INTERNAL_NV_TI$';
const getIsNewInput = (effectInputEquals) => ([input, resultState, token]) => token !== resultState.resultToken ||

@@ -38,3 +40,3 @@ isNoValueType(resultState.resultInput) ||

const effectId = getEffectId();
const wrappedResultEffect = (input, store, previousInput, previousResult) => store.getEffect(effectId).pipe(take(1), switchMap(effect => {
const wrappedResultEffect = (input, args) => args.store.getEffect(effectId).pipe(take(1), switchMap(effect => {
try {

@@ -44,3 +46,3 @@ const wrappedEffect = config.wrappedEffectGetter

: effect;
return wrappedEffect(input, store, previousInput, previousResult);
return wrappedEffect(input, args);
}

@@ -51,5 +53,5 @@ catch (error) {

}));
const internalResultEffect = (input, store, previousInput, previousResult) => new Observable(subscriber => {
const internalResultEffect = (input, args) => new Observable(subscriber => {
let currentResult = NO_VALUE;
const subscription = wrappedResultEffect(input, store, previousInput, previousResult).subscribe({
const subscription = wrappedResultEffect(input, args).subscribe({
next: result => {

@@ -73,3 +75,9 @@ currentResult = result;

error: e => {
subscriber.error(e);
subscriber.next({
result: toEffectError({
unhandledError: e,
}),
completed: true,
});
subscriber.complete();
currentResult = NO_VALUE;

@@ -108,3 +116,3 @@ },

store.addDerivedState(triggeredInputBehavior, store.getEventStream(triggeredInputEvent), NO_VALUE_TRIGGERED_INPUT);
store.addEventSource(outIds.completedSuccesses, store.getEventStream(outIds.successes).pipe(filter(isCompletedSuccess)));
store.addEventSource(outIds.completedResults, store.getEventStream(outIds.results).pipe(filter(isCompletedResultEvent)));
// It is important to setup the combined observable as behavior,

@@ -123,3 +131,3 @@ // because a simple shareReplay (even with refCount) could create a memory leak!!!

: combined.pipe(debounceTime(config.effectDebounceTime));
store.add4TypedEventSource(resultEvent, triggeredInputEvent, outIds.errors, outIds.successes, eventSourceInput.pipe(filter(isNewInput), switchMap(([input, resultState, token, triggeredInput]) => config.withTrigger && input !== triggeredInput
store.add3TypedEventSource(resultEvent, triggeredInputEvent, outIds.results, eventSourceInput.pipe(filter(isNewInput), switchMap(([input, resultState, token, triggeredInput]) => config.withTrigger && input !== triggeredInput
? store.getEventStream(inIds.trigger).pipe(map(() => ({

@@ -129,3 +137,9 @@ type: triggeredInputEvent,

})))
: internalResultEffect(input, store, resultState.resultInput, isNotNoValueType(resultState.result) ? resultState.result.result : NO_VALUE).pipe(switchMap((result) => of({
: internalResultEffect(input, {
store,
previousInput: resultState.resultInput,
previousResult: isNotNoValueType(resultState.result)
? resultState.result.result
: NO_VALUE,
}).pipe(switchMap((result) => of({
type: resultEvent,

@@ -139,27 +153,13 @@ event: {

}, {
type: outIds.successes,
type: outIds.results,
event: {
// EffectSuccess<IT, RT>
// EffectResultEvent<IT, RT, ER>
result: result.result,
resultInput: input,
previousInput: resultState.resultInput,
previousResult: isNotNoValueType(resultState.result)
? resultState.result.result
: NO_VALUE,
// previousInput: resultState.resultInput,
// previousResult: isNotNoValueType(resultState.result)
// ? resultState.result.result
// : NO_VALUE,
completed: result.completed,
},
})), catchError(error => of({
type: outIds.errors,
event: {
error,
errorInput: input,
},
}, {
type: resultEvent,
event: {
result: NO_VALUE,
resultInput: input,
resultError: error,
resultToken: token,
},
}))))), resultEvent);

@@ -183,3 +183,8 @@ const getIsPending = config.withTrigger

}
: Object.assign({ currentInput: input, result: isNotNoValueType(resultState.result) ? resultState.result.result : NO_VALUE, resultInput: resultState.resultInput, resultError: resultState.resultError, resultPending: false }, (resultState.resultError ? { resultError: resultState.resultError } : {})))), config.initialResultGetter
: {
currentInput: input,
result: isNotNoValueType(resultState.result) ? resultState.result.result : NO_VALUE,
resultInput: resultState.resultInput,
resultPending: false,
})), config.initialResultGetter
? {

@@ -192,4 +197,2 @@ currentInput: NO_VALUE,

: NO_VALUE);
store.addDerivedState(outIds.result, store.getBehavior(outIds.combined).pipe(filter(isCombinedEffectResultInSuccessState)));
store.addDerivedState(outIds.pending, store.getBehavior(outIds.combined).pipe(map(c => c.resultPending)));
};

@@ -210,2 +213,3 @@ return {

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
* @returns {EffectSignalsFactory<Input, Result>}

@@ -212,0 +216,0 @@ */

import { CombinedEffectResult, EffectOutputSignals } from './effect-signals-factory';
import { ModelInputSignals, ModelWithDefault } from './model-signals-factory';
import { SignalsFactory } from './signals-factory';
import { DerivedId, EffectId, EventId } from './store-utils';
import { DerivedId, EffectId, EventId, NoValueType } from './store-utils';
import { ModelValidationResult } from './type-utils';

@@ -18,13 +18,16 @@ import { ValidatedInputWithResult, ValidatedInputWithResultOutput } from './validated-input-with-result-signals-factory';

* @template SaveOutput - specifies the output-type for the save-effect (defaults to LoadInput)
* @template ValidationErrorType - specifies the error-type for failed validations
* @template ValidationFailedType - specifies the type representing a failed validations (total or for a distinct property, hence `ModelValidationResult<Entity, ValidationFailedType>` will be used as validation result)
* @template LoadError - specifies the error type of the load-effect
* @template ValidationError - specifies the error type of the validation-effect (use never, if your validation cannot error)
* @template SaveError - specifies the error type of the result-effect
*/
export type EntityEditModel<Entity, LoadInput = number, SaveOutput = LoadInput, ValidationErrorType = string> = {
export type EntityEditModel<Entity, LoadInput = number, SaveOutput = LoadInput, ValidationFailedType = string, LoadError = unknown, ValidationError = unknown, SaveError = unknown> = {
/**
* The {@link CombinedEffectResult} for the load effect
*/
load: CombinedEffectResult<LoadInput | null, Entity>;
load: CombinedEffectResult<LoadInput | null, Entity, LoadError>;
/**
* The {@link ValidatedInputWithResult} for validation and result effects
*/
edit: ValidatedInputWithResult<ModelWithDefault<Entity>, ModelValidationResult<Entity, ValidationErrorType>, SaveOutput>;
edit: ValidatedInputWithResult<ModelWithDefault<Entity>, ModelValidationResult<Entity, ValidationFailedType>, SaveOutput, ValidationError, SaveError>;
/**

@@ -37,5 +40,5 @@ * The current Entity state (matching edit.currentInput.model)

* Current {@link ModelValidationResult} for entity.
* If entity === edit.validatedInput.model, this matches edit.validationResult, else it's null.
* If `entity === edit.validatedInput.model` and `isNotEffectError(edit.validationResult)` this matches edit.validationResult, else it's NO_VALUE.
*/
validation: ModelValidationResult<Entity, ValidationErrorType>;
validation: ModelValidationResult<Entity, ValidationFailedType> | NoValueType;
/**

@@ -52,3 +55,3 @@ * true, if either:

* the validation effect is pending, or
* edit.isValid is false (the current validation result represents invalid entity state), or
* edit.isValid is false (the current validation result represents invalid entity state, or the validation errored), or
* the result-input equals the current-input

@@ -78,9 +81,9 @@ */

*/
export type EntityEditOutput<Entity, LoadInput = number, SaveOutput = LoadInput, ValidationErrorType = string> = {
export type EntityEditOutput<Entity, LoadInput = number, SaveOutput = LoadInput, ValidationFailedType = string, LoadError = unknown, ValidationError = unknown, SaveError = unknown> = {
/** {@link EffectOutputSignals} for the load effect */
load: EffectOutputSignals<LoadInput | null, Entity>;
load: EffectOutputSignals<LoadInput | null, Entity, LoadError>;
/** {@link ValidatedInputWithResultOutput} for the validation and result effects */
edit: ValidatedInputWithResultOutput<ModelWithDefault<Entity>, ModelValidationResult<Entity, ValidationErrorType>, SaveOutput>;
edit: ValidatedInputWithResultOutput<ModelWithDefault<Entity>, ModelValidationResult<Entity, ValidationFailedType>, SaveOutput, ValidationError, SaveError>;
/** derived bahavior for the {@link EntityEditModel} */
model: DerivedId<EntityEditModel<Entity, LoadInput, SaveOutput, ValidationErrorType>>;
model: DerivedId<EntityEditModel<Entity, LoadInput, SaveOutput, ValidationFailedType, LoadError, ValidationError, SaveError>>;
};

@@ -111,9 +114,9 @@ /**

*/
export type EntityEditEffects<Entity, LoadInput = number, SaveOutput = LoadInput, ValidationErrorType = string> = {
export type EntityEditEffects<Entity, LoadInput = number, SaveOutput = LoadInput, ValidationFailedType = string, LoadError = unknown, ValidationError = unknown, SaveError = unknown> = {
/** effect that takes an entity-id or null and returns a corresponding entity (which sets the default model) */
load: EffectId<LoadInput | null, Entity>;
load: EffectId<LoadInput | null, Entity, LoadError>;
/** effect that takes a {@link ModelWithDefault} for the entity and returns the corresponding {@link ModelValidationResult} */
validation: EffectId<ModelWithDefault<Entity>, ModelValidationResult<Entity, ValidationErrorType>>;
validation: EffectId<ModelWithDefault<Entity>, ModelValidationResult<Entity, ValidationFailedType>, ValidationError>;
/** effect that takes an entity and returns the id of the persisted entity */
save: EffectId<Entity, SaveOutput>;
save: EffectId<Entity, SaveOutput, SaveError>;
};

@@ -126,5 +129,8 @@ /**

* @template SaveOutput - specifies the output-type for the save-effect (defaults to LoadInput)
* @template ValidationErrorType - specifies the error-type for failed validations
* @template ValidationFailedType - specifies the type representing a failed validations (total or for a distinct property, hence `ModelValidationResult<Entity, ValidationFailedType>` will be used as validation result)
* @template LoadError - specifies the error type of the load-effect
* @template ValidationError - specifies the error type of the validation-effect (use never, if your validation cannot error)
* @template SaveError - specifies the error type of the result-effect
*/
export type EntityEditFactory<Entity, LoadInput = number, SaveOutput = LoadInput, ValidationErrorType = string> = SignalsFactory<EntityEditInput<Entity, LoadInput>, EntityEditOutput<Entity, LoadInput, SaveOutput, ValidationErrorType>, EntityEditConfiguration<Entity>, EntityEditEffects<Entity, LoadInput, SaveOutput, ValidationErrorType>>;
export type EntityEditFactory<Entity, LoadInput = number, SaveOutput = LoadInput, ValidationFailedType = string, LoadError = unknown, ValidationError = unknown, SaveError = unknown> = SignalsFactory<EntityEditInput<Entity, LoadInput>, EntityEditOutput<Entity, LoadInput, SaveOutput, ValidationFailedType, LoadError, ValidationError, SaveError>, EntityEditConfiguration<Entity>, EntityEditEffects<Entity, LoadInput, SaveOutput, ValidationFailedType, LoadError, ValidationError, SaveError>>;
/**

@@ -140,4 +146,7 @@ * Generic function to create a specific {@link EntityEditFactory}.

* @template SaveOutput - specifies the output-type for the save-effect (defaults to LoadInput)
* @template ValidationErrorType - specifies the error-type for failed validations
* @template ValidationFailedType - specifies the type representing a failed validations (total or for a distinct property, hence `ModelValidationResult<Entity, ValidationFailedType>` will be used as validation result)
* @template LoadError - specifies the error type of the load-effect
* @template ValidationError - specifies the error type of the validation-effect (use never, if your validation cannot error)
* @template SaveError - specifies the error type of the result-effect
*/
export declare const getEntityEditSignalsFactory: <Entity, LoadInput = number, SaveOutput = LoadInput, ValidationErrorType = string>() => EntityEditFactory<Entity, LoadInput, SaveOutput, ValidationErrorType>;
export declare const getEntityEditSignalsFactory: <Entity, LoadInput = number, SaveOutput = LoadInput, ValidationFailedType = string, LoadError = unknown, ValidationError = unknown, SaveError = unknown>() => EntityEditFactory<Entity, LoadInput, SaveOutput, ValidationFailedType, LoadError, ValidationError, SaveError>;
import { combineLatest, distinctUntilChanged, map, startWith } from 'rxjs';
import { filter, switchMap, take } from 'rxjs/operators';
import { getEffectSignalsFactory, } from './effect-signals-factory';
import { isNotEffectError } from './effect-result';
import { getEffectSignalsFactory, isCombinedEffectResultInCompletedSuccessState, } from './effect-signals-factory';
import { getModelSignalsFactory, } from './model-signals-factory';

@@ -44,3 +45,6 @@ import { NO_VALUE, getDerivedId, getEffectId, isNotNoValueType, } from './store-utils';

* @template SaveOutput - specifies the output-type for the save-effect (defaults to LoadInput)
* @template ValidationErrorType - specifies the error-type for failed validations
* @template ValidationFailedType - specifies the type representing a failed validations (total or for a distinct property, hence `ModelValidationResult<Entity, ValidationFailedType>` will be used as validation result)
* @template LoadError - specifies the error type of the load-effect
* @template ValidationError - specifies the error type of the validation-effect (use never, if your validation cannot error)
* @template SaveError - specifies the error type of the result-effect
*/

@@ -50,3 +54,3 @@ export const getEntityEditSignalsFactory = () => getEffectSignalsFactory() // model-fetch (fetching the edit entity)

.compose(getModelSignalsFactory()) // editing-model
.connectObservable((store, output) => store.getBehavior(output.result).pipe(map(result => result.result)), 'setAsDefault', true) // connecting entity model-fetch-result to editing-model
.connectObservable(({ store, output }) => store.getBehavior(output.combined).pipe(filter(isCombinedEffectResultInCompletedSuccessState), map(result => result.result)), 'setAsDefault', true) // connecting entity model-fetch-result to editing-model
.compose(getValidatedInputWithResultSignalsFactory()) // model validation and save

@@ -73,5 +77,11 @@ .connect('modelWithDefault', 'input', false) // connecting editing-model and vali-persist-input

.addEffectId('save', () => getEffectId())
.extendSetup((store, _, output, config, effects) => {
store.addEffect(effects.result, (modelWithResult, st, prevInput, prevResult) => st.getEffect(effects.save).pipe(take(1), // without this, the effect would never complete
switchMap(eff => eff(modelWithResult.model, st, isNotNoValueType(prevInput) ? prevInput.model : NO_VALUE, prevResult))));
.extendSetup(({ store, output, config, effects }) => {
store.addEffect(effects.result, (modelWithResult, args) => args.store.getEffect(effects.save).pipe(take(1), // without this, the effect would never complete
switchMap(eff => eff(modelWithResult.model, {
store: args.store,
previousInput: isNotNoValueType(args.previousInput)
? args.previousInput.model
: NO_VALUE,
previousResult: args.previousResult,
}))));
store.addDerivedState(output.combinedModel, combineLatest([

@@ -99,5 +109,7 @@ combineLatest([

: shallowEquals(edit.resultInput.model, edit.currentInput.model))),
edit.currentInput === edit.validatedInput && isNotNoValueType(edit.validationResult)
edit.currentInput === edit.validatedInput &&
isNotNoValueType(edit.validationResult) &&
isNotEffectError(edit.validationResult)
? edit.validationResult
: null,
: NO_VALUE,
]), distinctUntilChanged(([aload, aedit, aloading, adisabled, avalidation], [bload, bedit, bloading, bdisabled, bvalidation]) => aloading === bloading &&

@@ -126,3 +138,3 @@ adisabled === bdisabled &&

if (config.onSaveCompletedEvent) {
store.connectObservable(store.getEventStream(output.resultCompletedSuccesses).pipe(map(() => undefined)), config.onSaveCompletedEvent);
store.connectObservable(store.getEventStream(output.conflicts2.completedResults).pipe(filter(e => isNotEffectError(e.result)), map(() => undefined)), config.onSaveCompletedEvent);
}

@@ -142,19 +154,9 @@ })

.mapOutput((ids) => ({
load: {
combined: ids.conflicts1.combined,
result: ids.conflicts1.result,
pending: ids.pending,
successes: ids.successes,
completedSuccesses: ids.completedSuccesses,
errors: ids.errors,
},
load: ids.conflicts1,
edit: {
combined: ids.conflicts2.combined,
result: ids.conflicts2.result,
validationSuccesses: ids.validationSuccesses,
validationCompletedSuccesses: ids.validationCompletedSuccesses,
validationErrors: ids.validationErrors,
resultSuccesses: ids.resultSuccesses,
resultCompletedSuccesses: ids.resultCompletedSuccesses,
resultErrors: ids.resultErrors,
validationResults: ids.validationResults,
validationCompletedResults: ids.validationCompletedResults,
results: ids.conflicts2.results,
completedResults: ids.conflicts2.completedResults,
},

@@ -161,0 +163,0 @@ model: ids.combinedModel,

@@ -0,1 +1,2 @@

export * from './effect-result';
export * from './effect-signals-factory';

@@ -10,2 +11,1 @@ export * from './entity-edit-signals-factory';

export * from './validated-input-with-result-signals-factory';
export * from './void-effect-signals-factory';

@@ -0,1 +1,2 @@

export * from './effect-result';
export * from './effect-signals-factory';

@@ -10,3 +11,2 @@ export * from './entity-edit-signals-factory';

export * from './validated-input-with-result-signals-factory';
export * from './void-effect-signals-factory';
//# sourceMappingURL=index.js.map

@@ -112,2 +112,9 @@ import { Observable } from 'rxjs';

export type ComposedFactory<IN1 extends NameToSignalId, OUT1 extends NameToSignalId, CONFIG1 extends Configuration, EFF1 extends NameToEffectId, IN2 extends NameToSignalId, OUT2 extends NameToSignalId, CONFIG2 extends Configuration, EFF2 extends NameToEffectId> = SignalsFactory<Merged<IN1, IN2>, Merged<OUT1, OUT2>, MergedConfiguration<CONFIG1, CONFIG2>, Merged<EFF1, EFF2>>;
export type SignalsFactoryArgs<IN extends NameToSignalId, OUT extends NameToSignalId, CONFIG extends Configuration, EFF extends NameToEffectId> = {
store: Store;
input: IN;
output: OUT;
config: CONFIG;
effects: EFF;
};
/**

@@ -121,3 +128,3 @@ * This type specifies the argument to the {@link SignalsFactory.extendSetup} method.

*/
export type ExtendSetup<IN extends NameToSignalId, OUT extends NameToSignalId, CONFIG extends Configuration, EFF extends NameToEffectId> = (store: Store, input: IN, output: OUT, config: CONFIG, effects: EFF) => void;
export type ExtendSetup<IN extends NameToSignalId, OUT extends NameToSignalId, CONFIG extends Configuration, EFF extends NameToEffectId> = (args: SignalsFactoryArgs<IN, OUT, CONFIG, EFF>) => void;
/**

@@ -326,3 +333,3 @@ * Function mapping from CONFIG1 to CONFIG2

*/
connectObservable<K extends keyof IN, S extends ToSignalIdValueType<IN[K]>, O extends Observable<S>, B extends boolean>(sourceGetter: (store: Store, output: OUT, config: CONFIG) => O, inputName: K, keepInputId: B): B extends true ? SignalsFactory<IN, OUT, CONFIG, EFF> : SignalsFactory<Omit<IN, K>, OUT, CONFIG, EFF>;
connectObservable<K extends keyof IN, S extends ToSignalIdValueType<IN[K]>, O extends Observable<S>, B extends boolean>(sourceGetter: (args: SignalsFactoryArgs<IN, OUT, CONFIG, EFF>) => O, inputName: K, keepInputId: B): B extends true ? SignalsFactory<IN, OUT, CONFIG, EFF> : SignalsFactory<Omit<IN, K>, OUT, CONFIG, EFF>;
/**

@@ -428,3 +435,3 @@ * The `mapConfig` method takes as argument a pure function that implements `MapConfig<CONFIG, CONFIG2>`.

*/
mapOutputBehavior<TNEW, KOUT extends keyof WithValueType<OUT, BehaviorId<any>>>(outputName: KOUT, mapper: (old: Observable<ToBehaviorIdValueType<OUT[KOUT]>>, store: Store, input: IN, output: OUT, config: CONFIG) => Observable<TNEW>): SignalsFactory<IN, AddOrReplaceId<OUT, KOUT, DerivedId<TNEW>>, CONFIG, EFF>;
mapOutputBehavior<TNEW, KOUT extends keyof WithValueType<OUT, BehaviorId<any>>>(outputName: KOUT, mapper: (old: Observable<ToBehaviorIdValueType<OUT[KOUT]>>, args: SignalsFactoryArgs<IN, OUT, CONFIG, EFF>) => Observable<TNEW>): SignalsFactory<IN, AddOrReplaceId<OUT, KOUT, DerivedId<TNEW>>, CONFIG, EFF>;
/**

@@ -431,0 +438,0 @@ * The `mapEffects` method takes as argument a pure function that implements `MapEffectIds<EFF, EFF2, CONFIG>`.

@@ -105,3 +105,3 @@ var __rest = (this && this.__rest) || function (s, e) {

s.setup(store);
extend(store, s.input, s.output, config, s.effects);
extend({ store, input: s.input, output: s.output, config, effects: s.effects });
} });

@@ -129,3 +129,3 @@ });

connect(outputName, inputName, keepInputId) {
const fnew = this.extendSetup((store, input, output) => {
const fnew = this.extendSetup(({ store, input, output }) => {
const fromId = output[outputName];

@@ -149,3 +149,3 @@ const toId = input[inputName];

connectId(fromId, inputName, keepInputId) {
const fnew = this.extendSetup((store, input) => {
const fnew = this.extendSetup(({ store, input }) => {
const toId = input[inputName];

@@ -168,5 +168,5 @@ store.connect(fromId, toId);

connectObservable(sourceGetter, inputName, keepInputId) {
const fnew = this.extendSetup((st, ip, op, conf) => {
const toId = ip[inputName];
st.connectObservable(sourceGetter(st, op, conf), toId);
const fnew = this.extendSetup(args => {
const toId = args.input[inputName];
args.store.connectObservable(sourceGetter(args), toId);
});

@@ -323,3 +323,9 @@ const result = (keepInputId ? fnew : fnew.removeInputId(inputName));

return Object.assign(Object.assign({}, s), { setup: store => {
store.addDerivedState(newId, mapper(store.getBehavior(oldId), store, s.input, s.output, config));
store.addDerivedState(newId, mapper(store.getBehavior(oldId), {
store,
input: s.input,
output: s.output,
config,
effects: s.effects,
}));
s.setup(store);

@@ -381,3 +387,3 @@ }, output: Object.assign(Object.assign({}, s.output), { [outputName]: newId }) });

useExistingEffect(name, idGetter, keepEffectId) {
const result = this.extendSetup((store, _, _2, config, effects) => {
const result = this.extendSetup(({ store, config, effects }) => {
store

@@ -384,0 +390,0 @@ .getEffect(idGetter(config))

@@ -115,3 +115,3 @@ import { Observable } from 'rxjs';

* The rx-signals `Store` uses this type to uniquely identify all of its result effects.
* An `EffectId<InputType, ResultType>` does not make any use of the generic parameters itself,
* An `EffectId<InputType, ResultType, ErrorType>` does not make any use of the generic parameters itself,
* but is given these parameters only as a trick to let Typescript infer and thus enforce the correct types.

@@ -122,6 +122,8 @@ * Use the {@link getEffectId} function to generate a corresponding ID.

* @template ResultType - specifies the type for the corresponding effects result
* @template ErrorType - specifies the type error-type for the effect. Use never for effects that cannot error.
*/
export type EffectId<InputType, ResultType> = symbol & {
export type EffectId<InputType, ResultType, ErrorType = unknown> = symbol & {
_inputType: InputType;
_resultType: ResultType;
_errorType: ErrorType;
};

@@ -157,6 +159,7 @@ /**

* @template ResultType - specifies the type for the corresponding effects result
* @template ErrorType - specifies the type error-type for the effect. Use `never` for effects that cannot error.
* @param {string} nameExtension - an optional extension to the symbol name (so the string representation). Usually you should not need this.
* @returns {EventId<T>}
*/
export declare const getEffectId: <InputType, ResultType>(nameExtension?: string) => EffectId<InputType, ResultType>;
export declare const getEffectId: <InputType, ResultType, ErrorType = unknown>(nameExtension?: string) => EffectId<InputType, ResultType, ErrorType>;
/**

@@ -163,0 +166,0 @@ * Typeguard to check whether a given `SignalId` is a `StateId`.

@@ -34,2 +34,3 @@ let stateExtension = 1;

* @template ResultType - specifies the type for the corresponding effects result
* @template ErrorType - specifies the type error-type for the effect. Use `never` for effects that cannot error.
* @param {string} nameExtension - an optional extension to the symbol name (so the string representation). Usually you should not need this.

@@ -36,0 +37,0 @@ * @returns {EventId<T>}

import { Observable } from 'rxjs';
import { EffectResult } from './effect-result';
import { BehaviorId, DerivedId, EffectId, EventId, NoValueType, SignalId, StateId, ToBehaviorIdValueType, ToEventIdValueType, ToSignalIdValueType } from './store-utils';

@@ -33,3 +34,12 @@ /**

};
/** universal type used for unhandled errors from effects */
export type UnhandledEffectError = {
unhandledError: unknown;
};
/**
* The usual `EffectResult` extended by `EffectError<UnhandledEffectError>`,
* to provide a type that also for effect consumers that handle unhandled effect errors
*/
export type SafeEffectResult<Result, Error> = EffectResult<Result, Error | UnhandledEffectError>;
/**
* The `Effect<Input>` type specifies a potentially impure function that takes an input and a {@link Store} as arguments

@@ -40,19 +50,23 @@ * and returns an effectful result as `Observable<Result>`.

* the corresponding `Effect`.
* The store argument can be used to access additional input from the store (thus, the `Effect` itself could also
* be pure and just use something impure that was put into the store, e.g. another `Effect`).
* The previousInput argument can be used e.g. to decide whether the effect must perform
* The args can be used to access additional input from the store (thus, the `Effect` itself could also
* be pure and just use something impure that was put into the store, e.g. another `Effect`),
* as well as the previousInput and previousResult.
* The previousInput can be used e.g. to decide whether the effect must perform
* a computation/query/etc., or if maybe the previousResult can be returned directly.
* If the effect cannot error, use `never` as `Error` type. In that case, `EffectResult<Result, Error>` will equal `Result`.
*
* @template Input - specifies the input type for the effect
* @template Result - specifies the result type for the effect
* @template Error - specifies the error type for the effect. Specify never, if the effect cannot error.
*/
export type Effect<Input, Result> = (
export type Effect<Input, Result, Error = unknown> = (
/** the effect input */
input: Input,
/** the Store instance that will be passed to the function (e.g. to inject some other Effect) */
store: Store,
/** the input of the previous function invocation, or NO_VALUE */
previousInput: Input | NoValueType,
/** the result of the previous function invocation, or NO_VALUE */
previousResult: Result | NoValueType) => Observable<Result>;
input: Input, args: {
/** the Store instance that will be passed to the function (e.g. to inject some other Effect) */
store: Store;
/** the input of the previous function invocation, or NO_VALUE */
previousInput: Input | NoValueType;
/** `SafeEffectResult` instead of `EffectResult`, to give consumers the option to provide unhandled errors */
previousResult: SafeEffectResult<Result, Error> | NoValueType;
}) => Observable<EffectResult<Result, Error>>;
/**

@@ -64,3 +78,3 @@ * ToEffectType is a utility type to get the corresponding Effect type

*/
export type ToEffectType<ID> = ID extends EffectId<infer I, infer O> ? Effect<I, O> : never;
export type ToEffectType<ID> = ID extends EffectId<infer I, infer O, infer E> ? Effect<I, O, E> : never;
/**

@@ -370,3 +384,3 @@ * The rx-signals Store provides RxJs-Observables for RP (reactive programming) - BehaviorStreams

*/
addEffect<ID extends EffectId<any, any>>(id: ID, effect: ToEffectType<ID>): void;
addEffect<ID extends EffectId<any, any, any>>(id: ID, effect: ToEffectType<ID>): void;
/**

@@ -381,6 +395,6 @@ * This method returns an `Observable` for the effect specified by identifier.

*
* @param {EffectId<InputType, ResultType>} id - the unique identifier for the effect
* @returns {Observable<Effect<InputType, ResultType>>} - the effect observable
* @param {EffectId<InputType, ResultType, ErrorType>} id - the unique identifier for the effect
* @returns {Observable<Effect<InputType, ResultType, ErrorType>>} - the effect observable
*/
getEffect<InputType, ResultType>(id: EffectId<InputType, ResultType>): Observable<Effect<InputType, ResultType>>;
getEffect<InputType, ResultType, ErrorType>(id: EffectId<InputType, ResultType, ErrorType>): Observable<Effect<InputType, ResultType, ErrorType>>;
/**

@@ -387,0 +401,0 @@ * The `isSubscribed` method is a convenience method for testing and debugging and should

@@ -531,4 +531,4 @@ import { BehaviorSubject, NEVER, asyncScheduler, delay, distinctUntilChanged, filter, firstValueFrom, map, merge, of, share, switchMap, take, withLatestFrom, } from 'rxjs';

*
* @param {EffectId<InputType, ResultType>} id - the unique identifier for the effect
* @returns {Observable<Effect<InputType, ResultType>>} - the effect observable
* @param {EffectId<InputType, ResultType, ErrorType>} id - the unique identifier for the effect
* @returns {Observable<Effect<InputType, ResultType, ErrorType>>} - the effect observable
*/

@@ -535,0 +535,0 @@ getEffect(id) {

@@ -1,3 +0,4 @@

import { CombinedEffectResultInSuccessState, EffectCompletedSuccess, EffectError, EffectSuccess } from './effect-signals-factory';
import { EffectCompletedResultEvent, EffectResultEvent } from './effect-signals-factory';
import { SignalsFactory } from './signals-factory';
import { SafeEffectResult } from './store';
import { DerivedId, EffectId, EventId, NoValueType } from './store-utils';

@@ -10,4 +11,6 @@ /**

* @template Result - specifies the result-type of the result-effect
* @template ValidationError - specifies the error type of the validation-effect
* @template ResultError - specifies the error type of the result-effect
*/
export type ValidatedInputWithResult<Input, ValidationResult, Result> = {
export type ValidatedInputWithResult<Input, ValidationResult, Result, ValidationError, ResultError> = {
/** current input (which might differ from the resultInput) */

@@ -20,3 +23,3 @@ currentInput: Input | NoValueType;

/** the current validationResult (or NO_VALUE, if no validation-result was received yet) */
validationResult: ValidationResult | NoValueType;
validationResult: SafeEffectResult<ValidationResult, ValidationError> | NoValueType;
/** only true if validationResult represents a valid state AND validationPending is false */

@@ -28,5 +31,4 @@ isValid: boolean;

resultInput: Input | NoValueType;
resultError?: any;
/** the current result (or NO_VALUE, if no result was received yet) */
result: Result | NoValueType;
result: SafeEffectResult<Result, ResultError> | NoValueType;
};

@@ -45,11 +47,8 @@ /**

*/
export type ValidatedInputWithResultOutput<Input, ValidationResult, Result> = {
combined: DerivedId<ValidatedInputWithResult<Input, ValidationResult, Result>>;
result: DerivedId<CombinedEffectResultInSuccessState<Input, Result>>;
validationErrors: EventId<EffectError<Input>>;
validationSuccesses: EventId<EffectSuccess<Input, ValidationResult>>;
validationCompletedSuccesses: EventId<EffectCompletedSuccess<Input, ValidationResult>>;
resultErrors: EventId<EffectError<Input>>;
resultSuccesses: EventId<EffectSuccess<Input, Result>>;
resultCompletedSuccesses: EventId<EffectCompletedSuccess<Input, Result>>;
export type ValidatedInputWithResultOutput<Input, ValidationResult, Result, ValidationError, ResultError> = {
combined: DerivedId<ValidatedInputWithResult<Input, ValidationResult, Result, ValidationError, ResultError>>;
validationResults: EventId<EffectResultEvent<Input, ValidationResult, ValidationError>>;
validationCompletedResults: EventId<EffectCompletedResultEvent<Input, ValidationResult, ValidationError>>;
results: EventId<EffectResultEvent<Input, Result, ResultError>>;
completedResults: EventId<EffectCompletedResultEvent<Input, Result, ResultError>>;
};

@@ -59,4 +58,5 @@ /**

*/
export type ValidatedInputWithResultConfig<Input, ValidationResult, Result> = {
isValidationResultValid?: (validationResult: ValidationResult) => boolean;
export type ValidatedInputWithResultConfig<Input, ValidationResult, Result, ValidationError> = {
/** whether the a validation result represents a valid state, defaults to `isNotEffectError(validationResult) && (validationResult ?? null) === null` */
isValidationResultValid?: (validationResult: SafeEffectResult<ValidationResult, ValidationError>) => boolean;
validationEffectDebounceTime?: number;

@@ -73,5 +73,5 @@ resultEffectDebounceTime?: number;

*/
export type ValidatedInputWithResultEffects<Input, ValidationResult, Result> = {
validation: EffectId<Input, ValidationResult>;
result: EffectId<Input, Result>;
export type ValidatedInputWithResultEffects<Input, ValidationResult, Result, ValidationError, ResultError> = {
validation: EffectId<Input, ValidationResult, ValidationError>;
result: EffectId<Input, Result, ResultError>;
};

@@ -83,6 +83,6 @@ /**

*/
export type ValidatedInputWithResultFactory<Input, ValidationResult, Result> = SignalsFactory<ValidatedInputWithResultInput<Input>, ValidatedInputWithResultOutput<Input, ValidationResult, Result>, ValidatedInputWithResultConfig<Input, ValidationResult, Result>, ValidatedInputWithResultEffects<Input, ValidationResult, Result>>;
export type ValidatedInputWithResultFactory<Input, ValidationResult, Result, ValidationError, ResultError> = SignalsFactory<ValidatedInputWithResultInput<Input>, ValidatedInputWithResultOutput<Input, ValidationResult, Result, ValidationError, ResultError>, ValidatedInputWithResultConfig<Input, ValidationResult, Result, ValidationError>, ValidatedInputWithResultEffects<Input, ValidationResult, Result, ValidationError, ResultError>>;
/**
* Generic function to create a specific {@link ValidatedInputWithResultFactory}.
*/
export declare const getValidatedInputWithResultSignalsFactory: <Input, ValidationResult, Result>() => ValidatedInputWithResultFactory<Input, ValidationResult, Result>;
export declare const getValidatedInputWithResultSignalsFactory: <Input, ValidationResult, Result, ValidationError, ResultError>() => ValidatedInputWithResultFactory<Input, ValidationResult, Result, ValidationError, ResultError>;
import { combineLatest, distinctUntilChanged, filter, map, startWith } from 'rxjs';
import { getEffectSignalsFactory, } from './effect-signals-factory';
import { NO_VALUE, getDerivedId, } from './store-utils';
import { NO_VALUE, getDerivedId, isNoValueType, isNotNoValueType, } from './store-utils';
const isResultInputGetterInput = (c) => !c.resultPending &&
c.resultInput !== NO_VALUE &&
c.result !== NO_VALUE &&
isNotNoValueType(c.resultInput) &&
isNotNoValueType(c.result) &&
c.currentInput === c.resultInput;
const resultInputGetter = (store, validationBehaviorId, isValidationResultValid) => store.getBehavior(validationBehaviorId).pipe(filter(isResultInputGetterInput), filter(c => isValidationResultValid(c.result)), map(c => c.resultInput), distinctUntilChanged());
const mapBehaviors = ([v, r], isValidationResultValid) => (Object.assign(Object.assign({ currentInput: v.currentInput, validationPending: v.resultPending, validatedInput: v.resultInput, validationResult: v.result, isValid: !v.resultPending && v.result !== NO_VALUE ? isValidationResultValid(v.result) : false, resultPending: r.resultPending, resultInput: r.resultInput }, (r.resultError ? { resultError: r.resultError } : {})), { result: r.result }));
const mapBehaviors = ([v, r], isValidationResultValid) => ({
currentInput: v.currentInput,
validationPending: v.resultPending,
validatedInput: v.resultInput,
validationResult: v.result,
isValid: !v.resultPending && isNotNoValueType(v.result) ? isValidationResultValid(v.result) : false,
resultPending: r.resultPending,
resultInput: r.resultInput,
result: r.result,
});
const setupCombinedBehavior = (store, outIds, id, isValidationResultValid, initialResultGetter) => {

@@ -21,3 +30,3 @@ store.addDerivedState(id, combineLatest([

r.currentInput === v.resultInput ||
v.result === NO_VALUE ||
isNoValueType(v.result) ||
!isValidationResultValid(v.result)), map(pair => mapBehaviors(pair, isValidationResultValid)), distinctUntilChanged((a, b) => a.currentInput === b.currentInput &&

@@ -28,3 +37,2 @@ a.isValid === b.isValid &&

a.resultPending === b.resultPending &&
a.resultError === b.resultError &&
a.validatedInput === b.validatedInput &&

@@ -58,5 +66,5 @@ a.validationPending === b.validationPending &&

})
.extendSetup((store, inIds, outIds, config) => {
.extendSetup(({ store, input, output, config }) => {
var _a;
store.connectObservable(resultInputGetter(store, outIds.conflicts1.combined, (_a = config.isValidationResultValid) !== null && _a !== void 0 ? _a : (validationResult => validationResult === null)), inIds.conflicts2.input);
store.connectObservable(resultInputGetter(store, output.conflicts1.combined, (_a = config.isValidationResultValid) !== null && _a !== void 0 ? _a : (validationResult => validationResult === null)), input.conflicts2.input);
})

@@ -67,5 +75,5 @@ .addOutputId('combined', config => {

})
.extendSetup((store, _, output, config) => {
.extendSetup(({ store, output, config }) => {
var _a;
setupCombinedBehavior(store, output, output.combined, (_a = config.isValidationResultValid) !== null && _a !== void 0 ? _a : (validationResult => validationResult === null), config.initialResultGetter);
setupCombinedBehavior(store, output, output.combined, (_a = config.isValidationResultValid) !== null && _a !== void 0 ? _a : (validationResult => (validationResult !== null && validationResult !== void 0 ? validationResult : null) === null), config.initialResultGetter);
})

@@ -80,10 +88,7 @@ .mapInput(input => ({

combined: output.combined,
result: output.conflicts2.result,
validationErrors: output.conflicts1.errors,
validationSuccesses: output.conflicts1.successes,
validationCompletedSuccesses: output.conflicts1.completedSuccesses,
resultErrors: output.conflicts2.errors,
resultSuccesses: output.conflicts2.successes,
resultCompletedSuccesses: output.conflicts2.completedSuccesses,
validationResults: output.conflicts1.results,
validationCompletedResults: output.conflicts1.completedResults,
results: output.conflicts2.results,
completedResults: output.conflicts2.completedResults,
}));
//# sourceMappingURL=validated-input-with-result-signals-factory.js.map
{
"name": "@rx-signals/store",
"version": "3.0.0-rc43",
"version": "3.0.0-rc44",
"description": "Reactive state- and effects-management with behaviors and event streams",

@@ -5,0 +5,0 @@ "author": "Gerd Neudert",

@@ -6,3 +6,3 @@ # _@rx-signals/store_

:warning: This documentation is work in progress for the upcoming 3.0.0 version.
There is however NO good reason to use 2.x over 3.0.0-rc43, so please start with the rc-version (3.0.0 will be the first version I'm going to advertise publicly, so it's more like a 1.0 in reality.).
There is however NO good reason to use 2.x over 3.0.0-rc44, so please start with the rc-version (3.0.0 will be the first version I'm going to advertise publicly, so it's more like a 1.0 in reality.).
2.x is deprecated and will NOT be maintained in any way.

@@ -15,3 +15,3 @@

**`npm install --save @rx-signals/store@3.0.0-rc43`**
**`npm install --save @rx-signals/store@3.0.0-rc44`**

@@ -18,0 +18,0 @@ ## Dependencies

import {
Observable,
catchError,
combineLatest,

@@ -13,4 +12,12 @@ debounceTime,

} from 'rxjs';
import {
EffectError,
EffectResult,
ToEffectError,
isEffectError,
isNotEffectError,
toEffectError,
} from './effect-result';
import { Signals, SignalsFactory } from './signals-factory';
import { Effect, Store } from './store';
import { Effect, SafeEffectResult, Store, UnhandledEffectError } from './store';
import {

@@ -35,4 +42,5 @@ DerivedId,

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type CombinedEffectResult<Input, Result> = {
export type CombinedEffectResult<Input, Result, Error> = {
/**

@@ -45,10 +53,10 @@ * The current input (which might differ from the resultInput),

/**
* The current result,
* The current effect-result,
* or `NO_VALUE` if no result was received yet,
* or the effect produced an error
*/
result: Result | NoValueType;
result: SafeEffectResult<Result, Error> | NoValueType;
/**
* The input that produced the current result,
* The input that produced the current effect-result,
* or `NO_VALUE`, if initial result or no result received yet */

@@ -58,9 +66,5 @@ resultInput: Input | NoValueType;

/**
* In case the effect led to an error (`result === NO_VALUE` in that case), else undefined */
resultError?: any;
/**
* Indicates whether the effect is currently running.
* In case of a factory without trigger, this will be true whenever one or multiple
* of the following conditions is met:
* of the following conditions are met:
* `currentInput !== resultInput`,

@@ -79,8 +83,8 @@ * or an invalidation event has been sent,

* @template Input - specifies the input type for the effect
* @template Error - specifies the error type of the effect
*/
export type CombinedEffectResultInErrorState<Input> = {
export type CombinedEffectResultInErrorState<Input, Error> = {
currentInput: Input;
result: NoValueType;
result: ToEffectError<Error> | EffectError<UnhandledEffectError>;
resultInput: Input;
resultError: any;
resultPending: false;

@@ -92,12 +96,8 @@ };

*/
export const isCombinedEffectResultInErrorState = <Input, Result>(
cer: CombinedEffectResult<Input, Result>,
): cer is CombinedEffectResultInErrorState<Input> =>
!!cer.resultError &&
!cer.resultPending &&
isNoValueType(cer.result) &&
isNotNoValueType(cer.resultInput);
export const isCombinedEffectResultInErrorState = <Input, Result, Error>(
cer: CombinedEffectResult<Input, Result, Error>,
): cer is CombinedEffectResultInErrorState<Input, Error> => isEffectError(cer.result);
/**
* Type representing a {@link CombinedEffectResult} in it's success state (non-pending)
* Type representing a {@link CombinedEffectResult} in it's success state (pending or non-pending)
*

@@ -111,3 +111,3 @@ * @template Input - specifies the input type for the effect

resultInput: Input | NoValueType;
resultPending: false;
resultPending: boolean;
};

@@ -118,26 +118,33 @@

*/
export const isCombinedEffectResultInSuccessState = <Input, Result>(
cer: CombinedEffectResult<Input, Result>,
): cer is CombinedEffectResultInSuccessState<Input, Result> =>
!cer.resultError && !cer.resultPending && isNotNoValueType(cer.result);
export const isCombinedEffectResultInSuccessState = <Input, Result, Error>(
cer: CombinedEffectResult<Input, Result, Error>,
): cer is CombinedEffectResultInSuccessState<Input, Result> => isNotEffectError(cer.result);
/**
* Value-type for error events produced by an `Effect<Input, any>` (unhandled effect errors).
* Type representing a {@link CombinedEffectResult} in it's success state (non-pending, hece completed effect)
*
* @template Input - specifies the input type for the effect
* @template Result - specifies the result type of the effect
*/
export type EffectError<Input> = {
/** the unhandled error thrown by an effect */
error: any;
/** the effect input that lead to the error */
errorInput: Input;
export type CombinedEffectResultInCompletedSuccessState<Input, Result> = {
currentInput: Input | NoValueType;
result: Result;
resultInput: Input | NoValueType;
resultPending: false;
};
/**
* Value-type for success events produced by {@link EffectSignals}.
* In case the effect completes after one result, two success events will
* Typeguard to check if a {@link CombinedEffectResult} is a {@link CombinedEffectResultInCompletedSuccessState}
*/
export const isCombinedEffectResultInCompletedSuccessState = <Input, Result, Error>(
cer: CombinedEffectResult<Input, Result, Error>,
): cer is CombinedEffectResultInCompletedSuccessState<Input, Result> =>
isNotEffectError(cer.result) && !cer.resultPending;
/**
* Value-type for result events produced by {@link EffectSignals}.
* In case the effect completes after one result, two reult events will
* be dispatched, one with completed false and one with completed true.
* This is to handle cases where the effect might send multiple results
* before completing. Thus, if an effect never completes, all success event
* before completing. Thus, if an effect never completes, all result events
* will have completed false.

@@ -147,6 +154,7 @@ *

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectSuccess<Input, Result> = {
export type EffectResultEvent<Input, Result, Error> = {
/** the effect result */
result: Result;
result: SafeEffectResult<Result, Error>;

@@ -157,6 +165,6 @@ /** the effect input that lead to the result */

/** the input of the previous completed result, or `NO_VALUE` */
previousInput: Input | NoValueType;
// previousInput: Input | NoValueType;
/** the previous completed result, or `NO_VALUE` */
previousResult: Result | NoValueType;
// previousResult: SafeEffectResult<Result, Error> | NoValueType;

@@ -168,4 +176,4 @@ /** has the effect for the given resultInput completed */

/**
* Value-type for completed success events produced by {@link EffectSignals}.
* In contrast to {@link EffectSuccess} events with this type are only dispatched,
* Value-type for completed result events produced by {@link EffectSignals}.
* In contrast to {@link EffectResultEvent}, events with this type are only dispatched,
* if the effect has completed, hence it will never be fired for effects that never complete.

@@ -175,11 +183,15 @@ *

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectCompletedSuccess<Input, Result> = Omit<
EffectSuccess<Input, Result>,
export type EffectCompletedResultEvent<Input, Result, Error> = Omit<
EffectResultEvent<Input, Result, Error>,
'completed'
> & { completed: true };
const isCompletedSuccess = <Input, Result>(
value: EffectCompletedSuccess<Input, Result> | EffectSuccess<Input, Result>,
): value is EffectCompletedSuccess<Input, Result> => value.completed;
/**
* Typeguard to check if a {@link EffectResultEvent} is a {@link EffectCompletedResultEvent}
*/
export const isCompletedResultEvent = <Input, Result, Error>(
value: EffectCompletedResultEvent<Input, Result, Error> | EffectResultEvent<Input, Result, Error>,
): value is EffectCompletedResultEvent<Input, Result, Error> => value.completed;

@@ -210,26 +222,18 @@ /**

* Type specifying the output {@link EffectSignals} (signals produced by `EffectSignals`).
* The {@link EffectSignalsFactory} takes care that subscribing error- or success-events keeps
* the effect itself lazy (hence only subscribing the combined behavior will subscribe the effect itself).
* The {@link EffectSignalsFactory} takes care that subscribing the result-events keeps
* the effect itself lazy (hence only subscribing the combined behavior will subscribe the effect itself)!
*
* @template Input - specifies the input type for the effect
* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectOutputSignals<Input, Result> = {
export type EffectOutputSignals<Input, Result, Error> = {
/** Produced combined effect result behavior */
combined: DerivedId<CombinedEffectResult<Input, Result>>;
combined: DerivedId<CombinedEffectResult<Input, Result, Error>>;
/** Convenience behavior derived from combined-behavior, representing only completed success states */
result: DerivedId<CombinedEffectResultInSuccessState<Input, Result>>;
/** Convenience behavior derived from combined-behavior, representing only pending state */
pending: DerivedId<boolean>;
/** Produced error events */
errors: EventId<EffectError<Input>>;
/** Produced success events */
successes: EventId<EffectSuccess<Input, Result>>;
results: EventId<EffectResultEvent<Input, Result, Error>>;
/** Produced success events */
completedSuccesses: EventId<EffectCompletedSuccess<Input, Result>>;
completedResults: EventId<EffectCompletedResultEvent<Input, Result, Error>>;
};

@@ -243,5 +247,6 @@

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectFactoryEffects<Input, Result> = {
id: EffectId<Input, Result>;
export type EffectFactoryEffects<Input, Result, Error> = {
id: EffectId<Input, Result, Error>;
};

@@ -253,10 +258,9 @@

* ```markdown
* 1.) The produced CombinedEffectResult<Input, Result> behavior must be lazy, hence, as long as it is not subscribed,
* no effect will be triggered.
* 2.) Unhandled effect errors are caught and dispatched as EffectError<Input>. A subscription of the corresponding
* errors event stream will NOT subscribe the result behavior (see requirement 1).
* 3.) In addition to the result behavior, also an event-stream for EffectSuccess<Input, Result> is provided. This is important
* in cases where an effect success should be used to trigger something else (e.g. close a popup), but you cannot use the result
* behavior, because it would mean to always subscribe the result. In contrast, subscription of the success event stream will NOT
* subscribe the result behavior. (the same holds true for the completedSuccesses event-stream)
* 1.) The produced CombinedEffectResult<Input, Result, Error> behavior must be lazy, hence, as long as it is not subscribed,
* no effect will be triggered (so subscribing just results, or completedResults will not trigger the effect).
* 2.) Unhandled effect errors are caught and will lead to an EffectError<UnhandledEffectError>.
* 3.) In addition to the combined-behavior, also event-streams for EffectResultEvent and EffectCompletedResultEvent are provided. This is important
* in cases where e.g. an effect success should be used to trigger something else (e.g. close a popup), but you cannot use the result
* behavior, because it would mean to always subscribe the result. In contrast, subscription of the an rsult-event-stream will NOT
* subscribe the effect.
* ```

@@ -268,7 +272,8 @@ *

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectSignals<Input, Result> = Signals<
export type EffectSignals<Input, Result, Error> = Signals<
EffectInputSignals<Input>,
EffectOutputSignals<Input, Result>,
EffectFactoryEffects<Input, Result>
EffectOutputSignals<Input, Result, Error>,
EffectFactoryEffects<Input, Result, Error>
>;

@@ -281,4 +286,5 @@

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectConfiguration<Input, Result> = {
export type EffectConfiguration<Input, Result, Error> = {
/** Function used to determine whether a new input equals the previous one. Defaults to strict equals (`a === b`) */

@@ -297,3 +303,3 @@ effectInputEquals?: (a: Input, b: Input) => boolean;

/** Function to wrap the effect defined by effectId with a custom `Effect` */
wrappedEffectGetter?: (effect: Effect<Input, Result>) => Effect<Input, Result>;
wrappedEffectGetter?: (effect: Effect<Input, Result, Error>) => Effect<Input, Result, Error>;

@@ -312,7 +318,8 @@ /** Specifies whether the input behavior should be subscribed eagerly (defaults to false) */

* @template Result - specifies the result type of the effect
* @param {EffectConfiguration<Input, Result>} config - the configuration for the `EffectSignals`
* @template Error - specifies the error type of the effect
* @param {EffectConfiguration<Input, Result, Error>} config - the configuration for the `EffectSignals`
*/
export type EffectSignalsBuild = <Input, Result>(
config: EffectConfiguration<Input, Result>,
) => EffectSignals<Input, Result>;
export type EffectSignalsBuild = <Input, Result, Error>(
config: EffectConfiguration<Input, Result, Error>,
) => EffectSignals<Input, Result, Error>;

@@ -325,29 +332,25 @@ const getInputSignalIds = <Input>(nameExtension?: string): EffectInputSignals<Input> => ({

const getOutputSignalIds = <Input, Result>(
const getOutputSignalIds = <Input, Result, Error>(
nameExtension?: string,
): EffectOutputSignals<Input, Result> => ({
combined: getDerivedId<CombinedEffectResult<Input, Result>>(`${nameExtension ?? ''}_combined`),
result: getDerivedId<CombinedEffectResultInSuccessState<Input, Result>>(
`${nameExtension ?? ''}_result`,
): EffectOutputSignals<Input, Result, Error> => ({
combined: getDerivedId<CombinedEffectResult<Input, Result, Error>>(
`${nameExtension ?? ''}_combined`,
),
pending: getDerivedId<boolean>(`${nameExtension ?? ''}_pending`),
errors: getEventId<EffectError<Input>>(`${nameExtension ?? ''}_errors`),
successes: getEventId<EffectSuccess<Input, Result>>(`${nameExtension ?? ''}_successes`),
completedSuccesses: getEventId<EffectCompletedSuccess<Input, Result>>(
`${nameExtension ?? ''}_completedSuccesses`,
results: getEventId<EffectResultEvent<Input, Result, Error>>(`${nameExtension ?? ''}_results`),
completedResults: getEventId<EffectCompletedResultEvent<Input, Result, Error>>(
`${nameExtension ?? ''}_completedResults`,
),
});
const NO_VALUE_TRIGGERED_INPUT = '$INTERNAL_NV_TI$';
const NO_VALUE_TRIGGERED_INPUT = '$RXS_INTERNAL_NV_TI$';
type NoValueTriggeredInput = typeof NO_VALUE_TRIGGERED_INPUT;
type InternalEffectResult<RT> = {
result: RT;
type InternalEffectResult<RT, Error> = {
result: SafeEffectResult<RT, Error>;
completed: boolean;
};
type InternalResultType<Input, Result> = {
result: Result | NoValueType;
type InternalResultType<Input, R> = {
result: R | NoValueType;
resultInput: Input | NoValueType;
resultError?: any;
resultToken: object | null;

@@ -357,6 +360,6 @@ };

const getIsNewInput =
<Input, Result>(effectInputEquals: (a: Input, b: Input) => boolean) =>
<Input, Result, Error>(effectInputEquals: (a: Input, b: Input) => boolean) =>
([input, resultState, token]: [
Input,
InternalResultType<Input, InternalEffectResult<Result>>,
InternalResultType<Input, InternalEffectResult<Result, Error>>,
object | null,

@@ -369,13 +372,15 @@ Input | NoValueTriggeredInput,

const getEffectBuilder: EffectSignalsBuild = <IT, RT>(
config: EffectConfiguration<IT, RT>,
): EffectSignals<IT, RT> => {
const effectId = getEffectId<IT, RT>();
const getEffectBuilder: EffectSignalsBuild = <IT, RT, ER>(
config: EffectConfiguration<IT, RT, ER>,
): EffectSignals<IT, RT, ER> => {
const effectId = getEffectId<IT, RT, ER>();
const wrappedResultEffect = (
input: IT,
store: Store,
previousInput: IT | NoValueType,
previousResult: RT | NoValueType,
args: {
store: Store;
previousInput: IT | NoValueType;
previousResult: SafeEffectResult<RT, ER> | NoValueType;
},
) =>
store.getEffect(effectId).pipe(
args.store.getEffect(effectId).pipe(
take(1),

@@ -387,3 +392,3 @@ switchMap(effect => {

: effect;
return wrappedEffect(input, store, previousInput, previousResult);
return wrappedEffect(input, args);
} catch (error) {

@@ -396,14 +401,11 @@ return throwError(() => error);

input: IT,
store: Store,
previousInput: IT | NoValueType,
previousResult: RT | NoValueType,
): Observable<InternalEffectResult<RT>> =>
new Observable<InternalEffectResult<RT>>(subscriber => {
let currentResult: RT | NoValueType = NO_VALUE;
const subscription = wrappedResultEffect(
input,
store,
previousInput,
previousResult,
).subscribe({
args: {
store: Store;
previousInput: IT | NoValueType;
previousResult: SafeEffectResult<RT, ER> | NoValueType;
},
): Observable<InternalEffectResult<RT, ER>> =>
new Observable<InternalEffectResult<RT, ER>>(subscriber => {
let currentResult: EffectResult<RT, ER> | NoValueType = NO_VALUE;
const subscription = wrappedResultEffect(input, args).subscribe({
next: result => {

@@ -427,3 +429,9 @@ currentResult = result;

error: e => {
subscriber.error(e);
subscriber.next({
result: toEffectError<UnhandledEffectError>({
unhandledError: e,
}),
completed: true,
});
subscriber.complete();
currentResult = NO_VALUE;

@@ -439,6 +447,6 @@ },

const effectInputEquals = config.effectInputEquals ?? ((a, b) => a === b);
const isNewInput = getIsNewInput<IT, RT>(effectInputEquals);
const isNewInput = getIsNewInput<IT, RT, ER>(effectInputEquals);
const inIds = getInputSignalIds<IT>(config.nameExtension);
const outIds = getOutputSignalIds<IT, RT>(config.nameExtension);
const outIds = getOutputSignalIds<IT, RT, ER>(config.nameExtension);
const setup = (store: Store) => {

@@ -454,4 +462,4 @@ const invalidateTokenBehavior = getStateId<object | null>();

const resultEvent = getEventId<InternalResultType<IT, InternalEffectResult<RT>>>();
const resultBehavior = getDerivedId<InternalResultType<IT, InternalEffectResult<RT>>>();
const resultEvent = getEventId<InternalResultType<IT, InternalEffectResult<RT, ER>>>();
const resultBehavior = getDerivedId<InternalResultType<IT, InternalEffectResult<RT, ER>>>();
const initialResult = config.initialResultGetter ? config.initialResultGetter() : NO_VALUE;

@@ -475,4 +483,4 @@ store.addDerivedState(resultBehavior, store.getEventStream(resultEvent), {

store.addEventSource(
outIds.completedSuccesses,
store.getEventStream(outIds.successes).pipe(filter(isCompletedSuccess)),
outIds.completedResults,
store.getEventStream(outIds.results).pipe(filter(isCompletedResultEvent)),
);

@@ -486,3 +494,3 @@

IT,
InternalResultType<IT, InternalEffectResult<RT>>,
InternalResultType<IT, InternalEffectResult<RT, ER>>,
object | null,

@@ -508,7 +516,6 @@ IT | NoValueTriggeredInput,

store.add4TypedEventSource(
store.add3TypedEventSource(
resultEvent,
triggeredInputEvent,
outIds.errors,
outIds.successes,
outIds.results,
eventSourceInput.pipe(

@@ -519,3 +526,3 @@ filter(isNewInput),

IT,
InternalResultType<IT, InternalEffectResult<RT>>,
InternalResultType<IT, InternalEffectResult<RT, ER>>,
object | null,

@@ -531,9 +538,10 @@ IT | NoValueTriggeredInput,

)
: internalResultEffect(
input,
: internalResultEffect(input, {
store,
resultState.resultInput,
isNotNoValueType(resultState.result) ? resultState.result.result : NO_VALUE,
).pipe(
switchMap((result: InternalEffectResult<RT>) =>
previousInput: resultState.resultInput,
previousResult: isNotNoValueType(resultState.result)
? resultState.result.result
: NO_VALUE,
}).pipe(
switchMap((result: InternalEffectResult<RT, ER>) =>
of(

@@ -550,11 +558,11 @@ {

{
type: outIds.successes,
type: outIds.results,
event: {
// EffectSuccess<IT, RT>
// EffectResultEvent<IT, RT, ER>
result: result.result,
resultInput: input,
previousInput: resultState.resultInput,
previousResult: isNotNoValueType(resultState.result)
? resultState.result.result
: NO_VALUE,
// previousInput: resultState.resultInput,
// previousResult: isNotNoValueType(resultState.result)
// ? resultState.result.result
// : NO_VALUE,
completed: result.completed,

@@ -565,22 +573,2 @@ },

),
catchError(error =>
of(
{
type: outIds.errors,
event: {
error,
errorInput: input,
},
},
{
type: resultEvent,
event: {
result: NO_VALUE,
resultInput: input,
resultError: error,
resultToken: token,
},
},
),
),
),

@@ -595,3 +583,3 @@ ),

IT,
InternalResultType<IT, InternalEffectResult<RT>>,
InternalResultType<IT, InternalEffectResult<RT, ER>>,
object | null,

@@ -607,3 +595,3 @@ IT | NoValueTriggeredInput,

IT,
InternalResultType<IT, InternalEffectResult<RT>>,
InternalResultType<IT, InternalEffectResult<RT, ER>>,
object | null,

@@ -632,5 +620,3 @@ IT | NoValueTriggeredInput,

resultInput: resultState.resultInput,
resultError: resultState.resultError,
resultPending: false,
...(resultState.resultError ? { resultError: resultState.resultError } : {}),
},

@@ -648,12 +634,2 @@ ),

);
store.addDerivedState(
outIds.result,
store.getBehavior(outIds.combined).pipe(filter(isCombinedEffectResultInSuccessState)),
);
store.addDerivedState(
outIds.pending,
store.getBehavior(outIds.combined).pipe(map(c => c.resultPending)),
);
};

@@ -675,8 +651,9 @@ return {

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
*/
export type EffectSignalsFactory<Input, Result> = SignalsFactory<
export type EffectSignalsFactory<Input, Result, Error> = SignalsFactory<
EffectInputSignals<Input>,
EffectOutputSignals<Input, Result>,
EffectConfiguration<Input, Result>,
EffectFactoryEffects<Input, Result>
EffectOutputSignals<Input, Result, Error>,
EffectConfiguration<Input, Result, Error>,
EffectFactoryEffects<Input, Result, Error>
>;

@@ -689,10 +666,15 @@

* @template Result - specifies the result type of the effect
* @template Error - specifies the error type of the effect
* @returns {EffectSignalsFactory<Input, Result>}
*/
export const getEffectSignalsFactory = <Input, Result>(): EffectSignalsFactory<Input, Result> =>
export const getEffectSignalsFactory = <Input, Result, Error>(): EffectSignalsFactory<
Input,
Result,
Error
> =>
new SignalsFactory<
EffectInputSignals<Input>,
EffectOutputSignals<Input, Result>,
EffectConfiguration<Input, Result>,
EffectFactoryEffects<Input, Result>
EffectOutputSignals<Input, Result, Error>,
EffectConfiguration<Input, Result, Error>,
EffectFactoryEffects<Input, Result, Error>
>(getEffectBuilder);
import { combineLatest, distinctUntilChanged, map, startWith } from 'rxjs';
import { filter, switchMap, take } from 'rxjs/operators';
import { isNotEffectError } from './effect-result';
import {

@@ -7,2 +8,3 @@ CombinedEffectResult,

getEffectSignalsFactory,
isCombinedEffectResultInCompletedSuccessState,
} from './effect-signals-factory';

@@ -20,2 +22,3 @@ import {

NO_VALUE,
NoValueType,
getDerivedId,

@@ -66,3 +69,6 @@ getEffectId,

* @template SaveOutput - specifies the output-type for the save-effect (defaults to LoadInput)
* @template ValidationErrorType - specifies the error-type for failed validations
* @template ValidationFailedType - specifies the type representing a failed validations (total or for a distinct property, hence `ModelValidationResult<Entity, ValidationFailedType>` will be used as validation result)
* @template LoadError - specifies the error type of the load-effect
* @template ValidationError - specifies the error type of the validation-effect (use never, if your validation cannot error)
* @template SaveError - specifies the error type of the result-effect
*/

@@ -73,3 +79,6 @@ export type EntityEditModel<

SaveOutput = LoadInput,
ValidationErrorType = string,
ValidationFailedType = string,
LoadError = unknown,
ValidationError = unknown,
SaveError = unknown,
> = {

@@ -79,3 +88,3 @@ /**

*/
load: CombinedEffectResult<LoadInput | null, Entity>;
load: CombinedEffectResult<LoadInput | null, Entity, LoadError>;

@@ -87,4 +96,6 @@ /**

ModelWithDefault<Entity>,
ModelValidationResult<Entity, ValidationErrorType>,
SaveOutput
ModelValidationResult<Entity, ValidationFailedType>,
SaveOutput,
ValidationError,
SaveError
>;

@@ -100,5 +111,5 @@

* Current {@link ModelValidationResult} for entity.
* If entity === edit.validatedInput.model, this matches edit.validationResult, else it's null.
* If `entity === edit.validatedInput.model` and `isNotEffectError(edit.validationResult)` this matches edit.validationResult, else it's NO_VALUE.
*/
validation: ModelValidationResult<Entity, ValidationErrorType>;
validation: ModelValidationResult<Entity, ValidationFailedType> | NoValueType;

@@ -117,3 +128,3 @@ /**

* the validation effect is pending, or
* edit.isValid is false (the current validation result represents invalid entity state), or
* edit.isValid is false (the current validation result represents invalid entity state, or the validation errored), or
* the result-input equals the current-input

@@ -152,6 +163,9 @@ */

SaveOutput = LoadInput,
ValidationErrorType = string,
ValidationFailedType = string,
LoadError = unknown,
ValidationError = unknown,
SaveError = unknown,
> = {
/** {@link EffectOutputSignals} for the load effect */
load: EffectOutputSignals<LoadInput | null, Entity>;
load: EffectOutputSignals<LoadInput | null, Entity, LoadError>;

@@ -161,8 +175,20 @@ /** {@link ValidatedInputWithResultOutput} for the validation and result effects */

ModelWithDefault<Entity>,
ModelValidationResult<Entity, ValidationErrorType>,
SaveOutput
ModelValidationResult<Entity, ValidationFailedType>,
SaveOutput,
ValidationError,
SaveError
>;
/** derived bahavior for the {@link EntityEditModel} */
model: DerivedId<EntityEditModel<Entity, LoadInput, SaveOutput, ValidationErrorType>>;
model: DerivedId<
EntityEditModel<
Entity,
LoadInput,
SaveOutput,
ValidationFailedType,
LoadError,
ValidationError,
SaveError
>
>;
};

@@ -203,6 +229,9 @@

SaveOutput = LoadInput,
ValidationErrorType = string,
ValidationFailedType = string,
LoadError = unknown,
ValidationError = unknown,
SaveError = unknown,
> = {
/** effect that takes an entity-id or null and returns a corresponding entity (which sets the default model) */
load: EffectId<LoadInput | null, Entity>;
load: EffectId<LoadInput | null, Entity, LoadError>;

@@ -212,7 +241,8 @@ /** effect that takes a {@link ModelWithDefault} for the entity and returns the corresponding {@link ModelValidationResult} */

ModelWithDefault<Entity>,
ModelValidationResult<Entity, ValidationErrorType>
ModelValidationResult<Entity, ValidationFailedType>,
ValidationError
>;
/** effect that takes an entity and returns the id of the persisted entity */
save: EffectId<Entity, SaveOutput>;
save: EffectId<Entity, SaveOutput, SaveError>;
};

@@ -226,3 +256,6 @@

* @template SaveOutput - specifies the output-type for the save-effect (defaults to LoadInput)
* @template ValidationErrorType - specifies the error-type for failed validations
* @template ValidationFailedType - specifies the type representing a failed validations (total or for a distinct property, hence `ModelValidationResult<Entity, ValidationFailedType>` will be used as validation result)
* @template LoadError - specifies the error type of the load-effect
* @template ValidationError - specifies the error type of the validation-effect (use never, if your validation cannot error)
* @template SaveError - specifies the error type of the result-effect
*/

@@ -233,8 +266,27 @@ export type EntityEditFactory<

SaveOutput = LoadInput,
ValidationErrorType = string,
ValidationFailedType = string,
LoadError = unknown,
ValidationError = unknown,
SaveError = unknown,
> = SignalsFactory<
EntityEditInput<Entity, LoadInput>,
EntityEditOutput<Entity, LoadInput, SaveOutput, ValidationErrorType>,
EntityEditOutput<
Entity,
LoadInput,
SaveOutput,
ValidationFailedType,
LoadError,
ValidationError,
SaveError
>,
EntityEditConfiguration<Entity>,
EntityEditEffects<Entity, LoadInput, SaveOutput, ValidationErrorType>
EntityEditEffects<
Entity,
LoadInput,
SaveOutput,
ValidationFailedType,
LoadError,
ValidationError,
SaveError
>
>;

@@ -252,3 +304,6 @@

* @template SaveOutput - specifies the output-type for the save-effect (defaults to LoadInput)
* @template ValidationErrorType - specifies the error-type for failed validations
* @template ValidationFailedType - specifies the type representing a failed validations (total or for a distinct property, hence `ModelValidationResult<Entity, ValidationFailedType>` will be used as validation result)
* @template LoadError - specifies the error type of the load-effect
* @template ValidationError - specifies the error type of the validation-effect (use never, if your validation cannot error)
* @template SaveError - specifies the error type of the result-effect
*/

@@ -259,9 +314,24 @@ export const getEntityEditSignalsFactory = <

SaveOutput = LoadInput,
ValidationErrorType = string,
>(): EntityEditFactory<Entity, LoadInput, SaveOutput, ValidationErrorType> =>
getEffectSignalsFactory<LoadInput | null, Entity>() // model-fetch (fetching the edit entity)
ValidationFailedType = string,
LoadError = unknown,
ValidationError = unknown,
SaveError = unknown,
>(): EntityEditFactory<
Entity,
LoadInput,
SaveOutput,
ValidationFailedType,
LoadError,
ValidationError,
SaveError
> =>
getEffectSignalsFactory<LoadInput | null, Entity, LoadError>() // model-fetch (fetching the edit entity)
.renameInputId('input', 'load')
.compose(getModelSignalsFactory<Entity>()) // editing-model
.connectObservable(
(store, output) => store.getBehavior(output.result).pipe(map(result => result.result)),
({ store, output }) =>
store.getBehavior(output.combined).pipe(
filter(isCombinedEffectResultInCompletedSuccessState),
map(result => result.result),
),
'setAsDefault',

@@ -273,4 +343,6 @@ true,

ModelWithDefault<Entity>,
ModelValidationResult<Entity, ValidationErrorType>,
SaveOutput
ModelValidationResult<Entity, ValidationFailedType>,
SaveOutput,
ValidationError,
SaveError
>(),

@@ -280,3 +352,13 @@ ) // model validation and save

.addOutputId('combinedModel', () =>
getDerivedId<EntityEditModel<Entity, LoadInput, SaveOutput, ValidationErrorType>>(),
getDerivedId<
EntityEditModel<
Entity,
LoadInput,
SaveOutput,
ValidationFailedType,
LoadError,
ValidationError,
SaveError
>
>(),
)

@@ -300,14 +382,15 @@ .mapConfig((config: EntityEditConfiguration<Entity>) => ({

}))
.addEffectId('save', () => getEffectId<Entity, SaveOutput>())
.extendSetup((store, _, output, config, effects) => {
store.addEffect(effects.result, (modelWithResult, st, prevInput, prevResult) =>
st.getEffect(effects.save).pipe(
.addEffectId('save', () => getEffectId<Entity, SaveOutput, SaveError>())
.extendSetup(({ store, output, config, effects }) => {
store.addEffect(effects.result, (modelWithResult, args) =>
args.store.getEffect(effects.save).pipe(
take(1), // without this, the effect would never complete
switchMap(eff =>
eff(
modelWithResult.model,
st,
isNotNoValueType(prevInput) ? prevInput.model : NO_VALUE,
prevResult,
),
eff(modelWithResult.model, {
store: args.store,
previousInput: isNotNoValueType(args.previousInput)
? args.previousInput.model
: NO_VALUE,
previousResult: args.previousResult,
}),
),

@@ -332,11 +415,13 @@ ),

([load, edit]): [
CombinedEffectResult<LoadInput | null, Entity>,
CombinedEffectResult<LoadInput | null, Entity, LoadError>,
ValidatedInputWithResult<
ModelWithDefault<Entity>,
ModelValidationResult<Entity, ValidationErrorType>,
SaveOutput
ModelValidationResult<Entity, ValidationFailedType>,
SaveOutput,
ValidationError,
SaveError
>,
boolean,
boolean,
ModelValidationResult<Entity, ValidationErrorType>,
ModelValidationResult<Entity, ValidationFailedType> | NoValueType,
] => [

@@ -355,5 +440,7 @@ load,

: shallowEquals(edit.resultInput.model, edit.currentInput.model))),
edit.currentInput === edit.validatedInput && isNotNoValueType(edit.validationResult)
edit.currentInput === edit.validatedInput &&
isNotNoValueType(edit.validationResult) &&
isNotEffectError(edit.validationResult)
? edit.validationResult
: null,
: NO_VALUE,
],

@@ -401,3 +488,6 @@ ),

store.connectObservable(
store.getEventStream(output.resultCompletedSuccesses).pipe(map(() => undefined)),
store.getEventStream(output.conflicts2.completedResults).pipe(
filter(e => isNotEffectError(e.result)),
map(() => undefined),
),
config.onSaveCompletedEvent,

@@ -421,20 +511,20 @@ );

.mapOutput(
(ids): EntityEditOutput<Entity, LoadInput, SaveOutput, ValidationErrorType> => ({
load: {
combined: ids.conflicts1.combined,
result: ids.conflicts1.result,
pending: ids.pending,
successes: ids.successes,
completedSuccesses: ids.completedSuccesses,
errors: ids.errors,
},
(
ids,
): EntityEditOutput<
Entity,
LoadInput,
SaveOutput,
ValidationFailedType,
LoadError,
ValidationError,
SaveError
> => ({
load: ids.conflicts1,
edit: {
combined: ids.conflicts2.combined,
result: ids.conflicts2.result,
validationSuccesses: ids.validationSuccesses,
validationCompletedSuccesses: ids.validationCompletedSuccesses,
validationErrors: ids.validationErrors,
resultSuccesses: ids.resultSuccesses,
resultCompletedSuccesses: ids.resultCompletedSuccesses,
resultErrors: ids.resultErrors,
validationResults: ids.validationResults,
validationCompletedResults: ids.validationCompletedResults,
results: ids.conflicts2.results,
completedResults: ids.conflicts2.completedResults,
},

@@ -445,3 +535,13 @@ model: ids.combinedModel,

.mapEffects(
(ids): EntityEditEffects<Entity, LoadInput, SaveOutput, ValidationErrorType> => ({
(
ids,
): EntityEditEffects<
Entity,
LoadInput,
SaveOutput,
ValidationFailedType,
LoadError,
ValidationError,
SaveError
> => ({
load: ids.id,

@@ -448,0 +548,0 @@ validation: ids.validation,

@@ -0,1 +1,2 @@

export * from './effect-result';
export * from './effect-signals-factory';

@@ -10,2 +11,1 @@ export * from './entity-edit-signals-factory';

export * from './validated-input-with-result-signals-factory';
export * from './void-effect-signals-factory';

@@ -180,2 +180,15 @@ /* eslint-disable @typescript-eslint/no-use-before-define */

export type SignalsFactoryArgs<
IN extends NameToSignalId,
OUT extends NameToSignalId,
CONFIG extends Configuration,
EFF extends NameToEffectId,
> = {
store: Store;
input: IN;
output: OUT;
config: CONFIG;
effects: EFF;
};
/**

@@ -194,3 +207,3 @@ * This type specifies the argument to the {@link SignalsFactory.extendSetup} method.

EFF extends NameToEffectId,
> = (store: Store, input: IN, output: OUT, config: CONFIG, effects: EFF) => void;
> = (args: SignalsFactoryArgs<IN, OUT, CONFIG, EFF>) => void;

@@ -463,3 +476,3 @@ /**

s.setup(store);
extend(store, s.input, s.output, config, s.effects);
extend({ store, input: s.input, output: s.output, config, effects: s.effects });
},

@@ -499,7 +512,9 @@ };

: SignalsFactory<Omit<IN, KIN>, OUT, CONFIG, EFF> {
const fnew: SignalsFactory<IN, OUT, CONFIG, EFF> = this.extendSetup((store, input, output) => {
const fromId: SignalId<any> = output[outputName] as SignalId<any>;
const toId: SignalId<any> = input[inputName] as SignalId<any>;
store.connect(fromId, toId);
});
const fnew: SignalsFactory<IN, OUT, CONFIG, EFF> = this.extendSetup(
({ store, input, output }) => {
const fromId: SignalId<any> = output[outputName] as SignalId<any>;
const toId: SignalId<any> = input[inputName] as SignalId<any>;
store.connect(fromId, toId);
},
);
const result = (keepInputId ? fnew : fnew.removeInputId(inputName)) as B extends true

@@ -528,3 +543,3 @@ ? SignalsFactory<IN, OUT, CONFIG, EFF>

: SignalsFactory<Omit<IN, K>, OUT, CONFIG, EFF> {
const fnew: SignalsFactory<IN, OUT, CONFIG, EFF> = this.extendSetup((store, input) => {
const fnew: SignalsFactory<IN, OUT, CONFIG, EFF> = this.extendSetup(({ store, input }) => {
const toId: SignalId<any> = input[inputName] as SignalId<any>;

@@ -555,3 +570,3 @@ store.connect(fromId, toId);

>(
sourceGetter: (store: Store, output: OUT, config: CONFIG) => O,
sourceGetter: (args: SignalsFactoryArgs<IN, OUT, CONFIG, EFF>) => O,
inputName: K,

@@ -562,5 +577,5 @@ keepInputId: B,

: SignalsFactory<Omit<IN, K>, OUT, CONFIG, EFF> {
const fnew: SignalsFactory<IN, OUT, CONFIG, EFF> = this.extendSetup((st, ip, op, conf) => {
const toId: SignalId<any> = ip[inputName] as SignalId<any>;
st.connectObservable(sourceGetter(st, op, conf), toId);
const fnew: SignalsFactory<IN, OUT, CONFIG, EFF> = this.extendSetup(args => {
const toId: SignalId<any> = args.input[inputName] as SignalId<any>;
args.store.connectObservable(sourceGetter(args), toId);
});

@@ -763,6 +778,3 @@ const result = (keepInputId ? fnew : fnew.removeInputId(inputName)) as B extends true

old: Observable<ToBehaviorIdValueType<OUT[KOUT]>>,
store: Store,
input: IN,
output: OUT,
config: CONFIG,
args: SignalsFactoryArgs<IN, OUT, CONFIG, EFF>,
) => Observable<TNEW>,

@@ -779,3 +791,9 @@ ): SignalsFactory<IN, AddOrReplaceId<OUT, KOUT, DerivedId<TNEW>>, CONFIG, EFF> {

newId,
mapper(store.getBehavior(oldId), store, s.input, s.output, config),
mapper(store.getBehavior(oldId), {
store,
input: s.input,
output: s.output,
config,
effects: s.effects,
}),
);

@@ -869,3 +887,3 @@ s.setup(store);

: SignalsFactory<IN, OUT, CONFIG, Omit<EFF, K>> {
const result = this.extendSetup((store, _, _2, config, effects) => {
const result = this.extendSetup(({ store, config, effects }) => {
store

@@ -872,0 +890,0 @@ .getEffect(idGetter(config))

@@ -126,3 +126,3 @@ import { Observable } from 'rxjs';

* The rx-signals `Store` uses this type to uniquely identify all of its result effects.
* An `EffectId<InputType, ResultType>` does not make any use of the generic parameters itself,
* An `EffectId<InputType, ResultType, ErrorType>` does not make any use of the generic parameters itself,
* but is given these parameters only as a trick to let Typescript infer and thus enforce the correct types.

@@ -133,6 +133,8 @@ * Use the {@link getEffectId} function to generate a corresponding ID.

* @template ResultType - specifies the type for the corresponding effects result
* @template ErrorType - specifies the type error-type for the effect. Use never for effects that cannot error.
*/
export type EffectId<InputType, ResultType> = symbol & {
export type EffectId<InputType, ResultType, ErrorType = unknown> = symbol & {
_inputType: InputType;
_resultType: ResultType;
_errorType: ErrorType;
};

@@ -180,9 +182,14 @@

* @template ResultType - specifies the type for the corresponding effects result
* @template ErrorType - specifies the type error-type for the effect. Use `never` for effects that cannot error.
* @param {string} nameExtension - an optional extension to the symbol name (so the string representation). Usually you should not need this.
* @returns {EventId<T>}
*/
export const getEffectId = <InputType, ResultType>(
export const getEffectId = <InputType, ResultType, ErrorType = unknown>(
nameExtension?: string,
): EffectId<InputType, ResultType> =>
Symbol(`Effect_${(nameExtension ?? '') + effectExtension++}`) as EffectId<InputType, ResultType>;
): EffectId<InputType, ResultType, ErrorType> =>
Symbol(`Effect_${(nameExtension ?? '') + effectExtension++}`) as EffectId<
InputType,
ResultType,
ErrorType
>;

@@ -189,0 +196,0 @@ /**

@@ -20,2 +20,3 @@ import {

import { DelayedEventQueue } from './delayed-event-queue';
import { EffectResult } from './effect-result';
import { SourceObservable } from './source-observable';

@@ -73,3 +74,14 @@ import {

/** universal type used for unhandled errors from effects */
export type UnhandledEffectError = {
unhandledError: unknown;
};
/**
* The usual `EffectResult` extended by `EffectError<UnhandledEffectError>`,
* to provide a type that also for effect consumers that handle unhandled effect errors
*/
export type SafeEffectResult<Result, Error> = EffectResult<Result, Error | UnhandledEffectError>;
/**
* The `Effect<Input>` type specifies a potentially impure function that takes an input and a {@link Store} as arguments

@@ -80,23 +92,28 @@ * and returns an effectful result as `Observable<Result>`.

* the corresponding `Effect`.
* The store argument can be used to access additional input from the store (thus, the `Effect` itself could also
* be pure and just use something impure that was put into the store, e.g. another `Effect`).
* The previousInput argument can be used e.g. to decide whether the effect must perform
* The args can be used to access additional input from the store (thus, the `Effect` itself could also
* be pure and just use something impure that was put into the store, e.g. another `Effect`),
* as well as the previousInput and previousResult.
* The previousInput can be used e.g. to decide whether the effect must perform
* a computation/query/etc., or if maybe the previousResult can be returned directly.
* If the effect cannot error, use `never` as `Error` type. In that case, `EffectResult<Result, Error>` will equal `Result`.
*
* @template Input - specifies the input type for the effect
* @template Result - specifies the result type for the effect
* @template Error - specifies the error type for the effect. Specify never, if the effect cannot error.
*/
export type Effect<Input, Result> = (
export type Effect<Input, Result, Error = unknown> = (
/** the effect input */
input: Input,
/** the Store instance that will be passed to the function (e.g. to inject some other Effect) */
store: Store,
args: {
/** the Store instance that will be passed to the function (e.g. to inject some other Effect) */
store: Store;
/** the input of the previous function invocation, or NO_VALUE */
previousInput: Input | NoValueType,
/** the input of the previous function invocation, or NO_VALUE */
previousInput: Input | NoValueType;
/** the result of the previous function invocation, or NO_VALUE */
previousResult: Result | NoValueType,
) => Observable<Result>;
/** `SafeEffectResult` instead of `EffectResult`, to give consumers the option to provide unhandled errors */
previousResult: SafeEffectResult<Result, Error> | NoValueType;
},
) => Observable<EffectResult<Result, Error>>;

@@ -109,3 +126,5 @@ /**

*/
export type ToEffectType<ID> = ID extends EffectId<infer I, infer O> ? Effect<I, O> : never;
export type ToEffectType<ID> = ID extends EffectId<infer I, infer O, infer E>
? Effect<I, O, E>
: never;

@@ -943,3 +962,3 @@ /**

*/
addEffect<ID extends EffectId<any, any>>(id: ID, effect: ToEffectType<ID>): void {
addEffect<ID extends EffectId<any, any, any>>(id: ID, effect: ToEffectType<ID>): void {
this.addState(id as unknown as StateId<ToEffectType<ID>>, () => effect);

@@ -957,10 +976,10 @@ }

*
* @param {EffectId<InputType, ResultType>} id - the unique identifier for the effect
* @returns {Observable<Effect<InputType, ResultType>>} - the effect observable
* @param {EffectId<InputType, ResultType, ErrorType>} id - the unique identifier for the effect
* @returns {Observable<Effect<InputType, ResultType, ErrorType>>} - the effect observable
*/
getEffect<InputType, ResultType>(
id: EffectId<InputType, ResultType>,
): Observable<Effect<InputType, ResultType>> {
return this.getBehavior<Effect<InputType, ResultType>>(
id as unknown as BehaviorId<Effect<InputType, ResultType>>,
getEffect<InputType, ResultType, ErrorType>(
id: EffectId<InputType, ResultType, ErrorType>,
): Observable<Effect<InputType, ResultType, ErrorType>> {
return this.getBehavior<Effect<InputType, ResultType, ErrorType>>(
id as unknown as BehaviorId<Effect<InputType, ResultType, ErrorType>>,
);

@@ -967,0 +986,0 @@ }

import { Observable, combineLatest, distinctUntilChanged, filter, map, startWith } from 'rxjs';
import {
CombinedEffectResult,
CombinedEffectResultInSuccessState,
EffectCompletedSuccess,
EffectError,
EffectCompletedResultEvent,
EffectOutputSignals,
EffectSuccess,
EffectResultEvent,
getEffectSignalsFactory,
} from './effect-signals-factory';
import { SignalsFactory } from './signals-factory';
import { Store } from './store';
import { SafeEffectResult, Store } from './store';
import {

@@ -21,2 +19,4 @@ BehaviorId,

getDerivedId,
isNoValueType,
isNotNoValueType,
} from './store-utils';

@@ -31,4 +31,12 @@ import { Merged } from './type-utils';

* @template Result - specifies the result-type of the result-effect
* @template ValidationError - specifies the error type of the validation-effect
* @template ResultError - specifies the error type of the result-effect
*/
export type ValidatedInputWithResult<Input, ValidationResult, Result> = {
export type ValidatedInputWithResult<
Input,
ValidationResult,
Result,
ValidationError,
ResultError,
> = {
/** current input (which might differ from the resultInput) */

@@ -44,3 +52,3 @@ currentInput: Input | NoValueType;

/** the current validationResult (or NO_VALUE, if no validation-result was received yet) */
validationResult: ValidationResult | NoValueType;
validationResult: SafeEffectResult<ValidationResult, ValidationError> | NoValueType;

@@ -56,7 +64,4 @@ /** only true if validationResult represents a valid state AND validationPending is false */

/* In case the resultInput led to an error (result === NO_VALUE in that case) */
resultError?: any;
/** the current result (or NO_VALUE, if no result was received yet) */
result: Result | NoValueType;
result: SafeEffectResult<Result, ResultError> | NoValueType;
};

@@ -77,11 +82,18 @@

*/
export type ValidatedInputWithResultOutput<Input, ValidationResult, Result> = {
combined: DerivedId<ValidatedInputWithResult<Input, ValidationResult, Result>>;
result: DerivedId<CombinedEffectResultInSuccessState<Input, Result>>;
validationErrors: EventId<EffectError<Input>>;
validationSuccesses: EventId<EffectSuccess<Input, ValidationResult>>;
validationCompletedSuccesses: EventId<EffectCompletedSuccess<Input, ValidationResult>>;
resultErrors: EventId<EffectError<Input>>;
resultSuccesses: EventId<EffectSuccess<Input, Result>>;
resultCompletedSuccesses: EventId<EffectCompletedSuccess<Input, Result>>;
export type ValidatedInputWithResultOutput<
Input,
ValidationResult,
Result,
ValidationError,
ResultError,
> = {
combined: DerivedId<
ValidatedInputWithResult<Input, ValidationResult, Result, ValidationError, ResultError>
>;
validationResults: EventId<EffectResultEvent<Input, ValidationResult, ValidationError>>;
validationCompletedResults: EventId<
EffectCompletedResultEvent<Input, ValidationResult, ValidationError>
>;
results: EventId<EffectResultEvent<Input, Result, ResultError>>;
completedResults: EventId<EffectCompletedResultEvent<Input, Result, ResultError>>;
};

@@ -92,4 +104,7 @@

*/
export type ValidatedInputWithResultConfig<Input, ValidationResult, Result> = {
isValidationResultValid?: (validationResult: ValidationResult) => boolean;
export type ValidatedInputWithResultConfig<Input, ValidationResult, Result, ValidationError> = {
/** whether the a validation result represents a valid state, defaults to `isNotEffectError(validationResult) && (validationResult ?? null) === null` */
isValidationResultValid?: (
validationResult: SafeEffectResult<ValidationResult, ValidationError>,
) => boolean;
validationEffectDebounceTime?: number;

@@ -107,5 +122,11 @@ resultEffectDebounceTime?: number;

*/
export type ValidatedInputWithResultEffects<Input, ValidationResult, Result> = {
validation: EffectId<Input, ValidationResult>;
result: EffectId<Input, Result>;
export type ValidatedInputWithResultEffects<
Input,
ValidationResult,
Result,
ValidationError,
ResultError,
> = {
validation: EffectId<Input, ValidationResult, ValidationError>;
result: EffectId<Input, Result, ResultError>;
};

@@ -118,28 +139,36 @@

*/
export type ValidatedInputWithResultFactory<Input, ValidationResult, Result> = SignalsFactory<
export type ValidatedInputWithResultFactory<
Input,
ValidationResult,
Result,
ValidationError,
ResultError,
> = SignalsFactory<
ValidatedInputWithResultInput<Input>,
ValidatedInputWithResultOutput<Input, ValidationResult, Result>,
ValidatedInputWithResultConfig<Input, ValidationResult, Result>,
ValidatedInputWithResultEffects<Input, ValidationResult, Result>
ValidatedInputWithResultOutput<Input, ValidationResult, Result, ValidationError, ResultError>,
ValidatedInputWithResultConfig<Input, ValidationResult, Result, ValidationError>,
ValidatedInputWithResultEffects<Input, ValidationResult, Result, ValidationError, ResultError>
>;
type ResultInputGetterInput<Input, ValidationResult> = {
type ResultInputGetterInput<Input, ValidationResult, ValidationError> = {
currentInput: Input;
resultInput: Input;
result: ValidationResult;
result: SafeEffectResult<ValidationResult, ValidationError>;
resultPending: boolean;
};
const isResultInputGetterInput = <Input, ValidationResult>(
c: CombinedEffectResult<Input, ValidationResult>, // from the validation effect
): c is ResultInputGetterInput<Input, ValidationResult> =>
const isResultInputGetterInput = <Input, ValidationResult, ValidationError>(
c: CombinedEffectResult<Input, ValidationResult, ValidationError>,
): c is ResultInputGetterInput<Input, ValidationResult, ValidationError> =>
!c.resultPending &&
c.resultInput !== NO_VALUE &&
c.result !== NO_VALUE &&
isNotNoValueType(c.resultInput) &&
isNotNoValueType(c.result) &&
c.currentInput === c.resultInput;
const resultInputGetter = <Input, ValidationResult>(
const resultInputGetter = <Input, ValidationResult, ValidationError>(
store: Store,
validationBehaviorId: BehaviorId<CombinedEffectResult<Input, ValidationResult>>,
isValidationResultValid: (validationResult: ValidationResult) => boolean,
validationBehaviorId: BehaviorId<CombinedEffectResult<Input, ValidationResult, ValidationError>>,
isValidationResultValid: (
validationResult: SafeEffectResult<ValidationResult, ValidationError>,
) => boolean,
): Observable<Input> =>

@@ -153,5 +182,10 @@ store.getBehavior(validationBehaviorId).pipe(

const mapBehaviors = <Input, ValidationResult, Result>(
[v, r]: [CombinedEffectResult<Input, ValidationResult>, CombinedEffectResult<Input, Result>],
isValidationResultValid: (validationResult: ValidationResult) => boolean,
const mapBehaviors = <Input, ValidationResult, Result, ValidationError, ResultError>(
[v, r]: [
CombinedEffectResult<Input, ValidationResult, ValidationError>,
CombinedEffectResult<Input, Result, ResultError>,
],
isValidationResultValid: (
validationResult: SafeEffectResult<ValidationResult, ValidationError>,
) => boolean,
) => ({

@@ -162,14 +196,21 @@ currentInput: v.currentInput,

validationResult: v.result,
isValid: !v.resultPending && v.result !== NO_VALUE ? isValidationResultValid(v.result) : false,
isValid:
!v.resultPending && isNotNoValueType(v.result) ? isValidationResultValid(v.result) : false,
resultPending: r.resultPending,
resultInput: r.resultInput,
...(r.resultError ? { resultError: r.resultError } : {}),
result: r.result,
});
const setupCombinedBehavior = <Input, ValidationResult, Result>(
const setupCombinedBehavior = <Input, ValidationResult, Result, ValidationError, ResultError>(
store: Store,
outIds: Merged<EffectOutputSignals<Input, ValidationResult>, EffectOutputSignals<Input, Result>>,
id: DerivedId<ValidatedInputWithResult<Input, ValidationResult, Result>>,
isValidationResultValid: (validationResult: ValidationResult) => boolean,
outIds: Merged<
EffectOutputSignals<Input, ValidationResult, ValidationError>,
EffectOutputSignals<Input, Result, ResultError>
>,
id: DerivedId<
ValidatedInputWithResult<Input, ValidationResult, Result, ValidationError, ResultError>
>,
isValidationResultValid: (
validationResult: SafeEffectResult<ValidationResult, ValidationError>,
) => boolean,
initialResultGetter?: () => Result,

@@ -194,3 +235,3 @@ ) => {

r.currentInput === v.resultInput ||
v.result === NO_VALUE ||
isNoValueType(v.result) ||
!isValidationResultValid(v.result),

@@ -206,3 +247,2 @@ ),

a.resultPending === b.resultPending &&
a.resultError === b.resultError &&
a.validatedInput === b.validatedInput &&

@@ -223,37 +263,49 @@ a.validationPending === b.validationPending &&

Result,
>(): ValidatedInputWithResultFactory<Input, ValidationResult, Result> =>
getEffectSignalsFactory<Input, ValidationResult>()
ValidationError,
ResultError,
>(): ValidatedInputWithResultFactory<
Input,
ValidationResult,
Result,
ValidationError,
ResultError
> =>
getEffectSignalsFactory<Input, ValidationResult, ValidationError>()
.renameEffectId('id', 'validation')
.compose(getEffectSignalsFactory<Input, Result>())
.compose(getEffectSignalsFactory<Input, Result, ResultError>())
.renameEffectId('id', 'result')
.mapConfig((config: ValidatedInputWithResultConfig<Input, ValidationResult, Result>) => ({
c1: {
effectDebounceTime: config.validationEffectDebounceTime,
eagerInputSubscription: config.eagerInputSubscription,
nameExtension: `${config.nameExtension ?? ''}_validation`,
},
c2: {
initialResultGetter: config.initialResultGetter,
withTrigger: config.withResultTrigger,
effectInputEquals: config.resultEffectInputEquals,
effectDebounceTime: config.resultEffectDebounceTime,
nameExtension: `${config.nameExtension ?? ''}_result`,
},
}))
.extendSetup((store, inIds, outIds, config) => {
.mapConfig(
(
config: ValidatedInputWithResultConfig<Input, ValidationResult, Result, ValidationError>,
) => ({
c1: {
effectDebounceTime: config.validationEffectDebounceTime,
eagerInputSubscription: config.eagerInputSubscription,
nameExtension: `${config.nameExtension ?? ''}_validation`,
},
c2: {
initialResultGetter: config.initialResultGetter,
withTrigger: config.withResultTrigger,
effectInputEquals: config.resultEffectInputEquals,
effectDebounceTime: config.resultEffectDebounceTime,
nameExtension: `${config.nameExtension ?? ''}_result`,
},
}),
)
.extendSetup(({ store, input, output, config }) => {
store.connectObservable(
resultInputGetter(
store,
outIds.conflicts1.combined,
output.conflicts1.combined,
config.isValidationResultValid ?? (validationResult => validationResult === null),
),
inIds.conflicts2.input,
input.conflicts2.input,
);
})
.addOutputId('combined', config =>
getDerivedId<ValidatedInputWithResult<Input, ValidationResult, Result>>(
`${config.nameExtension ?? ''}_combined`,
),
getDerivedId<
ValidatedInputWithResult<Input, ValidationResult, Result, ValidationError, ResultError>
>(`${config.nameExtension ?? ''}_combined`),
)
.extendSetup((store, _, output, config) => {
.extendSetup(({ store, output, config }) => {
setupCombinedBehavior(

@@ -263,3 +315,3 @@ store,

output.combined,
config.isValidationResultValid ?? (validationResult => validationResult === null),
config.isValidationResultValid ?? (validationResult => (validationResult ?? null) === null),
config.initialResultGetter,

@@ -276,9 +328,6 @@ );

combined: output.combined,
result: output.conflicts2.result,
validationErrors: output.conflicts1.errors,
validationSuccesses: output.conflicts1.successes,
validationCompletedSuccesses: output.conflicts1.completedSuccesses,
resultErrors: output.conflicts2.errors,
resultSuccesses: output.conflicts2.successes,
resultCompletedSuccesses: output.conflicts2.completedSuccesses,
validationResults: output.conflicts1.results,
validationCompletedResults: output.conflicts1.completedResults,
results: output.conflicts2.results,
completedResults: output.conflicts2.completedResults,
}));

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc