New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

@stackflow/core

Package Overview
Dependencies
Maintainers
3
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stackflow/core - npm Package Compare versions

Comparing version
1.1.1
to
1.2.0
+7
dist/activity-utils/findTargetActivityIndices.d.ts
import type { Activity } from "../Stack";
import type { DomainEvent } from "../event-types";
export declare function findTargetActivityIndices(activities: Activity[], event: DomainEvent, context: {
now: number;
transitionDuration: number;
}): number[];
//# sourceMappingURL=findTargetActivityIndices.d.ts.map
{"version":3,"file":"findTargetActivityIndices.d.ts","sourceRoot":"","sources":["../../src/activity-utils/findTargetActivityIndices.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAA2B,MAAM,UAAU,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAiBlD,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,QAAQ,EAAE,EACtB,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE;IACP,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB,EAAE,MAAM,CAAC;CAC5B,GACA,MAAM,EAAE,CAsFV"}
import type { Activity } from "../Stack";
import type { DomainEvent } from "../event-types";
/**
* Create activity list reducers for each event type (Activity[] + Event => Activity[])
*/
export declare function makeActivitiesReducer({ transitionDuration, now, resumedAt, }: {
transitionDuration: number;
now: number;
resumedAt?: number;
}): (target: Activity[], event: DomainEvent) => Activity[];
//# sourceMappingURL=makeActivitiesReducer.d.ts.map
{"version":3,"file":"makeActivitiesReducer.d.ts","sourceRoot":"","sources":["../../src/activity-utils/makeActivitiesReducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAA2B,MAAM,UAAU,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAA8B,MAAM,gBAAgB,CAAC;AAS9E;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,kBAAkB,EAClB,GAAG,EACH,SAAS,GACV,EAAE;IACD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,0DA0DA"}
import type { Activity, ActivityTransitionState } from "../Stack";
import type { PushedEvent, ReplacedEvent } from "../event-types";
export declare function makeActivityFromEvent(event: PushedEvent | ReplacedEvent, transitionState: ActivityTransitionState): Activity;
//# sourceMappingURL=makeActivityFromEvent.d.ts.map
{"version":3,"file":"makeActivityFromEvent.d.ts","sourceRoot":"","sources":["../../src/activity-utils/makeActivityFromEvent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEjE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,WAAW,GAAG,aAAa,EAClC,eAAe,EAAE,uBAAuB,GACvC,QAAQ,CAqBV"}
import type { Activity } from "../Stack";
import type { DomainEvent } from "../event-types";
/**
* Create activity reducers for each event type (Activity + Event => Activity)
*/
export declare function makeActivityReducer(context: {
transitionDuration: number;
now: number;
resumedAt?: number;
}): (target: Activity, event: DomainEvent) => Activity;
//# sourceMappingURL=makeActivityReducer.d.ts.map
{"version":3,"file":"makeActivityReducer.d.ts","sourceRoot":"","sources":["../../src/activity-utils/makeActivityReducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAA2B,MAAM,UAAU,CAAC;AAClE,OAAO,KAAK,EACV,WAAW,EAMZ,MAAM,gBAAgB,CAAC;AAQxB;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,kBAAkB,EAAE,MAAM,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,sDA0GA"}
import type { DomainEvent } from "../event-types";
export declare function makeReducer<T>(reducerMap: {
[key in DomainEvent["name"]]: (state: T, event: Extract<DomainEvent, {
name: key;
}>) => T;
}): (target: T, event: DomainEvent) => T;
//# sourceMappingURL=makeReducer.d.ts.map
{"version":3,"file":"makeReducer.d.ts","sourceRoot":"","sources":["../../src/activity-utils/makeReducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAIlD,wBAAgB,WAAW,CAAC,CAAC,EAC3B,UAAU,EAAE;KACT,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,GAAG,CAC5B,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC,KACvC,CAAC;CACP,YAEe,CAAC,SAAS,WAAW,OAOtC"}
import type { Stack } from "../Stack";
import type { DomainEvent } from "../event-types";
export declare function makeStackReducer(context: {
now: number;
resumedAt?: number;
}): (target: Stack, event: DomainEvent) => Stack;
//# sourceMappingURL=makeStackReducer.d.ts.map
{"version":3,"file":"makeStackReducer.d.ts","sourceRoot":"","sources":["../../src/activity-utils/makeStackReducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,KAAK,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAEV,WAAW,EAIZ,MAAM,gBAAgB,CAAC;AA+ExB,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,gDAiEA"}
import type { BaseDomainEvent } from "./_base";
export type PausedEvent = BaseDomainEvent<"Paused", {}>;
//# sourceMappingURL=PausedEvent.d.ts.map
{"version":3,"file":"PausedEvent.d.ts","sourceRoot":"","sources":["../../src/event-types/PausedEvent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC"}
import type { BaseDomainEvent } from "./_base";
export type ResumedEvent = BaseDomainEvent<"Resumed", {}>;
//# sourceMappingURL=ResumedEvent.d.ts.map
{"version":3,"file":"ResumedEvent.d.ts","sourceRoot":"","sources":["../../src/event-types/ResumedEvent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC"}
import type { StackflowActions } from "../interfaces";
import type { StackflowPlugin } from "../interfaces";
type ActionCreatorOptions = {
dispatchEvent: StackflowActions["dispatchEvent"];
pluginInstances: ReturnType<StackflowPlugin>[];
actions: StackflowActions;
};
export declare function makeActions({ dispatchEvent, pluginInstances, actions, }: ActionCreatorOptions): Omit<StackflowActions, "dispatchEvent" | "getStack">;
export {};
//# sourceMappingURL=makeActions.d.ts.map
{"version":3,"file":"makeActions.d.ts","sourceRoot":"","sources":["../../src/utils/makeActions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrD,KAAK,oBAAoB,GAAG;IAC1B,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAC;IACjD,eAAe,EAAE,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;IAC/C,OAAO,EAAE,gBAAgB,CAAC;CAC3B,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,aAAa,EACb,eAAe,EACf,OAAO,GACR,EAAE,oBAAoB,GAAG,IAAI,CAAC,gBAAgB,EAAE,eAAe,GAAG,UAAU,CAAC,CAmH7E"}
import type { Effect } from "../Effect";
import type { StackflowActions, StackflowPlugin } from "../interfaces";
export declare function triggerPostEffectHooks(effects: Effect[], plugins: ReturnType<StackflowPlugin>[], actions: StackflowActions): void;
//# sourceMappingURL=triggerPostEffectHooks.d.ts.map
{"version":3,"file":"triggerPostEffectHooks.d.ts","sourceRoot":"","sources":["../../src/utils/triggerPostEffectHooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEvE,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,UAAU,CAAC,eAAe,CAAC,EAAE,EACtC,OAAO,EAAE,gBAAgB,GACxB,IAAI,CAkCN"}
import type { DomainEvent } from "../event-types";
import type { BaseDomainEvent } from "../event-types/_base";
import type { StackflowPlugin } from "../interfaces";
import type { StackflowActions } from "../interfaces";
type PreEffectHookResult<T> = {
isPrevented: boolean;
nextActionParams: T;
};
type EventNameToParams<K extends ActionName> = Omit<Extract<DomainEvent, {
name: K;
}>, keyof BaseDomainEvent>;
type ActionName = Exclude<DomainEvent["name"], "Initialized" | "ActivityRegistered">;
export declare function triggerPreEffectHook<K extends ActionName>(actionName: K, actionParams: EventNameToParams<K>, pluginInstances: ReturnType<StackflowPlugin>[], actions: StackflowActions): PreEffectHookResult<EventNameToParams<K>>;
export {};
//# sourceMappingURL=triggerPreEffectHooks.d.ts.map
{"version":3,"file":"triggerPreEffectHooks.d.ts","sourceRoot":"","sources":["../../src/utils/triggerPreEffectHooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,KAAK,mBAAmB,CAAC,CAAC,IAAI;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,CAAC,CAAC;CACrB,CAAC;AAEF,KAAK,iBAAiB,CAAC,CAAC,SAAS,UAAU,IAAI,IAAI,CACjD,OAAO,CAAC,WAAW,EAAE;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,EACjC,MAAM,eAAe,CACtB,CAAC;AAaF,KAAK,UAAU,GAAG,OAAO,CACvB,WAAW,CAAC,MAAM,CAAC,EACnB,aAAa,GAAG,oBAAoB,CACrC,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,UAAU,EACvD,UAAU,EAAE,CAAC,EACb,YAAY,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAClC,eAAe,EAAE,UAAU,CAAC,eAAe,CAAC,EAAE,EAC9C,OAAO,EAAE,gBAAgB,GACxB,mBAAmB,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CA+B3C"}
import type { Activity, ActivityTransitionState } from "../Stack";
import type { DomainEvent } from "../event-types";
import { findIndices, last } from "../utils";
function isActivityNotExited(activity: Activity) {
return !activity.exitedBy;
}
function compareActivitiesByEventDate(a1: Activity, a2: Activity) {
return a2.enteredBy.eventDate - a1.enteredBy.eventDate;
}
function findLatestActiveActivity(activities: Activity[]) {
return activities
.filter(isActivityNotExited)
.sort(compareActivitiesByEventDate)[0];
}
export function findTargetActivityIndices(
activities: Activity[],
event: DomainEvent,
context: {
now: number;
transitionDuration: number;
},
): number[] {
const targetActivities: number[] = [];
switch (event.name) {
case "Replaced": {
const alreadyExistingActivityIndex = last(
findIndices(activities, (activity) => activity.id === event.activityId),
);
if (alreadyExistingActivityIndex !== undefined) {
break;
}
const sorted = activities
.slice()
.sort(compareActivitiesByEventDate)
.filter(isActivityNotExited);
const isTransitionDone =
context.now - event.eventDate >= context.transitionDuration;
const transitionState: ActivityTransitionState =
event.skipEnterActiveState || isTransitionDone
? "enter-done"
: "enter-active";
if (transitionState === "enter-done") {
const range = sorted.findIndex(
(activity) =>
!(
event.skipEnterActiveState &&
activity.enteredBy.name === "Replaced" &&
activity.transitionState === "enter-active"
),
);
return sorted.slice(0, range + 1).map((a) => activities.indexOf(a));
}
break;
}
case "Popped": {
const sorted = activities
.filter(isActivityNotExited)
.sort(compareActivitiesByEventDate);
const latestActivity = sorted.slice(0, sorted.length - 1)[0];
if (latestActivity) {
targetActivities.push(activities.indexOf(latestActivity));
}
break;
}
case "StepPushed":
case "StepReplaced": {
const latestActivity = findLatestActiveActivity(activities);
if (latestActivity) {
if (
event.targetActivityId &&
event.targetActivityId !== latestActivity.id
) {
break;
}
targetActivities.push(activities.indexOf(latestActivity));
}
break;
}
case "StepPopped": {
const latestActivity = findLatestActiveActivity(activities);
if (latestActivity && latestActivity.steps.length > 1) {
if (
event.targetActivityId &&
event.targetActivityId !== latestActivity.id
) {
break;
}
targetActivities.push(activities.indexOf(latestActivity));
}
break;
}
default:
break;
}
return targetActivities;
}
import type { Activity, ActivityTransitionState } from "../Stack";
import type { DomainEvent, PushedEvent, ReplacedEvent } from "../event-types";
import { findNewActivityIndex } from "./findNewActivityIndex";
import { makeActivityFromEvent } from "./makeActivityFromEvent";
import { makeReducer } from "./makeReducer";
function noop(activities: Activity[]) {
return activities;
}
/**
* Create activity list reducers for each event type (Activity[] + Event => Activity[])
*/
export function makeActivitiesReducer({
transitionDuration,
now,
resumedAt,
}: {
transitionDuration: number;
now: number;
resumedAt?: number;
}) {
return makeReducer({
/**
* Push new activity to activities
*/
Pushed(activities: Activity[], event: PushedEvent): Activity[] {
const isTransitionDone =
now - (resumedAt ?? event.eventDate) >= transitionDuration;
const transitionState: ActivityTransitionState =
event.skipEnterActiveState || isTransitionDone
? "enter-done"
: "enter-active";
const reservedIndex = findNewActivityIndex(activities, event);
return [
...activities.slice(0, reservedIndex),
makeActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1),
];
},
/**
* Replace activity at reservedIndex with new activity
*/
Replaced(activities: Activity[], event: ReplacedEvent): Activity[] {
const isTransitionDone =
now - (resumedAt ?? event.eventDate) >= transitionDuration;
const reservedIndex = findNewActivityIndex(activities, event);
// reuse state of alreadyExistingActivity
const transitionState =
activities[reservedIndex]?.transitionState ??
(event.skipEnterActiveState || isTransitionDone
? "enter-done"
: "enter-active");
return [
...activities.slice(0, reservedIndex),
makeActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1),
];
},
/**
* noop
*/
Initialized: noop,
ActivityRegistered: noop,
Popped: noop,
StepPushed: noop,
StepReplaced: noop,
StepPopped: noop,
Paused: noop,
Resumed: noop,
});
}
import type { Activity, ActivityTransitionState } from "../Stack";
import type { PushedEvent, ReplacedEvent } from "../event-types";
export function makeActivityFromEvent(
event: PushedEvent | ReplacedEvent,
transitionState: ActivityTransitionState,
): Activity {
return {
id: event.activityId,
name: event.activityName,
transitionState,
params: event.activityParams,
context: event.activityContext,
steps: [
{
id: event.activityId,
params: event.activityParams,
enteredBy: event,
zIndex: -1,
},
],
enteredBy: event,
isTop: false,
isActive: false,
isRoot: false,
zIndex: -1,
};
}
import type { Activity, ActivityTransitionState } from "../Stack";
import type {
DomainEvent,
PoppedEvent,
ReplacedEvent,
StepPoppedEvent,
StepPushedEvent,
StepReplacedEvent,
} from "../event-types";
import { last } from "../utils";
import { makeReducer } from "./makeReducer";
function noop(activity: Activity) {
return activity;
}
/**
* Create activity reducers for each event type (Activity + Event => Activity)
*/
export function makeActivityReducer(context: {
transitionDuration: number;
now: number;
resumedAt?: number;
}) {
return makeReducer({
/**
* Change transition state to exit-done
*/
Replaced: (activity: Activity, event: ReplacedEvent): Activity => ({
...activity,
exitedBy: event,
transitionState: "exit-done",
}),
/**
* Change transition state to exit-done or exit-active depending on skipExitActiveState
*/
Popped: (activity: Activity, event: PoppedEvent): Activity => {
const isTransitionDone =
context.now - (context.resumedAt ?? event.eventDate) >=
context.transitionDuration;
const transitionState: ActivityTransitionState =
event.skipExitActiveState || isTransitionDone
? "exit-done"
: "exit-active";
return {
...activity,
exitedBy: event,
transitionState,
params:
transitionState === "exit-done"
? activity.steps[0].params
: activity.params,
steps:
transitionState === "exit-done"
? [activity.steps[0]]
: activity.steps,
};
},
/**
* Replace step params
* Push new step
*/
StepPushed: (activity: Activity, event: StepPushedEvent): Activity => {
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event,
zIndex: activity.zIndex,
hasZIndex: event.hasZIndex ?? false,
};
return {
...activity,
params: event.stepParams,
steps: [...activity.steps, newRoute],
};
},
/**
* Replace step params
* Replace the last step
*/
StepReplaced: (activity: Activity, event: StepReplacedEvent): Activity => {
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event,
zIndex: activity.zIndex,
hasZIndex: event.hasZIndex ?? false,
};
return {
...activity,
params: event.stepParams,
steps: [
...activity.steps.slice(0, activity.steps.length - 1),
newRoute,
],
};
},
/**
* Pop the last step
* If there are params in the previous step, set them as the new params
*/
StepPopped: (activity: Activity, event: StepPoppedEvent): Activity => {
activity.steps.pop();
const beforeActivityParams = last(activity.steps)?.params;
return {
...activity,
params: beforeActivityParams ?? activity.params,
};
},
/**
* noop
*/
Initialized: noop,
ActivityRegistered: noop,
Pushed: noop,
Paused: noop,
Resumed: noop,
} as const);
}
import type { DomainEvent } from "../event-types";
type Reducer<T> = (state: T, event: DomainEvent) => T;
export function makeReducer<T>(
reducerMap: {
[key in DomainEvent["name"]]: (
state: T,
event: Extract<DomainEvent, { name: key }>,
) => T;
},
) {
return (target: T, event: DomainEvent) => {
const reducer = (reducerMap[event.name] as Reducer<T>).bind(reducerMap);
if (reducer) {
return reducer(target, event);
}
throw new Error(`No reducer for event ${JSON.stringify(event)}`);
};
}
import type { Activity, Stack } from "../Stack";
import type {
ActivityRegisteredEvent,
DomainEvent,
InitializedEvent,
PausedEvent,
ResumedEvent,
} from "../event-types";
import { findTargetActivityIndices } from "./findTargetActivityIndices";
import { makeActivitiesReducer } from "./makeActivitiesReducer";
import { makeActivityReducer } from "./makeActivityReducer";
import { makeReducer } from "./makeReducer";
function withPauseReducer<T extends DomainEvent>(
reducer: (stack: Stack, event: T) => Stack,
) {
return (stack: Stack, event: T) => {
if (stack.globalTransitionState === "paused") {
return {
...stack,
pausedEvents: stack.pausedEvents
? [...stack.pausedEvents, event]
: [event],
};
}
return reducer(stack, event);
};
}
function withActivitiesReducer<T extends DomainEvent>(
reducer: (stack: Stack, event: T) => Stack,
context: {
now: number;
resumedAt?: number;
},
) {
return (stack: Stack, event: T) => {
const activitiesReducer = makeActivitiesReducer({
transitionDuration: stack.transitionDuration,
now: context.now,
resumedAt: context.resumedAt,
});
const activityReducer = makeActivityReducer({
transitionDuration: stack.transitionDuration,
now: context.now,
resumedAt: context.resumedAt,
});
const activities = activitiesReducer(stack.activities, event);
const targetActivityIndices = findTargetActivityIndices(
stack.activities,
event,
{ transitionDuration: stack.transitionDuration, now: context.now },
);
for (const targetActivityIndex of targetActivityIndices) {
activities[targetActivityIndex] = activityReducer(
activities[targetActivityIndex],
event,
);
}
const isLoading = activities.find(
(activity) =>
activity.transitionState === "enter-active" ||
activity.transitionState === "exit-active",
);
const globalTransitionState =
stack.globalTransitionState === "paused"
? "paused"
: isLoading
? "loading"
: "idle";
return reducer({ ...stack, activities, globalTransitionState }, event);
};
}
function noop(stack: Stack) {
return stack;
}
export function makeStackReducer(context: {
now: number;
resumedAt?: number;
}) {
return makeReducer({
Initialized: withPauseReducer(
withActivitiesReducer((stack: Stack, event: InitializedEvent): Stack => {
return {
...stack,
transitionDuration: event.transitionDuration,
};
}, context),
),
ActivityRegistered: withPauseReducer(
withActivitiesReducer(
(stack: Stack, event: ActivityRegisteredEvent): Stack => {
return {
...stack,
registeredActivities: [
...stack.registeredActivities,
{
name: event.activityName,
...(event.activityParamsSchema
? {
paramsSchema: event.activityParamsSchema,
}
: null),
},
],
};
},
context,
),
),
Paused: withPauseReducer(
withActivitiesReducer((stack: Stack, event: PausedEvent): Stack => {
return {
...stack,
globalTransitionState: "paused",
};
}, context),
),
Resumed: withActivitiesReducer(
(stack: Stack, event: ResumedEvent): Stack => {
if (stack.globalTransitionState !== "paused" || !stack.pausedEvents) {
return stack;
}
const reducer = makeStackReducer({
now: context.now,
resumedAt: event.eventDate,
});
const { pausedEvents, ...rest } = stack;
return pausedEvents.reduce(reducer, {
...rest,
globalTransitionState: "idle",
});
},
context,
),
Pushed: withPauseReducer(withActivitiesReducer(noop, context)),
Replaced: withPauseReducer(withActivitiesReducer(noop, context)),
Popped: withPauseReducer(withActivitiesReducer(noop, context)),
StepPushed: withPauseReducer(withActivitiesReducer(noop, context)),
StepReplaced: withPauseReducer(withActivitiesReducer(noop, context)),
StepPopped: withPauseReducer(withActivitiesReducer(noop, context)),
});
}
import type { BaseDomainEvent } from "./_base";
export type PausedEvent = BaseDomainEvent<"Paused", {}>;
import type { BaseDomainEvent } from "./_base";
export type ResumedEvent = BaseDomainEvent<"Resumed", {}>;
import type { StackflowActions } from "../interfaces";
import type { StackflowPlugin } from "../interfaces";
import { triggerPreEffectHook } from "./triggerPreEffectHooks";
type ActionCreatorOptions = {
dispatchEvent: StackflowActions["dispatchEvent"];
pluginInstances: ReturnType<StackflowPlugin>[];
actions: StackflowActions;
};
export function makeActions({
dispatchEvent,
pluginInstances,
actions,
}: ActionCreatorOptions): Omit<StackflowActions, "dispatchEvent" | "getStack"> {
return {
push(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Pushed",
params,
pluginInstances,
actions,
);
if (isPrevented) {
return;
}
dispatchEvent("Pushed", nextActionParams);
},
replace(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Replaced",
params,
pluginInstances,
actions,
);
if (isPrevented) {
return;
}
dispatchEvent("Replaced", nextActionParams);
},
pop(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Popped",
params,
pluginInstances,
actions,
);
if (isPrevented) {
return;
}
dispatchEvent("Popped", nextActionParams);
},
stepPush(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"StepPushed",
params,
pluginInstances,
actions,
);
if (isPrevented) {
return;
}
dispatchEvent("StepPushed", nextActionParams);
},
stepReplace(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"StepReplaced",
params,
pluginInstances,
actions,
);
if (isPrevented) {
return;
}
dispatchEvent("StepReplaced", nextActionParams);
},
stepPop(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"StepPopped",
params,
pluginInstances,
actions,
);
if (isPrevented) {
return;
}
dispatchEvent("StepPopped", nextActionParams);
},
pause(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Paused",
params,
pluginInstances,
actions,
);
if (isPrevented) {
return;
}
dispatchEvent("Paused", nextActionParams);
},
resume(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Resumed",
params,
pluginInstances,
actions,
);
if (isPrevented) {
return;
}
dispatchEvent("Resumed", nextActionParams);
},
};
}
import type { Effect } from "../Effect";
import type { StackflowActions, StackflowPlugin } from "../interfaces";
export function triggerPostEffectHooks(
effects: Effect[],
plugins: ReturnType<StackflowPlugin>[],
actions: StackflowActions,
): void {
effects.forEach((effect) => {
plugins.forEach((plugin) => {
switch (effect._TAG) {
case "PUSHED":
plugin.onPushed?.({ actions, effect });
break;
case "REPLACED":
plugin.onReplaced?.({ actions, effect });
break;
case "POPPED":
plugin.onPopped?.({ actions, effect });
break;
case "STEP_PUSHED":
plugin.onStepPushed?.({ actions, effect });
break;
case "STEP_REPLACED":
plugin.onStepReplaced?.({ actions, effect });
break;
case "STEP_POPPED":
plugin.onStepPopped?.({ actions, effect });
break;
case "PAUSED":
plugin.onPaused?.({ actions, effect });
break;
case "RESUMED":
plugin.onResumed?.({ actions, effect });
break;
case "%SOMETHING_CHANGED%":
plugin.onChanged?.({ actions, effect });
break;
}
});
});
}
import type { DomainEvent } from "../event-types";
import type { BaseDomainEvent } from "../event-types/_base";
import type { StackflowPlugin } from "../interfaces";
import type { StackflowActions } from "../interfaces";
import type { StackflowPluginPreEffectHook } from "../interfaces/StackflowPluginHook";
type PreEffectHookResult<T> = {
isPrevented: boolean;
nextActionParams: T;
};
type EventNameToParams<K extends ActionName> = Omit<
Extract<DomainEvent, { name: K }>,
keyof BaseDomainEvent
>;
const PLUGIN_HOOK_MAP = {
Pushed: "onBeforePush",
Replaced: "onBeforeReplace",
Popped: "onBeforePop",
StepPushed: "onBeforeStepPush",
StepReplaced: "onBeforeStepReplace",
StepPopped: "onBeforeStepPop",
Paused: "onBeforePause",
Resumed: "onBeforeResume",
} as const;
type ActionName = Exclude<
DomainEvent["name"],
"Initialized" | "ActivityRegistered"
>;
export function triggerPreEffectHook<K extends ActionName>(
actionName: K,
actionParams: EventNameToParams<K>,
pluginInstances: ReturnType<StackflowPlugin>[],
actions: StackflowActions,
): PreEffectHookResult<EventNameToParams<K>> {
let isPrevented = false;
let nextActionParams = { ...actionParams };
for (const pluginInstance of pluginInstances) {
const hook = pluginInstance[PLUGIN_HOOK_MAP[actionName]] as
| StackflowPluginPreEffectHook<EventNameToParams<K>>
| undefined;
if (hook) {
hook({
actionParams: { ...nextActionParams },
actions: {
...actions,
preventDefault: () => {
isPrevented = true;
},
overrideActionParams: (partialActionParams: EventNameToParams<K>) => {
nextActionParams = {
...nextActionParams,
...partialActionParams,
};
},
},
});
}
}
return {
isPrevented,
nextActionParams,
};
}
+1
-1
import type { Activity } from "../Stack";
import type { PushedEvent, ReplacedEvent } from "../event-types";
export default function findNewActivityIndex(event: PushedEvent | ReplacedEvent, activities: Activity[]): number;
export declare function findNewActivityIndex(activities: Activity[], event: PushedEvent | ReplacedEvent): number;
//# sourceMappingURL=findNewActivityIndex.d.ts.map
import type { Stack } from "./Stack";
import type { DomainEvent } from "./event-types";
export declare function aggregate(events: DomainEvent[], now: number): Stack;
export declare function aggregate(inputEvents: DomainEvent[], now: number): Stack;
//# sourceMappingURL=aggregate.d.ts.map

@@ -24,3 +24,7 @@ import type { Activity, ActivityStep } from "./Stack";

activity: Activity;
} | {
_TAG: "PAUSED";
} | {
_TAG: "RESUMED";
};
//# sourceMappingURL=Effect.d.ts.map
import type { ActivityRegisteredEvent } from "./ActivityRegisteredEvent";
import type { InitializedEvent } from "./InitializedEvent";
import type { PausedEvent } from "./PausedEvent";
import type { PoppedEvent } from "./PoppedEvent";
import type { PushedEvent } from "./PushedEvent";
import type { ReplacedEvent } from "./ReplacedEvent";
import type { ResumedEvent } from "./ResumedEvent";
import type { StepPoppedEvent } from "./StepPoppedEvent";
import type { StepPushedEvent } from "./StepPushedEvent";
import type { StepReplacedEvent } from "./StepReplacedEvent";
export type DomainEvent = ActivityRegisteredEvent | InitializedEvent | StepPoppedEvent | StepPushedEvent | StepReplacedEvent | PoppedEvent | PushedEvent | ReplacedEvent;
export type DomainEvent = ActivityRegisteredEvent | InitializedEvent | StepPoppedEvent | StepPushedEvent | StepReplacedEvent | PoppedEvent | PushedEvent | ReplacedEvent | PausedEvent | ResumedEvent;
export * from "./ActivityRegisteredEvent";

@@ -18,2 +20,4 @@ export * from "./InitializedEvent";

export * from "./StepReplacedEvent";
export * from "./PausedEvent";
export * from "./ResumedEvent";
//# sourceMappingURL=index.d.ts.map

@@ -8,3 +8,4 @@ import type { BaseDomainEvent } from "./_base";

targetActivityId?: string;
hasZIndex?: boolean;
}>;
//# sourceMappingURL=StepPushedEvent.d.ts.map

@@ -8,3 +8,4 @@ import type { BaseDomainEvent } from "./_base";

targetActivityId?: string;
hasZIndex?: boolean;
}>;
//# sourceMappingURL=StepReplacedEvent.d.ts.map

@@ -25,2 +25,14 @@ "use strict";

var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __objRest = (source, exclude) => {
var target = {};
for (var prop in source)
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
target[prop] = source[prop];
if (source != null && __getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(source)) {
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
target[prop] = source[prop];
}
return target;
};
var __export = (target, all) => {

@@ -150,3 +162,3 @@ for (var name in all)

// src/activity-utils/findTargetActivityIndexes.ts
// src/activity-utils/findTargetActivityIndices.ts
function isActivityNotExited(activity) {

@@ -161,3 +173,3 @@ return !activity.exitedBy;

}
function findTargetActivityIndexes(activities, event, isTransitionDone) {
function findTargetActivityIndices(activities, event, context) {
const targetActivities = [];

@@ -173,2 +185,3 @@ switch (event.name) {

const sorted = activities.slice().sort(compareActivitiesByEventDate).filter(isActivityNotExited);
const isTransitionDone = context.now - event.eventDate >= context.transitionDuration;
const transitionState = event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";

@@ -218,39 +231,6 @@ if (transitionState === "enter-done") {

// src/activity-utils/createActivityFromEvent.ts
var createActivityFromEvent = (event, transitionState) => ({
id: event.activityId,
name: event.activityName,
transitionState,
params: event.activityParams,
context: event.activityContext,
steps: [
{
id: event.activityId,
params: event.activityParams,
enteredBy: event
}
],
enteredBy: event,
isTop: false,
isActive: false,
isRoot: false,
zIndex: -1
});
// src/activity-utils/createReducer.ts
function createReducer(reducerMap) {
return (activity, event) => {
const reducer = reducerMap[event.name];
if (reducer) {
return reducer(activity, event);
}
throw new Error(`No reducer for event ${JSON.stringify(event)}`);
};
}
// src/activity-utils/findNewActivityIndex.ts
function findNewActivityIndex(event, activities) {
function findNewActivityIndex(activities, event) {
switch (event.name) {
case "Pushed":
return activities.length;
case "Replaced": {

@@ -267,134 +247,279 @@ const alreadyExistingActivityIndex = last(

// src/activity-utils/makeActivitiesReducers.ts
var makeActivitiesReducers = (isTransitionDone) => createReducer({
/**
* noop
*/
Initialized: (activities, event) => activities,
/**
* noop
*/
ActivityRegistered: (activities, event) => activities,
/**
* Push new activity to activities
*/
Pushed: (activities, event) => {
const transitionState = event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";
const reservedIndex = findNewActivityIndex(event, activities);
return [
...activities.slice(0, reservedIndex),
createActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1)
];
},
/**
* Replace activity at reservedIndex with new activity
*/
Replaced: (activities, event) => {
var _a, _b;
const reservedIndex = findNewActivityIndex(event, activities);
const transitionState = (_b = (_a = activities[reservedIndex]) == null ? void 0 : _a.transitionState) != null ? _b : event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";
return [
...activities.slice(0, reservedIndex),
createActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1)
];
},
/**
* noop
*/
Popped: (activities, event) => activities,
/**
* noop
*/
StepPushed: (activities, event) => activities,
/**
* noop
*/
StepReplaced: (activities, event) => activities,
/**
* noop
*/
StepPopped: (activities, event) => activities
});
// src/activity-utils/makeActivityFromEvent.ts
function makeActivityFromEvent(event, transitionState) {
return {
id: event.activityId,
name: event.activityName,
transitionState,
params: event.activityParams,
context: event.activityContext,
steps: [
{
id: event.activityId,
params: event.activityParams,
enteredBy: event,
zIndex: -1
}
],
enteredBy: event,
isTop: false,
isActive: false,
isRoot: false,
zIndex: -1
};
}
// src/activity-utils/makeActivityReducers.ts
var makeActivityReducers = (isTransitionDone) => createReducer({
/**
* noop
*/
Initialized: (activity, event) => activity,
/**
* noop
*/
ActivityRegistered: (activity, event) => activity,
/**
* noop
*/
Pushed: (activity, event) => activity,
/**
* Change transition state to exit-done
*/
Replaced: (activity, event) => __spreadProps(__spreadValues({}, activity), {
exitedBy: event,
transitionState: "exit-done"
}),
/**
* Change transition state to exit-done or exit-active depending on skipExitActiveState
*/
Popped: (activity, event) => {
const transitionState = event.skipExitActiveState || isTransitionDone ? "exit-done" : "exit-active";
return __spreadProps(__spreadValues({}, activity), {
// src/activity-utils/makeReducer.ts
function makeReducer(reducerMap) {
return (target, event) => {
const reducer = reducerMap[event.name].bind(reducerMap);
if (reducer) {
return reducer(target, event);
}
throw new Error(`No reducer for event ${JSON.stringify(event)}`);
};
}
// src/activity-utils/makeActivitiesReducer.ts
function noop(activities) {
return activities;
}
function makeActivitiesReducer({
transitionDuration,
now,
resumedAt
}) {
return makeReducer({
/**
* Push new activity to activities
*/
Pushed(activities, event) {
const isTransitionDone = now - (resumedAt != null ? resumedAt : event.eventDate) >= transitionDuration;
const transitionState = event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";
const reservedIndex = findNewActivityIndex(activities, event);
return [
...activities.slice(0, reservedIndex),
makeActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1)
];
},
/**
* Replace activity at reservedIndex with new activity
*/
Replaced(activities, event) {
var _a, _b;
const isTransitionDone = now - (resumedAt != null ? resumedAt : event.eventDate) >= transitionDuration;
const reservedIndex = findNewActivityIndex(activities, event);
const transitionState = (_b = (_a = activities[reservedIndex]) == null ? void 0 : _a.transitionState) != null ? _b : event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";
return [
...activities.slice(0, reservedIndex),
makeActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1)
];
},
/**
* noop
*/
Initialized: noop,
ActivityRegistered: noop,
Popped: noop,
StepPushed: noop,
StepReplaced: noop,
StepPopped: noop,
Paused: noop,
Resumed: noop
});
}
// src/activity-utils/makeActivityReducer.ts
function noop2(activity) {
return activity;
}
function makeActivityReducer(context) {
return makeReducer({
/**
* Change transition state to exit-done
*/
Replaced: (activity, event) => __spreadProps(__spreadValues({}, activity), {
exitedBy: event,
transitionState,
params: transitionState === "exit-done" ? activity.steps[0].params : activity.params,
steps: transitionState === "exit-done" ? [activity.steps[0]] : activity.steps
transitionState: "exit-done"
}),
/**
* Change transition state to exit-done or exit-active depending on skipExitActiveState
*/
Popped: (activity, event) => {
var _a;
const isTransitionDone = context.now - ((_a = context.resumedAt) != null ? _a : event.eventDate) >= context.transitionDuration;
const transitionState = event.skipExitActiveState || isTransitionDone ? "exit-done" : "exit-active";
return __spreadProps(__spreadValues({}, activity), {
exitedBy: event,
transitionState,
params: transitionState === "exit-done" ? activity.steps[0].params : activity.params,
steps: transitionState === "exit-done" ? [activity.steps[0]] : activity.steps
});
},
/**
* Replace step params
* Push new step
*/
StepPushed: (activity, event) => {
var _a;
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event,
zIndex: activity.zIndex,
hasZIndex: (_a = event.hasZIndex) != null ? _a : false
};
return __spreadProps(__spreadValues({}, activity), {
params: event.stepParams,
steps: [...activity.steps, newRoute]
});
},
/**
* Replace step params
* Replace the last step
*/
StepReplaced: (activity, event) => {
var _a;
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event,
zIndex: activity.zIndex,
hasZIndex: (_a = event.hasZIndex) != null ? _a : false
};
return __spreadProps(__spreadValues({}, activity), {
params: event.stepParams,
steps: [
...activity.steps.slice(0, activity.steps.length - 1),
newRoute
]
});
},
/**
* Pop the last step
* If there are params in the previous step, set them as the new params
*/
StepPopped: (activity, event) => {
var _a;
activity.steps.pop();
const beforeActivityParams = (_a = last(activity.steps)) == null ? void 0 : _a.params;
return __spreadProps(__spreadValues({}, activity), {
params: beforeActivityParams != null ? beforeActivityParams : activity.params
});
},
/**
* noop
*/
Initialized: noop2,
ActivityRegistered: noop2,
Pushed: noop2,
Paused: noop2,
Resumed: noop2
});
}
// src/activity-utils/makeStackReducer.ts
function withPauseReducer(reducer) {
return (stack, event) => {
if (stack.globalTransitionState === "paused") {
return __spreadProps(__spreadValues({}, stack), {
pausedEvents: stack.pausedEvents ? [...stack.pausedEvents, event] : [event]
});
}
return reducer(stack, event);
};
}
function withActivitiesReducer(reducer, context) {
return (stack, event) => {
const activitiesReducer = makeActivitiesReducer({
transitionDuration: stack.transitionDuration,
now: context.now,
resumedAt: context.resumedAt
});
},
/**
* Replace step params
* Push new step
*/
StepPushed: (activity, event) => {
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event
};
return __spreadProps(__spreadValues({}, activity), {
params: event.stepParams,
steps: [...activity.steps, newRoute]
const activityReducer = makeActivityReducer({
transitionDuration: stack.transitionDuration,
now: context.now,
resumedAt: context.resumedAt
});
},
/**
* Replace step params
* Replace the last step
*/
StepReplaced: (activity, event) => {
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event
};
return __spreadProps(__spreadValues({}, activity), {
params: event.stepParams,
steps: [
...activity.steps.slice(0, activity.steps.length - 1),
newRoute
]
});
},
/**
* Pop the last step
* If there are params in the previous step, set them as the new params
*/
StepPopped: (activity, event) => {
var _a;
activity.steps.pop();
const beforeActivityParams = (_a = last(activity.steps)) == null ? void 0 : _a.params;
return __spreadProps(__spreadValues({}, activity), {
params: beforeActivityParams != null ? beforeActivityParams : activity.params
});
}
});
const activities = activitiesReducer(stack.activities, event);
const targetActivityIndices = findTargetActivityIndices(
stack.activities,
event,
{ transitionDuration: stack.transitionDuration, now: context.now }
);
for (const targetActivityIndex of targetActivityIndices) {
activities[targetActivityIndex] = activityReducer(
activities[targetActivityIndex],
event
);
}
const isLoading = activities.find(
(activity) => activity.transitionState === "enter-active" || activity.transitionState === "exit-active"
);
const globalTransitionState = stack.globalTransitionState === "paused" ? "paused" : isLoading ? "loading" : "idle";
return reducer(__spreadProps(__spreadValues({}, stack), { activities, globalTransitionState }), event);
};
}
function noop3(stack) {
return stack;
}
function makeStackReducer(context) {
return makeReducer({
Initialized: withPauseReducer(
withActivitiesReducer((stack, event) => {
return __spreadProps(__spreadValues({}, stack), {
transitionDuration: event.transitionDuration
});
}, context)
),
ActivityRegistered: withPauseReducer(
withActivitiesReducer(
(stack, event) => {
return __spreadProps(__spreadValues({}, stack), {
registeredActivities: [
...stack.registeredActivities,
__spreadValues({
name: event.activityName
}, event.activityParamsSchema ? {
paramsSchema: event.activityParamsSchema
} : null)
]
});
},
context
)
),
Paused: withPauseReducer(
withActivitiesReducer((stack, event) => {
return __spreadProps(__spreadValues({}, stack), {
globalTransitionState: "paused"
});
}, context)
),
Resumed: withActivitiesReducer(
(stack, event) => {
if (stack.globalTransitionState !== "paused" || !stack.pausedEvents) {
return stack;
}
const reducer = makeStackReducer({
now: context.now,
resumedAt: event.eventDate
});
const _a = stack, { pausedEvents } = _a, rest = __objRest(_a, ["pausedEvents"]);
return pausedEvents.reduce(reducer, __spreadProps(__spreadValues({}, rest), {
globalTransitionState: "idle"
}));
},
context
),
Pushed: withPauseReducer(withActivitiesReducer(noop3, context)),
Replaced: withPauseReducer(withActivitiesReducer(noop3, context)),
Popped: withPauseReducer(withActivitiesReducer(noop3, context)),
StepPushed: withPauseReducer(withActivitiesReducer(noop3, context)),
StepReplaced: withPauseReducer(withActivitiesReducer(noop3, context)),
StepPopped: withPauseReducer(withActivitiesReducer(noop3, context))
});
}

@@ -436,34 +561,16 @@ // src/event-utils/filterEvents.ts

// src/aggregate.ts
function aggregate(events, now) {
const sortedEvents = uniqBy(
[...events].sort((a, b) => compareBy(a, b, (e) => e.id)),
function aggregate(inputEvents, now) {
const events = uniqBy(
[...inputEvents].sort((a, b) => compareBy(a, b, (e) => e.id)),
(e) => e.id
);
validateEvents(sortedEvents);
const initEvent = filterEvents(sortedEvents, "Initialized")[0];
const activityRegisteredEvents = filterEvents(events, "ActivityRegistered");
const { transitionDuration } = initEvent;
const activities = sortedEvents.reduce(
(activities2, event) => {
const isTransitionDone = now - event.eventDate >= transitionDuration;
const targets = findTargetActivityIndexes(
activities2,
event,
isTransitionDone
);
const activityReducer = makeActivityReducers(isTransitionDone);
const activitiesReducer = makeActivitiesReducers(isTransitionDone);
const newActivities = activitiesReducer(activities2, event);
targets.forEach((targetIdx) => {
newActivities[targetIdx] = activityReducer(
newActivities[targetIdx],
event
);
});
return newActivities;
},
[]
);
const uniqActivities = uniqBy(activities, (activity) => activity.id);
const visibleActivities = uniqActivities.filter(
validateEvents(events);
const stackReducer = makeStackReducer({ now });
const stack = events.reduce(stackReducer, {
activities: [],
globalTransitionState: "idle",
registeredActivities: [],
transitionDuration: 0
});
const visibleActivities = stack.activities.filter(
(activity) => activity.transitionState === "enter-active" || activity.transitionState === "enter-done" || activity.transitionState === "exit-active"

@@ -476,11 +583,27 @@ );

const lastEnteredActivity = enteredActivities[enteredActivities.length - 1];
const globalTransitionState = activities.find(
(activity) => activity.transitionState === "enter-active" || activity.transitionState === "exit-active"
) ? "loading" : "idle";
const output = {
activities: uniqActivities.map((activity) => {
const zIndex = visibleActivities.findIndex(
const output = __spreadProps(__spreadValues({}, stack), {
activities: stack.activities.map((activity) => {
let zIndex = visibleActivities.findIndex(
({ id: id2 }) => id2 === activity.id
);
return __spreadValues(__spreadProps(__spreadValues({
const beforeActivities = visibleActivities.slice(0, zIndex);
for (const beforeActivity of beforeActivities) {
for (const step of beforeActivity.steps) {
if (step.hasZIndex) {
zIndex += 1;
}
}
}
const steps = activity.steps.reduce((acc, step) => {
var _a;
const lastStep = last(acc);
const lastStepZIndex = (_a = lastStep == null ? void 0 : lastStep.zIndex) != null ? _a : zIndex;
return [
...acc,
__spreadProps(__spreadValues({}, step), {
zIndex: lastStepZIndex + (step.hasZIndex ? 1 : 0)
})
];
}, []);
return __spreadValues(__spreadValues({
id: activity.id,

@@ -490,23 +613,15 @@ name: activity.name,

params: activity.params,
steps: activity.steps,
enteredBy: activity.enteredBy
steps,
enteredBy: activity.enteredBy,
zIndex,
isTop: (lastVisibleActivity == null ? void 0 : lastVisibleActivity.id) === activity.id,
isActive: (lastEnteredActivity == null ? void 0 : lastEnteredActivity.id) === activity.id,
isRoot: zIndex === 0 || zIndex === 1 && activity.transitionState === "enter-active" && activity.enteredBy.name === "Replaced"
}, activity.exitedBy ? {
exitedBy: activity.exitedBy
} : null), {
isTop: (lastVisibleActivity == null ? void 0 : lastVisibleActivity.id) === activity.id,
isActive: (lastEnteredActivity == null ? void 0 : lastEnteredActivity.id) === activity.id,
isRoot: zIndex === 0 || zIndex === 1 && activity.transitionState === "enter-active" && activity.enteredBy.name === "Replaced",
zIndex
}), activity.context ? {
} : null), activity.context ? {
context: activity.context
} : null);
}).sort((a, b) => compareBy(a, b, (activity) => activity.id)),
registeredActivities: activityRegisteredEvents.map((event) => __spreadValues({
name: event.activityName
}, event.activityParamsSchema ? {
paramsSchema: event.activityParamsSchema
} : null)),
transitionDuration,
globalTransitionState
};
}).sort((a, b) => compareBy(a, b, (activity) => activity.id))
});
return output;

@@ -529,2 +644,14 @@ }

}
const isPaused = prevOutput.globalTransitionState !== "paused" && nextOutput.globalTransitionState === "paused";
const isResumed = prevOutput.globalTransitionState === "paused" && nextOutput.globalTransitionState !== "paused";
if (isPaused) {
output.push({
_TAG: "PAUSED"
});
}
if (isResumed) {
output.push({
_TAG: "RESUMED"
});
}
for (let i = 0; i < Math.max(prevOutput.activities.length, nextOutput.activities.length); i += 1) {

@@ -608,2 +735,182 @@ const prevActivity = prevOutput.activities[i];

// src/utils/triggerPreEffectHooks.ts
var PLUGIN_HOOK_MAP = {
Pushed: "onBeforePush",
Replaced: "onBeforeReplace",
Popped: "onBeforePop",
StepPushed: "onBeforeStepPush",
StepReplaced: "onBeforeStepReplace",
StepPopped: "onBeforeStepPop",
Paused: "onBeforePause",
Resumed: "onBeforeResume"
};
function triggerPreEffectHook(actionName, actionParams, pluginInstances, actions) {
let isPrevented = false;
let nextActionParams = __spreadValues({}, actionParams);
for (const pluginInstance of pluginInstances) {
const hook = pluginInstance[PLUGIN_HOOK_MAP[actionName]];
if (hook) {
hook({
actionParams: __spreadValues({}, nextActionParams),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault: () => {
isPrevented = true;
},
overrideActionParams: (partialActionParams) => {
nextActionParams = __spreadValues(__spreadValues({}, nextActionParams), partialActionParams);
}
})
});
}
}
return {
isPrevented,
nextActionParams
};
}
// src/utils/makeActions.ts
function makeActions({
dispatchEvent,
pluginInstances,
actions
}) {
return {
push(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Pushed",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Pushed", nextActionParams);
},
replace(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Replaced",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Replaced", nextActionParams);
},
pop(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Popped",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Popped", nextActionParams);
},
stepPush(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"StepPushed",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("StepPushed", nextActionParams);
},
stepReplace(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"StepReplaced",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("StepReplaced", nextActionParams);
},
stepPop(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"StepPopped",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("StepPopped", nextActionParams);
},
pause(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Paused",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Paused", nextActionParams);
},
resume(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Resumed",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Resumed", nextActionParams);
}
};
}
// src/utils/triggerPostEffectHooks.ts
function triggerPostEffectHooks(effects, plugins, actions) {
effects.forEach((effect) => {
plugins.forEach((plugin) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
switch (effect._TAG) {
case "PUSHED":
(_a = plugin.onPushed) == null ? void 0 : _a.call(plugin, { actions, effect });
break;
case "REPLACED":
(_b = plugin.onReplaced) == null ? void 0 : _b.call(plugin, { actions, effect });
break;
case "POPPED":
(_c = plugin.onPopped) == null ? void 0 : _c.call(plugin, { actions, effect });
break;
case "STEP_PUSHED":
(_d = plugin.onStepPushed) == null ? void 0 : _d.call(plugin, { actions, effect });
break;
case "STEP_REPLACED":
(_e = plugin.onStepReplaced) == null ? void 0 : _e.call(plugin, { actions, effect });
break;
case "STEP_POPPED":
(_f = plugin.onStepPopped) == null ? void 0 : _f.call(plugin, { actions, effect });
break;
case "PAUSED":
(_g = plugin.onPaused) == null ? void 0 : _g.call(plugin, { actions, effect });
break;
case "RESUMED":
(_h = plugin.onResumed) == null ? void 0 : _h.call(plugin, { actions, effect });
break;
case "%SOMETHING_CHANGED%":
(_i = plugin.onChanged) == null ? void 0 : _i.call(plugin, { actions, effect });
break;
}
});
});
}
// src/makeCoreStore.ts

@@ -652,157 +959,2 @@ var SECOND = 1e3;

};
const setStackValue = (nextStackValue) => {
const effects = produceEffects(stack.value, nextStackValue);
stack.value = nextStackValue;
triggerPostEffectHooks(effects, pluginInstances);
};
const dispatchEvent = (name, params) => {
const newEvent = makeEvent(name, params);
const nextStackValue = aggregate(
[...events.value, newEvent],
(/* @__PURE__ */ new Date()).getTime()
);
events.value.push(newEvent);
setStackValue(nextStackValue);
const interval = setInterval(() => {
const nextStackValue2 = aggregate(events.value, (/* @__PURE__ */ new Date()).getTime());
if (!(0, import_react_fast_compare2.default)(stack.value, nextStackValue2)) {
setStackValue(nextStackValue2);
}
if (nextStackValue2.globalTransitionState === "idle") {
clearInterval(interval);
}
}, INTERVAL_MS);
};
function triggerPreEffectHooks(event, plugins) {
let isPrevented = false;
let nextEvent = __spreadValues({}, event);
function toParams(event2) {
const params = __spreadValues({}, event2);
params.name = void 0;
return params;
}
const preventDefault = () => {
isPrevented = true;
};
const overrideActionParams = (nextActionParams) => {
nextEvent = __spreadValues(__spreadValues({}, nextEvent), nextActionParams);
};
plugins.forEach((plugin) => {
var _a2, _b2, _c2, _d2, _e, _f;
switch (nextEvent.name) {
case "Pushed": {
(_a2 = plugin.onBeforePush) == null ? void 0 : _a2.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "Replaced": {
(_b2 = plugin.onBeforeReplace) == null ? void 0 : _b2.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "Popped": {
(_c2 = plugin.onBeforePop) == null ? void 0 : _c2.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "StepPushed": {
(_d2 = plugin.onBeforeStepPush) == null ? void 0 : _d2.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "StepReplaced": {
(_e = plugin.onBeforeStepReplace) == null ? void 0 : _e.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "StepPopped": {
(_f = plugin.onBeforeStepPop) == null ? void 0 : _f.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
default:
break;
}
});
return {
isPrevented,
overriddenParams: toParams(nextEvent)
};
}
function triggerPostEffectHooks(effects, plugins) {
effects.forEach((effect) => {
plugins.forEach((plugin) => {
var _a2, _b2, _c2, _d2, _e, _f, _g;
switch (effect._TAG) {
case "PUSHED":
return (_a2 = plugin.onPushed) == null ? void 0 : _a2.call(plugin, {
actions,
effect
});
case "REPLACED":
return (_b2 = plugin.onReplaced) == null ? void 0 : _b2.call(plugin, {
actions,
effect
});
case "POPPED":
return (_c2 = plugin.onPopped) == null ? void 0 : _c2.call(plugin, {
actions,
effect
});
case "STEP_PUSHED":
return (_d2 = plugin.onStepPushed) == null ? void 0 : _d2.call(plugin, {
actions,
effect
});
case "STEP_REPLACED":
return (_e = plugin.onStepReplaced) == null ? void 0 : _e.call(plugin, {
actions,
effect
});
case "STEP_POPPED":
return (_f = plugin.onStepPopped) == null ? void 0 : _f.call(plugin, {
actions,
effect
});
case "%SOMETHING_CHANGED%":
return (_g = plugin.onChanged) == null ? void 0 : _g.call(plugin, {
actions,
effect
});
default:
return void 0;
}
});
});
}
const actions = {

@@ -812,64 +964,50 @@ getStack() {

},
dispatchEvent,
push(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("Pushed", params),
pluginInstances
dispatchEvent(name, params) {
const newEvent = makeEvent(name, params);
const nextStackValue = aggregate(
[...events.value, newEvent],
(/* @__PURE__ */ new Date()).getTime()
);
if (isPrevented) {
return;
}
dispatchEvent("Pushed", overriddenParams);
events.value.push(newEvent);
setStackValue(nextStackValue);
const interval = setInterval(() => {
const nextStackValue2 = aggregate(events.value, (/* @__PURE__ */ new Date()).getTime());
if (!(0, import_react_fast_compare2.default)(stack.value, nextStackValue2)) {
setStackValue(nextStackValue2);
}
if (nextStackValue2.globalTransitionState === "idle") {
clearInterval(interval);
}
}, INTERVAL_MS);
},
replace(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("Replaced", params),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("Replaced", overriddenParams);
push: () => {
},
pop(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("Popped", params != null ? params : {}),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("Popped", overriddenParams);
replace: () => {
},
stepPush(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("StepPushed", params != null ? params : {}),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("StepPushed", overriddenParams);
pop: () => {
},
stepReplace(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("StepReplaced", params != null ? params : {}),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("StepReplaced", overriddenParams);
stepPush: () => {
},
stepPop(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("StepPopped", params != null ? params : {}),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("StepPopped", overriddenParams);
stepReplace: () => {
},
stepPop: () => {
},
pause: () => {
},
resume: () => {
}
};
const setStackValue = (nextStackValue) => {
const effects = produceEffects(stack.value, nextStackValue);
stack.value = nextStackValue;
triggerPostEffectHooks(effects, pluginInstances, actions);
};
Object.assign(
actions,
makeActions({
dispatchEvent: actions.dispatchEvent,
pluginInstances,
actions
})
);
return {

@@ -876,0 +1014,0 @@ actions,

@@ -20,2 +20,14 @@ var __defProp = Object.defineProperty;

var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __objRest = (source, exclude) => {
var target = {};
for (var prop in source)
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
target[prop] = source[prop];
if (source != null && __getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(source)) {
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
target[prop] = source[prop];
}
return target;
};

@@ -113,3 +125,3 @@ // src/utils/compareBy.ts

// src/activity-utils/findTargetActivityIndexes.ts
// src/activity-utils/findTargetActivityIndices.ts
function isActivityNotExited(activity) {

@@ -124,3 +136,3 @@ return !activity.exitedBy;

}
function findTargetActivityIndexes(activities, event, isTransitionDone) {
function findTargetActivityIndices(activities, event, context) {
const targetActivities = [];

@@ -136,2 +148,3 @@ switch (event.name) {

const sorted = activities.slice().sort(compareActivitiesByEventDate).filter(isActivityNotExited);
const isTransitionDone = context.now - event.eventDate >= context.transitionDuration;
const transitionState = event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";

@@ -181,39 +194,6 @@ if (transitionState === "enter-done") {

// src/activity-utils/createActivityFromEvent.ts
var createActivityFromEvent = (event, transitionState) => ({
id: event.activityId,
name: event.activityName,
transitionState,
params: event.activityParams,
context: event.activityContext,
steps: [
{
id: event.activityId,
params: event.activityParams,
enteredBy: event
}
],
enteredBy: event,
isTop: false,
isActive: false,
isRoot: false,
zIndex: -1
});
// src/activity-utils/createReducer.ts
function createReducer(reducerMap) {
return (activity, event) => {
const reducer = reducerMap[event.name];
if (reducer) {
return reducer(activity, event);
}
throw new Error(`No reducer for event ${JSON.stringify(event)}`);
};
}
// src/activity-utils/findNewActivityIndex.ts
function findNewActivityIndex(event, activities) {
function findNewActivityIndex(activities, event) {
switch (event.name) {
case "Pushed":
return activities.length;
case "Replaced": {

@@ -230,134 +210,279 @@ const alreadyExistingActivityIndex = last(

// src/activity-utils/makeActivitiesReducers.ts
var makeActivitiesReducers = (isTransitionDone) => createReducer({
/**
* noop
*/
Initialized: (activities, event) => activities,
/**
* noop
*/
ActivityRegistered: (activities, event) => activities,
/**
* Push new activity to activities
*/
Pushed: (activities, event) => {
const transitionState = event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";
const reservedIndex = findNewActivityIndex(event, activities);
return [
...activities.slice(0, reservedIndex),
createActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1)
];
},
/**
* Replace activity at reservedIndex with new activity
*/
Replaced: (activities, event) => {
var _a, _b;
const reservedIndex = findNewActivityIndex(event, activities);
const transitionState = (_b = (_a = activities[reservedIndex]) == null ? void 0 : _a.transitionState) != null ? _b : event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";
return [
...activities.slice(0, reservedIndex),
createActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1)
];
},
/**
* noop
*/
Popped: (activities, event) => activities,
/**
* noop
*/
StepPushed: (activities, event) => activities,
/**
* noop
*/
StepReplaced: (activities, event) => activities,
/**
* noop
*/
StepPopped: (activities, event) => activities
});
// src/activity-utils/makeActivityFromEvent.ts
function makeActivityFromEvent(event, transitionState) {
return {
id: event.activityId,
name: event.activityName,
transitionState,
params: event.activityParams,
context: event.activityContext,
steps: [
{
id: event.activityId,
params: event.activityParams,
enteredBy: event,
zIndex: -1
}
],
enteredBy: event,
isTop: false,
isActive: false,
isRoot: false,
zIndex: -1
};
}
// src/activity-utils/makeActivityReducers.ts
var makeActivityReducers = (isTransitionDone) => createReducer({
/**
* noop
*/
Initialized: (activity, event) => activity,
/**
* noop
*/
ActivityRegistered: (activity, event) => activity,
/**
* noop
*/
Pushed: (activity, event) => activity,
/**
* Change transition state to exit-done
*/
Replaced: (activity, event) => __spreadProps(__spreadValues({}, activity), {
exitedBy: event,
transitionState: "exit-done"
}),
/**
* Change transition state to exit-done or exit-active depending on skipExitActiveState
*/
Popped: (activity, event) => {
const transitionState = event.skipExitActiveState || isTransitionDone ? "exit-done" : "exit-active";
return __spreadProps(__spreadValues({}, activity), {
// src/activity-utils/makeReducer.ts
function makeReducer(reducerMap) {
return (target, event) => {
const reducer = reducerMap[event.name].bind(reducerMap);
if (reducer) {
return reducer(target, event);
}
throw new Error(`No reducer for event ${JSON.stringify(event)}`);
};
}
// src/activity-utils/makeActivitiesReducer.ts
function noop(activities) {
return activities;
}
function makeActivitiesReducer({
transitionDuration,
now,
resumedAt
}) {
return makeReducer({
/**
* Push new activity to activities
*/
Pushed(activities, event) {
const isTransitionDone = now - (resumedAt != null ? resumedAt : event.eventDate) >= transitionDuration;
const transitionState = event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";
const reservedIndex = findNewActivityIndex(activities, event);
return [
...activities.slice(0, reservedIndex),
makeActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1)
];
},
/**
* Replace activity at reservedIndex with new activity
*/
Replaced(activities, event) {
var _a, _b;
const isTransitionDone = now - (resumedAt != null ? resumedAt : event.eventDate) >= transitionDuration;
const reservedIndex = findNewActivityIndex(activities, event);
const transitionState = (_b = (_a = activities[reservedIndex]) == null ? void 0 : _a.transitionState) != null ? _b : event.skipEnterActiveState || isTransitionDone ? "enter-done" : "enter-active";
return [
...activities.slice(0, reservedIndex),
makeActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1)
];
},
/**
* noop
*/
Initialized: noop,
ActivityRegistered: noop,
Popped: noop,
StepPushed: noop,
StepReplaced: noop,
StepPopped: noop,
Paused: noop,
Resumed: noop
});
}
// src/activity-utils/makeActivityReducer.ts
function noop2(activity) {
return activity;
}
function makeActivityReducer(context) {
return makeReducer({
/**
* Change transition state to exit-done
*/
Replaced: (activity, event) => __spreadProps(__spreadValues({}, activity), {
exitedBy: event,
transitionState,
params: transitionState === "exit-done" ? activity.steps[0].params : activity.params,
steps: transitionState === "exit-done" ? [activity.steps[0]] : activity.steps
transitionState: "exit-done"
}),
/**
* Change transition state to exit-done or exit-active depending on skipExitActiveState
*/
Popped: (activity, event) => {
var _a;
const isTransitionDone = context.now - ((_a = context.resumedAt) != null ? _a : event.eventDate) >= context.transitionDuration;
const transitionState = event.skipExitActiveState || isTransitionDone ? "exit-done" : "exit-active";
return __spreadProps(__spreadValues({}, activity), {
exitedBy: event,
transitionState,
params: transitionState === "exit-done" ? activity.steps[0].params : activity.params,
steps: transitionState === "exit-done" ? [activity.steps[0]] : activity.steps
});
},
/**
* Replace step params
* Push new step
*/
StepPushed: (activity, event) => {
var _a;
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event,
zIndex: activity.zIndex,
hasZIndex: (_a = event.hasZIndex) != null ? _a : false
};
return __spreadProps(__spreadValues({}, activity), {
params: event.stepParams,
steps: [...activity.steps, newRoute]
});
},
/**
* Replace step params
* Replace the last step
*/
StepReplaced: (activity, event) => {
var _a;
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event,
zIndex: activity.zIndex,
hasZIndex: (_a = event.hasZIndex) != null ? _a : false
};
return __spreadProps(__spreadValues({}, activity), {
params: event.stepParams,
steps: [
...activity.steps.slice(0, activity.steps.length - 1),
newRoute
]
});
},
/**
* Pop the last step
* If there are params in the previous step, set them as the new params
*/
StepPopped: (activity, event) => {
var _a;
activity.steps.pop();
const beforeActivityParams = (_a = last(activity.steps)) == null ? void 0 : _a.params;
return __spreadProps(__spreadValues({}, activity), {
params: beforeActivityParams != null ? beforeActivityParams : activity.params
});
},
/**
* noop
*/
Initialized: noop2,
ActivityRegistered: noop2,
Pushed: noop2,
Paused: noop2,
Resumed: noop2
});
}
// src/activity-utils/makeStackReducer.ts
function withPauseReducer(reducer) {
return (stack, event) => {
if (stack.globalTransitionState === "paused") {
return __spreadProps(__spreadValues({}, stack), {
pausedEvents: stack.pausedEvents ? [...stack.pausedEvents, event] : [event]
});
}
return reducer(stack, event);
};
}
function withActivitiesReducer(reducer, context) {
return (stack, event) => {
const activitiesReducer = makeActivitiesReducer({
transitionDuration: stack.transitionDuration,
now: context.now,
resumedAt: context.resumedAt
});
},
/**
* Replace step params
* Push new step
*/
StepPushed: (activity, event) => {
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event
};
return __spreadProps(__spreadValues({}, activity), {
params: event.stepParams,
steps: [...activity.steps, newRoute]
const activityReducer = makeActivityReducer({
transitionDuration: stack.transitionDuration,
now: context.now,
resumedAt: context.resumedAt
});
},
/**
* Replace step params
* Replace the last step
*/
StepReplaced: (activity, event) => {
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event
};
return __spreadProps(__spreadValues({}, activity), {
params: event.stepParams,
steps: [
...activity.steps.slice(0, activity.steps.length - 1),
newRoute
]
});
},
/**
* Pop the last step
* If there are params in the previous step, set them as the new params
*/
StepPopped: (activity, event) => {
var _a;
activity.steps.pop();
const beforeActivityParams = (_a = last(activity.steps)) == null ? void 0 : _a.params;
return __spreadProps(__spreadValues({}, activity), {
params: beforeActivityParams != null ? beforeActivityParams : activity.params
});
}
});
const activities = activitiesReducer(stack.activities, event);
const targetActivityIndices = findTargetActivityIndices(
stack.activities,
event,
{ transitionDuration: stack.transitionDuration, now: context.now }
);
for (const targetActivityIndex of targetActivityIndices) {
activities[targetActivityIndex] = activityReducer(
activities[targetActivityIndex],
event
);
}
const isLoading = activities.find(
(activity) => activity.transitionState === "enter-active" || activity.transitionState === "exit-active"
);
const globalTransitionState = stack.globalTransitionState === "paused" ? "paused" : isLoading ? "loading" : "idle";
return reducer(__spreadProps(__spreadValues({}, stack), { activities, globalTransitionState }), event);
};
}
function noop3(stack) {
return stack;
}
function makeStackReducer(context) {
return makeReducer({
Initialized: withPauseReducer(
withActivitiesReducer((stack, event) => {
return __spreadProps(__spreadValues({}, stack), {
transitionDuration: event.transitionDuration
});
}, context)
),
ActivityRegistered: withPauseReducer(
withActivitiesReducer(
(stack, event) => {
return __spreadProps(__spreadValues({}, stack), {
registeredActivities: [
...stack.registeredActivities,
__spreadValues({
name: event.activityName
}, event.activityParamsSchema ? {
paramsSchema: event.activityParamsSchema
} : null)
]
});
},
context
)
),
Paused: withPauseReducer(
withActivitiesReducer((stack, event) => {
return __spreadProps(__spreadValues({}, stack), {
globalTransitionState: "paused"
});
}, context)
),
Resumed: withActivitiesReducer(
(stack, event) => {
if (stack.globalTransitionState !== "paused" || !stack.pausedEvents) {
return stack;
}
const reducer = makeStackReducer({
now: context.now,
resumedAt: event.eventDate
});
const _a = stack, { pausedEvents } = _a, rest = __objRest(_a, ["pausedEvents"]);
return pausedEvents.reduce(reducer, __spreadProps(__spreadValues({}, rest), {
globalTransitionState: "idle"
}));
},
context
),
Pushed: withPauseReducer(withActivitiesReducer(noop3, context)),
Replaced: withPauseReducer(withActivitiesReducer(noop3, context)),
Popped: withPauseReducer(withActivitiesReducer(noop3, context)),
StepPushed: withPauseReducer(withActivitiesReducer(noop3, context)),
StepReplaced: withPauseReducer(withActivitiesReducer(noop3, context)),
StepPopped: withPauseReducer(withActivitiesReducer(noop3, context))
});
}

@@ -399,34 +524,16 @@ // src/event-utils/filterEvents.ts

// src/aggregate.ts
function aggregate(events, now) {
const sortedEvents = uniqBy(
[...events].sort((a, b) => compareBy(a, b, (e) => e.id)),
function aggregate(inputEvents, now) {
const events = uniqBy(
[...inputEvents].sort((a, b) => compareBy(a, b, (e) => e.id)),
(e) => e.id
);
validateEvents(sortedEvents);
const initEvent = filterEvents(sortedEvents, "Initialized")[0];
const activityRegisteredEvents = filterEvents(events, "ActivityRegistered");
const { transitionDuration } = initEvent;
const activities = sortedEvents.reduce(
(activities2, event) => {
const isTransitionDone = now - event.eventDate >= transitionDuration;
const targets = findTargetActivityIndexes(
activities2,
event,
isTransitionDone
);
const activityReducer = makeActivityReducers(isTransitionDone);
const activitiesReducer = makeActivitiesReducers(isTransitionDone);
const newActivities = activitiesReducer(activities2, event);
targets.forEach((targetIdx) => {
newActivities[targetIdx] = activityReducer(
newActivities[targetIdx],
event
);
});
return newActivities;
},
[]
);
const uniqActivities = uniqBy(activities, (activity) => activity.id);
const visibleActivities = uniqActivities.filter(
validateEvents(events);
const stackReducer = makeStackReducer({ now });
const stack = events.reduce(stackReducer, {
activities: [],
globalTransitionState: "idle",
registeredActivities: [],
transitionDuration: 0
});
const visibleActivities = stack.activities.filter(
(activity) => activity.transitionState === "enter-active" || activity.transitionState === "enter-done" || activity.transitionState === "exit-active"

@@ -439,11 +546,27 @@ );

const lastEnteredActivity = enteredActivities[enteredActivities.length - 1];
const globalTransitionState = activities.find(
(activity) => activity.transitionState === "enter-active" || activity.transitionState === "exit-active"
) ? "loading" : "idle";
const output = {
activities: uniqActivities.map((activity) => {
const zIndex = visibleActivities.findIndex(
const output = __spreadProps(__spreadValues({}, stack), {
activities: stack.activities.map((activity) => {
let zIndex = visibleActivities.findIndex(
({ id: id2 }) => id2 === activity.id
);
return __spreadValues(__spreadProps(__spreadValues({
const beforeActivities = visibleActivities.slice(0, zIndex);
for (const beforeActivity of beforeActivities) {
for (const step of beforeActivity.steps) {
if (step.hasZIndex) {
zIndex += 1;
}
}
}
const steps = activity.steps.reduce((acc, step) => {
var _a;
const lastStep = last(acc);
const lastStepZIndex = (_a = lastStep == null ? void 0 : lastStep.zIndex) != null ? _a : zIndex;
return [
...acc,
__spreadProps(__spreadValues({}, step), {
zIndex: lastStepZIndex + (step.hasZIndex ? 1 : 0)
})
];
}, []);
return __spreadValues(__spreadValues({
id: activity.id,

@@ -453,23 +576,15 @@ name: activity.name,

params: activity.params,
steps: activity.steps,
enteredBy: activity.enteredBy
steps,
enteredBy: activity.enteredBy,
zIndex,
isTop: (lastVisibleActivity == null ? void 0 : lastVisibleActivity.id) === activity.id,
isActive: (lastEnteredActivity == null ? void 0 : lastEnteredActivity.id) === activity.id,
isRoot: zIndex === 0 || zIndex === 1 && activity.transitionState === "enter-active" && activity.enteredBy.name === "Replaced"
}, activity.exitedBy ? {
exitedBy: activity.exitedBy
} : null), {
isTop: (lastVisibleActivity == null ? void 0 : lastVisibleActivity.id) === activity.id,
isActive: (lastEnteredActivity == null ? void 0 : lastEnteredActivity.id) === activity.id,
isRoot: zIndex === 0 || zIndex === 1 && activity.transitionState === "enter-active" && activity.enteredBy.name === "Replaced",
zIndex
}), activity.context ? {
} : null), activity.context ? {
context: activity.context
} : null);
}).sort((a, b) => compareBy(a, b, (activity) => activity.id)),
registeredActivities: activityRegisteredEvents.map((event) => __spreadValues({
name: event.activityName
}, event.activityParamsSchema ? {
paramsSchema: event.activityParamsSchema
} : null)),
transitionDuration,
globalTransitionState
};
}).sort((a, b) => compareBy(a, b, (activity) => activity.id))
});
return output;

@@ -492,2 +607,14 @@ }

}
const isPaused = prevOutput.globalTransitionState !== "paused" && nextOutput.globalTransitionState === "paused";
const isResumed = prevOutput.globalTransitionState === "paused" && nextOutput.globalTransitionState !== "paused";
if (isPaused) {
output.push({
_TAG: "PAUSED"
});
}
if (isResumed) {
output.push({
_TAG: "RESUMED"
});
}
for (let i = 0; i < Math.max(prevOutput.activities.length, nextOutput.activities.length); i += 1) {

@@ -571,2 +698,182 @@ const prevActivity = prevOutput.activities[i];

// src/utils/triggerPreEffectHooks.ts
var PLUGIN_HOOK_MAP = {
Pushed: "onBeforePush",
Replaced: "onBeforeReplace",
Popped: "onBeforePop",
StepPushed: "onBeforeStepPush",
StepReplaced: "onBeforeStepReplace",
StepPopped: "onBeforeStepPop",
Paused: "onBeforePause",
Resumed: "onBeforeResume"
};
function triggerPreEffectHook(actionName, actionParams, pluginInstances, actions) {
let isPrevented = false;
let nextActionParams = __spreadValues({}, actionParams);
for (const pluginInstance of pluginInstances) {
const hook = pluginInstance[PLUGIN_HOOK_MAP[actionName]];
if (hook) {
hook({
actionParams: __spreadValues({}, nextActionParams),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault: () => {
isPrevented = true;
},
overrideActionParams: (partialActionParams) => {
nextActionParams = __spreadValues(__spreadValues({}, nextActionParams), partialActionParams);
}
})
});
}
}
return {
isPrevented,
nextActionParams
};
}
// src/utils/makeActions.ts
function makeActions({
dispatchEvent,
pluginInstances,
actions
}) {
return {
push(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Pushed",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Pushed", nextActionParams);
},
replace(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Replaced",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Replaced", nextActionParams);
},
pop(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Popped",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Popped", nextActionParams);
},
stepPush(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"StepPushed",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("StepPushed", nextActionParams);
},
stepReplace(params) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"StepReplaced",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("StepReplaced", nextActionParams);
},
stepPop(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"StepPopped",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("StepPopped", nextActionParams);
},
pause(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Paused",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Paused", nextActionParams);
},
resume(params = {}) {
const { isPrevented, nextActionParams } = triggerPreEffectHook(
"Resumed",
params,
pluginInstances,
actions
);
if (isPrevented) {
return;
}
dispatchEvent("Resumed", nextActionParams);
}
};
}
// src/utils/triggerPostEffectHooks.ts
function triggerPostEffectHooks(effects, plugins, actions) {
effects.forEach((effect) => {
plugins.forEach((plugin) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
switch (effect._TAG) {
case "PUSHED":
(_a = plugin.onPushed) == null ? void 0 : _a.call(plugin, { actions, effect });
break;
case "REPLACED":
(_b = plugin.onReplaced) == null ? void 0 : _b.call(plugin, { actions, effect });
break;
case "POPPED":
(_c = plugin.onPopped) == null ? void 0 : _c.call(plugin, { actions, effect });
break;
case "STEP_PUSHED":
(_d = plugin.onStepPushed) == null ? void 0 : _d.call(plugin, { actions, effect });
break;
case "STEP_REPLACED":
(_e = plugin.onStepReplaced) == null ? void 0 : _e.call(plugin, { actions, effect });
break;
case "STEP_POPPED":
(_f = plugin.onStepPopped) == null ? void 0 : _f.call(plugin, { actions, effect });
break;
case "PAUSED":
(_g = plugin.onPaused) == null ? void 0 : _g.call(plugin, { actions, effect });
break;
case "RESUMED":
(_h = plugin.onResumed) == null ? void 0 : _h.call(plugin, { actions, effect });
break;
case "%SOMETHING_CHANGED%":
(_i = plugin.onChanged) == null ? void 0 : _i.call(plugin, { actions, effect });
break;
}
});
});
}
// src/makeCoreStore.ts

@@ -615,157 +922,2 @@ var SECOND = 1e3;

};
const setStackValue = (nextStackValue) => {
const effects = produceEffects(stack.value, nextStackValue);
stack.value = nextStackValue;
triggerPostEffectHooks(effects, pluginInstances);
};
const dispatchEvent = (name, params) => {
const newEvent = makeEvent(name, params);
const nextStackValue = aggregate(
[...events.value, newEvent],
(/* @__PURE__ */ new Date()).getTime()
);
events.value.push(newEvent);
setStackValue(nextStackValue);
const interval = setInterval(() => {
const nextStackValue2 = aggregate(events.value, (/* @__PURE__ */ new Date()).getTime());
if (!isEqual2(stack.value, nextStackValue2)) {
setStackValue(nextStackValue2);
}
if (nextStackValue2.globalTransitionState === "idle") {
clearInterval(interval);
}
}, INTERVAL_MS);
};
function triggerPreEffectHooks(event, plugins) {
let isPrevented = false;
let nextEvent = __spreadValues({}, event);
function toParams(event2) {
const params = __spreadValues({}, event2);
params.name = void 0;
return params;
}
const preventDefault = () => {
isPrevented = true;
};
const overrideActionParams = (nextActionParams) => {
nextEvent = __spreadValues(__spreadValues({}, nextEvent), nextActionParams);
};
plugins.forEach((plugin) => {
var _a2, _b2, _c2, _d2, _e, _f;
switch (nextEvent.name) {
case "Pushed": {
(_a2 = plugin.onBeforePush) == null ? void 0 : _a2.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "Replaced": {
(_b2 = plugin.onBeforeReplace) == null ? void 0 : _b2.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "Popped": {
(_c2 = plugin.onBeforePop) == null ? void 0 : _c2.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "StepPushed": {
(_d2 = plugin.onBeforeStepPush) == null ? void 0 : _d2.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "StepReplaced": {
(_e = plugin.onBeforeStepReplace) == null ? void 0 : _e.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
case "StepPopped": {
(_f = plugin.onBeforeStepPop) == null ? void 0 : _f.call(plugin, {
actionParams: __spreadValues({}, nextEvent),
actions: __spreadProps(__spreadValues({}, actions), {
preventDefault,
overrideActionParams
})
});
break;
}
default:
break;
}
});
return {
isPrevented,
overriddenParams: toParams(nextEvent)
};
}
function triggerPostEffectHooks(effects, plugins) {
effects.forEach((effect) => {
plugins.forEach((plugin) => {
var _a2, _b2, _c2, _d2, _e, _f, _g;
switch (effect._TAG) {
case "PUSHED":
return (_a2 = plugin.onPushed) == null ? void 0 : _a2.call(plugin, {
actions,
effect
});
case "REPLACED":
return (_b2 = plugin.onReplaced) == null ? void 0 : _b2.call(plugin, {
actions,
effect
});
case "POPPED":
return (_c2 = plugin.onPopped) == null ? void 0 : _c2.call(plugin, {
actions,
effect
});
case "STEP_PUSHED":
return (_d2 = plugin.onStepPushed) == null ? void 0 : _d2.call(plugin, {
actions,
effect
});
case "STEP_REPLACED":
return (_e = plugin.onStepReplaced) == null ? void 0 : _e.call(plugin, {
actions,
effect
});
case "STEP_POPPED":
return (_f = plugin.onStepPopped) == null ? void 0 : _f.call(plugin, {
actions,
effect
});
case "%SOMETHING_CHANGED%":
return (_g = plugin.onChanged) == null ? void 0 : _g.call(plugin, {
actions,
effect
});
default:
return void 0;
}
});
});
}
const actions = {

@@ -775,64 +927,50 @@ getStack() {

},
dispatchEvent,
push(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("Pushed", params),
pluginInstances
dispatchEvent(name, params) {
const newEvent = makeEvent(name, params);
const nextStackValue = aggregate(
[...events.value, newEvent],
(/* @__PURE__ */ new Date()).getTime()
);
if (isPrevented) {
return;
}
dispatchEvent("Pushed", overriddenParams);
events.value.push(newEvent);
setStackValue(nextStackValue);
const interval = setInterval(() => {
const nextStackValue2 = aggregate(events.value, (/* @__PURE__ */ new Date()).getTime());
if (!isEqual2(stack.value, nextStackValue2)) {
setStackValue(nextStackValue2);
}
if (nextStackValue2.globalTransitionState === "idle") {
clearInterval(interval);
}
}, INTERVAL_MS);
},
replace(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("Replaced", params),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("Replaced", overriddenParams);
push: () => {
},
pop(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("Popped", params != null ? params : {}),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("Popped", overriddenParams);
replace: () => {
},
stepPush(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("StepPushed", params != null ? params : {}),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("StepPushed", overriddenParams);
pop: () => {
},
stepReplace(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("StepReplaced", params != null ? params : {}),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("StepReplaced", overriddenParams);
stepPush: () => {
},
stepPop(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("StepPopped", params != null ? params : {}),
pluginInstances
);
if (isPrevented) {
return;
}
dispatchEvent("StepPopped", overriddenParams);
stepReplace: () => {
},
stepPop: () => {
},
pause: () => {
},
resume: () => {
}
};
const setStackValue = (nextStackValue) => {
const effects = produceEffects(stack.value, nextStackValue);
stack.value = nextStackValue;
triggerPostEffectHooks(effects, pluginInstances, actions);
};
Object.assign(
actions,
makeActions({
dispatchEvent: actions.dispatchEvent,
pluginInstances,
actions
})
);
return {

@@ -839,0 +977,0 @@ actions,

import type { Stack } from "../Stack";
import type { PoppedEvent, PushedEvent, ReplacedEvent, StepPoppedEvent, StepPushedEvent, StepReplacedEvent } from "../event-types";
import type { PausedEvent, PoppedEvent, PushedEvent, ReplacedEvent, ResumedEvent, StepPoppedEvent, StepPushedEvent, StepReplacedEvent } from "../event-types";
import type { BaseDomainEvent } from "../event-types/_base";

@@ -38,3 +38,11 @@ import type { DispatchEvent } from "../event-utils";

stepPop: (params?: Omit<StepPoppedEvent, keyof BaseDomainEvent>) => void;
/**
* Pause stack change
*/
pause: (params?: Omit<PausedEvent, keyof BaseDomainEvent>) => void;
/**
* Resume paused stack
*/
resume: (params?: Omit<ResumedEvent, keyof BaseDomainEvent>) => void;
};
//# sourceMappingURL=StackflowActions.d.ts.map

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

import type { PoppedEvent, PushedEvent, ReplacedEvent, StepPoppedEvent, StepPushedEvent, StepReplacedEvent } from "../event-types";
import type { PausedEvent, PoppedEvent, PushedEvent, ReplacedEvent, ResumedEvent, StepPoppedEvent, StepPushedEvent, StepReplacedEvent } from "../event-types";
import type { BaseDomainEvent } from "../event-types/_base";

@@ -38,2 +38,10 @@ import type { StackflowPluginHook, StackflowPluginPostEffectHook, StackflowPluginPreEffectHook } from "./StackflowPluginHook";

/**
* Called before `PausedEvent` dispatched
*/
onBeforePause?: StackflowPluginPreEffectHook<Omit<PausedEvent, keyof BaseDomainEvent>>;
/**
* Called before `ResumedEvent` dispatched
*/
onBeforeResume?: StackflowPluginPreEffectHook<Omit<ResumedEvent, keyof BaseDomainEvent>>;
/**
* Called when the `push` procedure is complete and the actual rendering is finished

@@ -63,2 +71,10 @@ */

/**
* Called when stack paused
*/
onPaused?: StackflowPluginPostEffectHook<"PAUSED">;
/**
* Called when stack resumed
*/
onResumed?: StackflowPluginPostEffectHook<"RESUMED">;
/**
* Called after any changes to the stack state are reflected in the actual rendering

@@ -65,0 +81,0 @@ */

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

import type { PoppedEvent, PushedEvent, ReplacedEvent, StepPoppedEvent, StepPushedEvent, StepReplacedEvent } from "./event-types";
import type { DomainEvent, PoppedEvent, PushedEvent, ReplacedEvent, StepPoppedEvent, StepPushedEvent, StepReplacedEvent } from "./event-types";
export type ActivityTransition = "enter" | "exit";

@@ -12,2 +12,4 @@ export type ActivityTransitionProgress = "active" | "done";

exitedBy?: ReplacedEvent | PoppedEvent | StepReplacedEvent | StepPoppedEvent;
zIndex: number;
hasZIndex?: boolean;
};

@@ -38,4 +40,5 @@ export type Activity = {

transitionDuration: number;
globalTransitionState: "idle" | "loading";
globalTransitionState: "idle" | "loading" | "paused";
pausedEvents?: DomainEvent[];
};
//# sourceMappingURL=Stack.d.ts.map
{
"name": "@stackflow/core",
"version": "1.1.1",
"version": "1.2.0",
"repository": {

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

@@ -5,9 +5,8 @@ import type { Activity } from "../Stack";

export default function findNewActivityIndex(
export function findNewActivityIndex(
activities: Activity[],
event: PushedEvent | ReplacedEvent,
activities: Activity[],
) {
switch (event.name) {
case "Pushed":
return activities.length;
case "Replaced": {

@@ -14,0 +13,0 @@ const alreadyExistingActivityIndex = last(

@@ -1,52 +0,36 @@

import type { Activity, Stack } from "./Stack";
import findTargetActivityIndexes from "./activity-utils/findTargetActivityIndexes";
import { makeActivitiesReducers } from "./activity-utils/makeActivitiesReducers";
import { makeActivityReducers } from "./activity-utils/makeActivityReducers";
import type { ActivityStep, Stack } from "./Stack";
import { makeStackReducer } from "./activity-utils/makeStackReducer";
import type { DomainEvent } from "./event-types";
import { filterEvents, validateEvents } from "./event-utils";
import { compareBy, uniqBy } from "./utils";
import { validateEvents } from "./event-utils";
import { compareBy, last, uniqBy } from "./utils";
export function aggregate(events: DomainEvent[], now: number): Stack {
const sortedEvents = uniqBy(
[...events].sort((a, b) => compareBy(a, b, (e) => e.id)),
export function aggregate(inputEvents: DomainEvent[], now: number): Stack {
/**
* 1. Pre-process
*/
const events = uniqBy(
[...inputEvents].sort((a, b) => compareBy(a, b, (e) => e.id)),
(e) => e.id,
);
validateEvents(sortedEvents);
/**
* 2. Validate events
*/
validateEvents(events);
const initEvent = filterEvents(sortedEvents, "Initialized")[0];
const activityRegisteredEvents = filterEvents(events, "ActivityRegistered");
const { transitionDuration } = initEvent;
/**
* 3. Run reducer
*/
const stackReducer = makeStackReducer({ now });
const stack = events.reduce(stackReducer, {
activities: [],
globalTransitionState: "idle",
registeredActivities: [],
transitionDuration: 0,
});
const activities = sortedEvents.reduce(
(activities: Activity[], event: DomainEvent) => {
const isTransitionDone = now - event.eventDate >= transitionDuration;
const targets = findTargetActivityIndexes(
activities,
event,
isTransitionDone,
);
const activityReducer = makeActivityReducers(isTransitionDone);
const activitiesReducer = makeActivitiesReducers(isTransitionDone);
const newActivities = activitiesReducer(activities, event);
targets.forEach((targetIdx) => {
newActivities[targetIdx] = activityReducer(
newActivities[targetIdx],
event,
);
});
return newActivities;
},
[],
);
const uniqActivities = uniqBy(activities, (activity) => activity.id);
const visibleActivities = uniqActivities.filter(
/**
* 4. Post-process
*/
const visibleActivities = stack.activities.filter(
(activity) =>

@@ -66,17 +50,33 @@ activity.transitionState === "enter-active" ||

const globalTransitionState = activities.find(
(activity) =>
activity.transitionState === "enter-active" ||
activity.transitionState === "exit-active",
)
? "loading"
: "idle";
const output: Stack = {
activities: uniqActivities
...stack,
activities: stack.activities
.map((activity) => {
const zIndex = visibleActivities.findIndex(
let zIndex = visibleActivities.findIndex(
({ id }) => id === activity.id,
);
const beforeActivities = visibleActivities.slice(0, zIndex);
for (const beforeActivity of beforeActivities) {
for (const step of beforeActivity.steps) {
if (step.hasZIndex) {
zIndex += 1;
}
}
}
const steps = activity.steps.reduce<ActivityStep[]>((acc, step) => {
const lastStep = last(acc);
const lastStepZIndex = lastStep?.zIndex ?? zIndex;
return [
...acc,
{
...step,
zIndex: lastStepZIndex + (step.hasZIndex ? 1 : 0),
},
];
}, []);
return {

@@ -87,9 +87,5 @@ id: activity.id,

params: activity.params,
steps: activity.steps,
steps,
enteredBy: activity.enteredBy,
...(activity.exitedBy
? {
exitedBy: activity.exitedBy,
}
: null),
zIndex,
isTop: lastVisibleActivity?.id === activity.id,

@@ -102,3 +98,7 @@ isActive: lastEnteredActivity?.id === activity.id,

activity.enteredBy.name === "Replaced"),
zIndex,
...(activity.exitedBy
? {
exitedBy: activity.exitedBy,
}
: null),
...(activity.context

@@ -112,12 +112,2 @@ ? {

.sort((a, b) => compareBy(a, b, (activity) => activity.id)),
registeredActivities: activityRegisteredEvents.map((event) => ({
name: event.activityName,
...(event.activityParamsSchema
? {
paramsSchema: event.activityParamsSchema,
}
: null),
})),
transitionDuration,
globalTransitionState,
};

@@ -124,0 +114,0 @@

@@ -32,2 +32,8 @@ import type { Activity, ActivityStep } from "./Stack";

activity: Activity;
}
| {
_TAG: "PAUSED";
}
| {
_TAG: "RESUMED";
};
import type { ActivityRegisteredEvent } from "./ActivityRegisteredEvent";
import type { InitializedEvent } from "./InitializedEvent";
import type { PausedEvent } from "./PausedEvent";
import type { PoppedEvent } from "./PoppedEvent";
import type { PushedEvent } from "./PushedEvent";
import type { ReplacedEvent } from "./ReplacedEvent";
import type { ResumedEvent } from "./ResumedEvent";
import type { StepPoppedEvent } from "./StepPoppedEvent";

@@ -18,3 +20,5 @@ import type { StepPushedEvent } from "./StepPushedEvent";

| PushedEvent
| ReplacedEvent;
| ReplacedEvent
| PausedEvent
| ResumedEvent;

@@ -29,1 +33,3 @@ export * from "./ActivityRegisteredEvent";

export * from "./StepReplacedEvent";
export * from "./PausedEvent";
export * from "./ResumedEvent";

@@ -11,3 +11,4 @@ import type { BaseDomainEvent } from "./_base";

targetActivityId?: string;
hasZIndex?: boolean;
}
>;

@@ -11,3 +11,4 @@ import type { BaseDomainEvent } from "./_base";

targetActivityId?: string;
hasZIndex?: boolean;
}
>;
import type { Stack } from "../Stack";
import type {
PausedEvent,
PoppedEvent,
PushedEvent,
ReplacedEvent,
ResumedEvent,
StepPoppedEvent,

@@ -53,2 +55,12 @@ StepPushedEvent,

stepPop: (params?: Omit<StepPoppedEvent, keyof BaseDomainEvent>) => void;
/**
* Pause stack change
*/
pause: (params?: Omit<PausedEvent, keyof BaseDomainEvent>) => void;
/**
* Resume paused stack
*/
resume: (params?: Omit<ResumedEvent, keyof BaseDomainEvent>) => void;
};
import type {
PausedEvent,
PoppedEvent,
PushedEvent,
ReplacedEvent,
ResumedEvent,
StepPoppedEvent,

@@ -70,2 +72,16 @@ StepPushedEvent,

/**
* Called before `PausedEvent` dispatched
*/
onBeforePause?: StackflowPluginPreEffectHook<
Omit<PausedEvent, keyof BaseDomainEvent>
>;
/**
* Called before `ResumedEvent` dispatched
*/
onBeforeResume?: StackflowPluginPreEffectHook<
Omit<ResumedEvent, keyof BaseDomainEvent>
>;
/**
* Called when the `push` procedure is complete and the actual rendering is finished

@@ -101,2 +117,12 @@ */

/**
* Called when stack paused
*/
onPaused?: StackflowPluginPostEffectHook<"PAUSED">;
/**
* Called when stack resumed
*/
onResumed?: StackflowPluginPostEffectHook<"RESUMED">;
/**
* Called after any changes to the stack state are reflected in the actual rendering

@@ -103,0 +129,0 @@ */

import isEqual from "react-fast-compare";
import type { Effect } from "./Effect";
import type { Stack } from "./Stack";
import { aggregate } from "./aggregate";
import type { DomainEvent, PushedEvent, StepPushedEvent } from "./event-types";
import type { BaseDomainEvent } from "./event-types/_base";
import { makeEvent } from "./event-utils";

@@ -12,2 +10,4 @@ import type { StackflowActions, StackflowPlugin } from "./interfaces";

import { divideBy, once } from "./utils";
import { makeActions } from "./utils/makeActions";
import { triggerPostEffectHooks } from "./utils/triggerPostEffectHooks";

@@ -81,5 +81,3 @@ const SECOND = 1000;

const events: {
value: DomainEvent[];
} = {
const events: { value: DomainEvent[] } = {
value: [...initialRemainingEvents, ...initialPushedEvents],

@@ -92,207 +90,2 @@ };

const setStackValue = (nextStackValue: Stack) => {
const effects = produceEffects(stack.value, nextStackValue);
stack.value = nextStackValue;
triggerPostEffectHooks(effects, pluginInstances);
};
const dispatchEvent: StackflowActions["dispatchEvent"] = (name, params) => {
const newEvent = makeEvent(name, params);
const nextStackValue = aggregate(
[...events.value, newEvent],
new Date().getTime(),
);
events.value.push(newEvent);
setStackValue(nextStackValue);
const interval = setInterval(() => {
const nextStackValue = aggregate(events.value, new Date().getTime());
if (!isEqual(stack.value, nextStackValue)) {
setStackValue(nextStackValue);
}
if (nextStackValue.globalTransitionState === "idle") {
clearInterval(interval);
}
}, INTERVAL_MS);
};
function triggerPreEffectHooks<T extends DomainEvent>(
event: T,
plugins: ReturnType<StackflowPlugin>[],
): {
isPrevented: boolean;
overriddenParams: Omit<T, keyof BaseDomainEvent>;
} {
let isPrevented = false;
let nextEvent: T = {
...event,
};
function toParams(event: T): Omit<T, keyof BaseDomainEvent> {
const params: Partial<BaseDomainEvent> & Omit<T, keyof BaseDomainEvent> =
{ ...event };
// delete params.id;
// delete params.eventDate;
params.name = undefined;
return params;
}
const preventDefault = () => {
isPrevented = true;
};
const overrideActionParams = (nextActionParams: any) => {
nextEvent = {
...nextEvent,
...nextActionParams,
};
};
plugins.forEach((plugin) => {
switch (nextEvent.name) {
case "Pushed": {
plugin.onBeforePush?.({
actionParams: {
...nextEvent,
},
actions: {
...actions,
preventDefault,
overrideActionParams,
},
});
break;
}
case "Replaced": {
plugin.onBeforeReplace?.({
actionParams: {
...nextEvent,
},
actions: {
...actions,
preventDefault,
overrideActionParams,
},
});
break;
}
case "Popped": {
plugin.onBeforePop?.({
actionParams: {
...nextEvent,
},
actions: {
...actions,
preventDefault,
overrideActionParams,
},
});
break;
}
case "StepPushed": {
plugin.onBeforeStepPush?.({
actionParams: {
...nextEvent,
},
actions: {
...actions,
preventDefault,
overrideActionParams,
},
});
break;
}
case "StepReplaced": {
plugin.onBeforeStepReplace?.({
actionParams: {
...nextEvent,
},
actions: {
...actions,
preventDefault,
overrideActionParams,
},
});
break;
}
case "StepPopped": {
plugin.onBeforeStepPop?.({
actionParams: {
...nextEvent,
},
actions: {
...actions,
preventDefault,
overrideActionParams,
},
});
break;
}
default:
break;
}
});
return {
isPrevented,
overriddenParams: toParams(nextEvent),
};
}
function triggerPostEffectHooks(
effects: Effect[],
plugins: ReturnType<StackflowPlugin>[],
) {
effects.forEach((effect) => {
plugins.forEach((plugin) => {
switch (effect._TAG) {
case "PUSHED":
return plugin.onPushed?.({
actions,
effect,
});
case "REPLACED":
return plugin.onReplaced?.({
actions,
effect,
});
case "POPPED":
return plugin.onPopped?.({
actions,
effect,
});
case "STEP_PUSHED":
return plugin.onStepPushed?.({
actions,
effect,
});
case "STEP_REPLACED":
return plugin.onStepReplaced?.({
actions,
effect,
});
case "STEP_POPPED":
return plugin.onStepPopped?.({
actions,
effect,
});
case "%SOMETHING_CHANGED%":
return plugin.onChanged?.({
actions,
effect,
});
default:
return undefined;
}
});
});
}
const actions: StackflowActions = {

@@ -302,77 +95,50 @@ getStack() {

},
dispatchEvent,
push(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("Pushed", params),
pluginInstances,
dispatchEvent(name, params) {
const newEvent = makeEvent(name, params);
const nextStackValue = aggregate(
[...events.value, newEvent],
new Date().getTime(),
);
if (isPrevented) {
return;
}
events.value.push(newEvent);
setStackValue(nextStackValue);
dispatchEvent("Pushed", overriddenParams);
},
replace(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("Replaced", params),
pluginInstances,
);
const interval = setInterval(() => {
const nextStackValue = aggregate(events.value, new Date().getTime());
if (isPrevented) {
return;
}
if (!isEqual(stack.value, nextStackValue)) {
setStackValue(nextStackValue);
}
dispatchEvent("Replaced", overriddenParams);
if (nextStackValue.globalTransitionState === "idle") {
clearInterval(interval);
}
}, INTERVAL_MS);
},
pop(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("Popped", params ?? {}),
pluginInstances,
);
push: () => {},
replace: () => {},
pop: () => {},
stepPush: () => {},
stepReplace: () => {},
stepPop: () => {},
pause: () => {},
resume: () => {},
};
if (isPrevented) {
return;
}
const setStackValue = (nextStackValue: Stack) => {
const effects = produceEffects(stack.value, nextStackValue);
stack.value = nextStackValue;
triggerPostEffectHooks(effects, pluginInstances, actions);
};
dispatchEvent("Popped", overriddenParams);
},
stepPush(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("StepPushed", params ?? {}),
pluginInstances,
);
// Initialize action methods after actions object is fully created
Object.assign(
actions,
makeActions({
dispatchEvent: actions.dispatchEvent,
pluginInstances,
actions,
}),
);
if (isPrevented) {
return;
}
dispatchEvent("StepPushed", overriddenParams);
},
stepReplace(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("StepReplaced", params ?? {}),
pluginInstances,
);
if (isPrevented) {
return;
}
dispatchEvent("StepReplaced", overriddenParams);
},
stepPop(params) {
const { isPrevented, overriddenParams } = triggerPreEffectHooks(
makeEvent("StepPopped", params ?? {}),
pluginInstances,
);
if (isPrevented) {
return;
}
dispatchEvent("StepPopped", overriddenParams);
},
};
return {

@@ -379,0 +145,0 @@ actions,

@@ -23,2 +23,3 @@ import { produceEffects } from "./produceEffects";

} as any,
zIndex: 0,
},

@@ -55,2 +56,3 @@ ],

} as any,
zIndex: 0,
},

@@ -99,2 +101,3 @@ ],

} as any,
zIndex: 0,
},

@@ -134,2 +137,3 @@ ],

} as any,
zIndex: 0,
},

@@ -172,2 +176,3 @@ ],

} as any,
zIndex: 0,
},

@@ -195,2 +200,3 @@ ],

} as any,
zIndex: 1,
},

@@ -230,2 +236,3 @@ ],

} as any,
zIndex: 0,
},

@@ -256,2 +263,3 @@ ],

} as any,
zIndex: 1,
},

@@ -288,2 +296,3 @@ ],

} as any,
zIndex: 0,
},

@@ -311,2 +320,3 @@ ],

} as any,
zIndex: 1,
},

@@ -341,2 +351,3 @@ ],

} as any,
zIndex: 0,
},

@@ -364,2 +375,3 @@ ],

} as any,
zIndex: 1,
},

@@ -399,2 +411,3 @@ ],

} as any,
zIndex: 1,
},

@@ -431,2 +444,3 @@ ],

} as any,
zIndex: 0,
},

@@ -454,2 +468,3 @@ ],

} as any,
zIndex: 1,
},

@@ -484,2 +499,3 @@ ],

} as any,
zIndex: 0,
},

@@ -507,2 +523,3 @@ ],

} as any,
zIndex: 1,
},

@@ -542,2 +559,3 @@ ],

} as any,
zIndex: 1,
},

@@ -568,2 +586,3 @@ ],

} as any,
zIndex: 0,
},

@@ -600,2 +619,3 @@ ],

} as any,
zIndex: 0,
},

@@ -623,2 +643,3 @@ ],

} as any,
zIndex: -1,
},

@@ -653,2 +674,3 @@ ],

} as any,
zIndex: 0,
},

@@ -676,2 +698,3 @@ ],

} as any,
zIndex: 1,
},

@@ -711,2 +734,3 @@ ],

} as any,
zIndex: 1,
},

@@ -743,2 +767,3 @@ ],

} as any,
zIndex: 0,
},

@@ -766,2 +791,3 @@ ],

} as any,
zIndex: -1,
},

@@ -796,2 +822,3 @@ ],

} as any,
zIndex: 0,
},

@@ -819,2 +846,3 @@ ],

} as any,
zIndex: -1,
},

@@ -842,2 +870,3 @@ ],

} as any,
zIndex: 1,
},

@@ -877,2 +906,3 @@ ],

} as any,
zIndex: 1,
},

@@ -909,2 +939,3 @@ ],

} as any,
zIndex: 0,
},

@@ -932,2 +963,3 @@ ],

} as any,
zIndex: 1,
},

@@ -962,2 +994,3 @@ ],

} as any,
zIndex: -1,
},

@@ -985,2 +1018,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1026,2 +1060,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1049,2 +1084,3 @@ ],

} as any,
zIndex: 1,
},

@@ -1079,2 +1115,3 @@ ],

} as any,
zIndex: -1,
},

@@ -1102,2 +1139,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1143,2 +1181,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1166,2 +1205,3 @@ ],

} as any,
zIndex: 1,
},

@@ -1196,2 +1236,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1219,2 +1260,3 @@ ],

} as any,
zIndex: 1,
},

@@ -1254,2 +1296,3 @@ ],

} as any,
zIndex: 1,
},

@@ -1286,2 +1329,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1319,2 +1363,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1327,2 +1372,3 @@ {

} as any,
zIndex: 0,
},

@@ -1335,2 +1381,3 @@ {

} as any,
zIndex: 0,
},

@@ -1370,2 +1417,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1378,2 +1426,3 @@ {

} as any,
zIndex: 0,
},

@@ -1386,2 +1435,3 @@ {

} as any,
zIndex: 0,
},

@@ -1400,2 +1450,3 @@ ],

} as any,
zIndex: 0,
} as any,

@@ -1420,2 +1471,3 @@ },

} as any,
zIndex: 0,
},

@@ -1428,2 +1480,3 @@ {

} as any,
zIndex: 0,
},

@@ -1436,2 +1489,3 @@ {

} as any,
zIndex: 0,
},

@@ -1450,2 +1504,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1481,2 +1536,3 @@ },

},
zIndex: -1,
},

@@ -1544,2 +1600,3 @@ ],

},
zIndex: 0,
},

@@ -1596,2 +1653,3 @@ ],

},
zIndex: -1,
},

@@ -1659,2 +1717,3 @@ ],

},
zIndex: 0,
},

@@ -1675,2 +1734,3 @@ {

},
zIndex: 0,
},

@@ -1739,2 +1799,3 @@ ],

},
zIndex: 0,
},

@@ -1755,2 +1816,3 @@ {

},
zIndex: 0,
},

@@ -1794,2 +1856,3 @@ ],

},
zIndex: 0,
},

@@ -1821,2 +1884,3 @@ },

} as any,
zIndex: 0,
},

@@ -1855,2 +1919,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1894,2 +1959,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1913,2 +1979,3 @@ ],

} as any,
zIndex: 0,
},

@@ -1939,2 +2006,3 @@ },

} as any,
zIndex: 0,
},

@@ -1947,2 +2015,3 @@ {

} as any,
zIndex: 0,
},

@@ -1955,2 +2024,3 @@ {

} as any,
zIndex: 0,
},

@@ -1982,2 +2052,3 @@ ],

} as any,
zIndex: 0,
},

@@ -2017,2 +2088,3 @@ ],

} as any,
zIndex: 0,
},

@@ -2043,2 +2115,3 @@ ],

} as any,
zIndex: 0,
},

@@ -2069,2 +2142,3 @@ ],

} as any,
zIndex: 0,
},

@@ -2083,1 +2157,101 @@ ],

});
test("produceEffects - Paused가 작동해, globalTransitionState가 paused로 변하면 PAUSED 이펙트가 일어납니다", () => {
expect(
produceEffects(
{
activities: [],
globalTransitionState: "idle",
registeredActivities: [],
transitionDuration: 270,
},
{
activities: [],
globalTransitionState: "paused",
registeredActivities: [],
transitionDuration: 270,
},
),
).toEqual([
{
_TAG: "%SOMETHING_CHANGED%",
},
{
_TAG: "PAUSED",
},
]);
expect(
produceEffects(
{
activities: [],
globalTransitionState: "loading",
registeredActivities: [],
transitionDuration: 270,
},
{
activities: [],
globalTransitionState: "paused",
registeredActivities: [],
transitionDuration: 270,
},
),
).toEqual([
{
_TAG: "%SOMETHING_CHANGED%",
},
{
_TAG: "PAUSED",
},
]);
});
test("produceEffects - Resumed가 작동해, globalTransitionState가 paused에서 idle|loading으로 변하면 RESUMED 이펙트가 일어납니다", () => {
expect(
produceEffects(
{
activities: [],
globalTransitionState: "paused",
registeredActivities: [],
transitionDuration: 270,
},
{
activities: [],
globalTransitionState: "idle",
registeredActivities: [],
transitionDuration: 270,
},
),
).toEqual([
{
_TAG: "%SOMETHING_CHANGED%",
},
{
_TAG: "RESUMED",
},
]);
expect(
produceEffects(
{
activities: [],
globalTransitionState: "paused",
registeredActivities: [],
transitionDuration: 270,
},
{
activities: [],
globalTransitionState: "loading",
registeredActivities: [],
transitionDuration: 270,
},
),
).toEqual([
{
_TAG: "%SOMETHING_CHANGED%",
},
{
_TAG: "RESUMED",
},
]);
});

@@ -18,2 +18,20 @@ import isEqual from "react-fast-compare";

const isPaused =
prevOutput.globalTransitionState !== "paused" &&
nextOutput.globalTransitionState === "paused";
const isResumed =
prevOutput.globalTransitionState === "paused" &&
nextOutput.globalTransitionState !== "paused";
if (isPaused) {
output.push({
_TAG: "PAUSED",
});
}
if (isResumed) {
output.push({
_TAG: "RESUMED",
});
}
for (

@@ -20,0 +38,0 @@ let i = 0;

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

import type { BaseDomainEvent } from "event-types/_base";
import type {
DomainEvent,
PoppedEvent,

@@ -22,2 +24,4 @@ PushedEvent,

exitedBy?: ReplacedEvent | PoppedEvent | StepReplacedEvent | StepPoppedEvent;
zIndex: number;
hasZIndex?: boolean;
};

@@ -51,3 +55,4 @@

transitionDuration: number;
globalTransitionState: "idle" | "loading";
globalTransitionState: "idle" | "loading" | "paused";
pausedEvents?: DomainEvent[];
};
import type { Activity, ActivityTransitionState } from "../Stack";
import type { PushedEvent, ReplacedEvent } from "../event-types";
export const createActivityFromEvent = (
event: PushedEvent | ReplacedEvent,
transitionState: ActivityTransitionState,
): Activity => ({
id: event.activityId,
name: event.activityName,
transitionState,
params: event.activityParams,
context: event.activityContext,
steps: [
{
id: event.activityId,
params: event.activityParams,
enteredBy: event,
},
],
enteredBy: event,
isTop: false,
isActive: false,
isRoot: false,
zIndex: -1,
});
import type { Activity, DomainEvent } from "..";
type Reducer<T> = (state: T, event: DomainEvent) => T;
export function createReducer<T>(
reducerMap: {
[key in DomainEvent["name"]]: (
state: T,
event: Extract<DomainEvent, { name: key }>,
) => T;
},
) {
return (activity: T, event: DomainEvent) => {
const reducer = reducerMap[event.name] as Reducer<T>;
if (reducer) {
return reducer(activity, event);
}
throw new Error(`No reducer for event ${JSON.stringify(event)}`);
};
}
import type { Activity, ActivityTransitionState } from "../Stack";
import type { DomainEvent } from "../event-types";
import { findIndices, last } from "../utils";
function isActivityNotExited(activity: Activity) {
return !activity.exitedBy;
}
function compareActivitiesByEventDate(a1: Activity, a2: Activity) {
return a2.enteredBy.eventDate - a1.enteredBy.eventDate;
}
function findLatestActiveActivity(activities: Activity[]) {
return activities
.filter(isActivityNotExited)
.sort(compareActivitiesByEventDate)[0];
}
export default function findTargetActivityIndexes(
activities: Activity[],
event: DomainEvent,
isTransitionDone: boolean,
): number[] {
const targetActivities: number[] = [];
switch (event.name) {
case "Replaced": {
const alreadyExistingActivityIndex = last(
findIndices(activities, (activity) => activity.id === event.activityId),
);
if (alreadyExistingActivityIndex !== undefined) {
break;
}
const sorted = activities
.slice()
.sort(compareActivitiesByEventDate)
.filter(isActivityNotExited);
const transitionState: ActivityTransitionState =
event.skipEnterActiveState || isTransitionDone
? "enter-done"
: "enter-active";
if (transitionState === "enter-done") {
const range = sorted.findIndex(
(activity) =>
!(
event.skipEnterActiveState &&
activity.enteredBy.name === "Replaced" &&
activity.transitionState === "enter-active"
),
);
return sorted.slice(0, range + 1).map((a) => activities.indexOf(a));
}
break;
}
case "Popped": {
const sorted = activities
.filter(isActivityNotExited)
.sort(compareActivitiesByEventDate);
const latestActivity = sorted.slice(0, sorted.length - 1)[0];
if (latestActivity) {
targetActivities.push(activities.indexOf(latestActivity));
}
break;
}
case "StepPushed":
case "StepReplaced": {
const latestActivity = findLatestActiveActivity(activities);
if (latestActivity) {
if (
event.targetActivityId &&
event.targetActivityId !== latestActivity.id
) {
break;
}
targetActivities.push(activities.indexOf(latestActivity));
}
break;
}
case "StepPopped": {
const latestActivity = findLatestActiveActivity(activities);
if (latestActivity && latestActivity.steps.length > 1) {
if (
event.targetActivityId &&
event.targetActivityId !== latestActivity.id
) {
break;
}
targetActivities.push(activities.indexOf(latestActivity));
}
break;
}
default:
break;
}
return targetActivities;
}
import type { Activity, ActivityTransitionState } from "../Stack";
import type {
ActivityRegisteredEvent,
DomainEvent,
InitializedEvent,
PoppedEvent,
PushedEvent,
ReplacedEvent,
StepPoppedEvent,
StepPushedEvent,
StepReplacedEvent,
} from "../event-types";
import { createActivityFromEvent } from "./createActivityFromEvent";
import { createReducer } from "./createReducer";
import findNewActivityIndex from "./findNewActivityIndex";
/**
* Create activity list reducers for each event type (Activity[] + Event => Activity[])
*/
export const makeActivitiesReducers = (isTransitionDone: boolean) =>
createReducer({
/**
* noop
*/
Initialized: (
activities: Activity[],
event: InitializedEvent,
): Activity[] => activities,
/**
* noop
*/
ActivityRegistered: (
activities: Activity[],
event: ActivityRegisteredEvent,
): Activity[] => activities,
/**
* Push new activity to activities
*/
Pushed: (activities: Activity[], event: PushedEvent): Activity[] => {
const transitionState: ActivityTransitionState =
event.skipEnterActiveState || isTransitionDone
? "enter-done"
: "enter-active";
const reservedIndex = findNewActivityIndex(event, activities);
return [
...activities.slice(0, reservedIndex),
createActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1),
];
},
/**
* Replace activity at reservedIndex with new activity
*/
Replaced: (activities: Activity[], event: ReplacedEvent): Activity[] => {
const reservedIndex = findNewActivityIndex(event, activities);
// reuse state of alreadyExistingActivity
const transitionState =
activities[reservedIndex]?.transitionState ??
(event.skipEnterActiveState || isTransitionDone
? "enter-done"
: "enter-active");
return [
...activities.slice(0, reservedIndex),
createActivityFromEvent(event, transitionState),
...activities.slice(reservedIndex + 1),
];
},
/**
* noop
*/
Popped: (activities: Activity[], event: PoppedEvent): Activity[] =>
activities,
/**
* noop
*/
StepPushed: (activities: Activity[], event: StepPushedEvent): Activity[] =>
activities,
/**
* noop
*/
StepReplaced: (
activities: Activity[],
event: StepReplacedEvent,
): Activity[] => activities,
/**
* noop
*/
StepPopped: (activities: Activity[], event: StepPoppedEvent): Activity[] =>
activities,
});
import type { Activity, ActivityTransitionState } from "../Stack";
import type {
ActivityRegisteredEvent,
DomainEvent,
InitializedEvent,
PoppedEvent,
PushedEvent,
ReplacedEvent,
StepPoppedEvent,
StepPushedEvent,
StepReplacedEvent,
} from "../event-types";
import { last } from "../utils";
import { createReducer } from "./createReducer";
/**
* Create activity reducers for each event type (Activity + Event => Activity)
*/
export const makeActivityReducers = (isTransitionDone: boolean) =>
createReducer({
/**
* noop
*/
Initialized: (activity: Activity, event: InitializedEvent): Activity =>
activity,
/**
* noop
*/
ActivityRegistered: (
activity: Activity,
event: ActivityRegisteredEvent,
): Activity => activity,
/**
* noop
*/
Pushed: (activity: Activity, event: PushedEvent): Activity => activity,
/**
* Change transition state to exit-done
*/
Replaced: (activity: Activity, event: ReplacedEvent): Activity => ({
...activity,
exitedBy: event,
transitionState: "exit-done",
}),
/**
* Change transition state to exit-done or exit-active depending on skipExitActiveState
*/
Popped: (activity: Activity, event: PoppedEvent): Activity => {
const transitionState: ActivityTransitionState =
event.skipExitActiveState || isTransitionDone
? "exit-done"
: "exit-active";
return {
...activity,
exitedBy: event,
transitionState,
params:
transitionState === "exit-done"
? activity.steps[0].params
: activity.params,
steps:
transitionState === "exit-done"
? [activity.steps[0]]
: activity.steps,
};
},
/**
* Replace step params
* Push new step
*/
StepPushed: (activity: Activity, event: StepPushedEvent): Activity => {
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event,
};
return {
...activity,
params: event.stepParams,
steps: [...activity.steps, newRoute],
};
},
/**
* Replace step params
* Replace the last step
*/
StepReplaced: (activity: Activity, event: StepReplacedEvent): Activity => {
const newRoute = {
id: event.stepId,
params: event.stepParams,
enteredBy: event,
};
return {
...activity,
params: event.stepParams,
steps: [
...activity.steps.slice(0, activity.steps.length - 1),
newRoute,
],
};
},
/**
* Pop the last step
* If there are params in the previous step, set them as the new params
*/
StepPopped: (activity: Activity, event: StepPoppedEvent): Activity => {
activity.steps.pop();
const beforeActivityParams = last(activity.steps)?.params;
return {
...activity,
params: beforeActivityParams ?? activity.params,
};
},
} as const);

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 too big to display