import { DispatchedPayload, Dispatcher } from "./Dispatcher"; import { DispatcherPayloadMeta } from "./DispatcherPayloadMeta"; import { StoreLike } from "./StoreLike"; import { UseCaseExecutor } from "./UseCaseExecutor"; import { CompletedPayload } from "./payload/CompletedPayload"; import { DidExecutedPayload } from "./payload/DidExecutedPayload"; import { ErrorPayload } from "./payload/ErrorPayload"; import { WillExecutedPayload } from "./payload/WillExecutedPayload"; import { UseCaseFunction } from "./FunctionalUseCaseContext"; import { FunctionalUseCase } from "./FunctionalUseCase"; import { StateMap } from "./UILayer/StoreGroupTypes"; import { TransactionContext } from "./UnitOfWork/TransactionContext"; import { LifeCycleEventHub } from "./LifeCycleEventHub"; import { UseCase } from "./UseCase"; /** * Initialize Context with a dispatcher, a Store(Group), and options. * * ## Example * * ```js * const appContext = new Context({ * dispatcher: new Dispatcher(), * store: AppStore.create(), * options: { * strict: true, * performanceProfile: process.env.NODE_ENV !== "production" * } * }); * ``` */ export interface ContextArgs { /** * Dispatcher instance. * * Notes: Almin 0.16+ * * It it optional parameter. */ dispatcher?: Dispatcher; /** * StoreGroup instance */ store: StoreLike; /** * Context's option is optional */ options?: { /** * Set `strict` to `true` to enable strict mode. * * Strict mode ensure consistency in almin. * If you update your store outside of almin, show warning in strict mode. * * For more details, see * * Default: false(`false` in production build) */ strict?: boolean; /** * Set `performanceProfile` to `true` to enable profiling performance. * * If it is `true`, Show UseCase, Store, StoreGroup, and Transaction performance tracing in the browser devtool timeline. * Only works in development mode and in browsers that support the [Performance.mark()](https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark). * * For more details, see * * Default: false(`false` in production build) */ performanceProfile?: boolean; }; } /** * Context class provide observing and communicating with **Store** and **UseCase**. */ export declare class Context { /** * @private */ private dispatcher; private lifeCycleEventHub; private storeGroup; private config; /** * Context should be initialized with `store` that is an instance of StoreLike implementation * * ### Example * * It is minimal initialization. * * ```js * const context = new Context({ * store: new MyStore() * }); * ``` * * In real case, you can pass `StoreGroup` instead of `Store`. * * ```js * const storeGroup = new StoreGroup([ * new AStore(), new BStore(), new CStore() * ]); * const context = new Context({ * store: storeGroup * }); * ``` */ constructor(args: ContextArgs); /** * Return almin life cycle events hub. * You can observe life cycle events on almin. * * See LifeCycleEventHub */ get events(): LifeCycleEventHub; /** * Return state value of StoreGroup or Store. * * ### Example * * If you pass `StoreGroup` to `store` of Constructor, * `Context#getState()` return the state object that merge each stores's state. * * ```js * const state = context.getState(); * console.log(state); * // { aState, bState } * ``` */ getState(): StateMap; /** * If anyone store that is passed to constructor is changed, `handler` is called. * `onChange` arguments is an array of `Store` instances that are changed. * * It returns unSubscribe function. * If you want to release handler, the returned function. * * It is useful for updating view in the `handler`. * * ### Example * * ```js * const unSubscribe = context.onChange(changingStores => { * console.log(changingStores); // Array * // Update view * }); * ``` * * * ## Notes * * If you want to know the change of registered store, please use `context.onChange`. * `context.onChange` is optimized for updating View. * By contrast, `context.events.*` is not optimized data. it is useful for logging. * */ onChange(handler: (changingStores: Array>) => void): () => void; /** * `Context#useCase` can accept two type of UseCase. * * - Instance of UseCase class * - **Functional UseCase** * * ### Example * * ```js * // UseCase class pattern * class AwesomeUseCase extends UseCase { * execute(...args){ } * } * * context.useCase(new AwesomeUseCase()).execute([1, 2, 3]); * ``` * * OR * * ```js * // Functional UseCase pattern * const awesomeUseCase = ({dispatcher}) => { * return (...args) => { } * }; * * context.useCase(awesomeUseCase).execute([1, 2, 3]); * ``` */ useCase(useCase: T): UseCaseExecutor; useCase(useCase: UseCaseFunction): UseCaseExecutor; /** * ## Transaction * * - **Stability**: Experimental * - This feature is subject to change. It may change or be removed in future versions. * * Create new Unit of Work and execute UseCase. * You can prevent heavy updating of StoreGroup. * * This feature only work in strict mode. * * Difference with `Context#useCase`: * * - Do not update StoreGroup automatically * - You should call `transactionContext.commit()` to update StoreGroup at any time * * ## Example * * ```js * context.transaction("A->B->C transaction", transactionContext => { * return transactionContext.useCase(new ChangeAUseCase()).execute() // no update store * .then(() => { * return transactionContext.useCase(new ChangeBUseCase()).execute(); // no update store * }).then(() => { * return transactionContext.useCase(new ChangeCUseCase()).execute(); // no update store * }).then(() => { * transactionContext.commit(); // update store * // replay: ChangeAUseCase -> ChangeBUseCase -> ChangeCUseCase * }); * }); * ``` * * ## Notes * * ### Transaction should be commit or exit * * Transaction context should be called `commit()` or `exit()`. * Because, this check logic avoid to silent failure of transaction. * * And, transaction context should return a promise. * In most case, transaction context should return `transactionContext.useCase(useCase).execute()`. * * ### Transaction disallow to do multiple commits * * A transaction can do a single `commit()` * Not to allow to do multiple commits in a transaction. * * Use multiple transaction chain insteadof multiple commit in a transaction. * * If you want to multiple commit, please file issue with the motivation. * * ### Transaction is not lock system * * The **transaction** does not lock updating of stores. * The **transaction** method that create a new unit of work. * * It means that the store may be updated by other unit of work during executing `context.transaction`. * `context.transaction` provide the way for bulk updating. * * A Unit of Work promise the order of events in the Unit of Work. * But, This don't promise the order of events between Unit of Works. * * ``` * ----------------- commit ----------------- * | Unit of Work A | ------> | | * ---------------- | StoreGroup | * ----------------- commit | | * | Unit of Work B | ------> | | * ---------------- ----------------- * ``` * * Current implementation is **READ COMMITTED** of Transaction Isolation Levels. * * - * * ### No commit transaction get cancelled * * You can write no `commit()` transaction. * This transaction add commitment to the unit of work, but does not `commit()`. * * ```js * context.transaction("No commit transaction", transactionContext => { * // No commit - Call `transactionContext.exit()` insteadof `transactionContext.commit()` * return transactionContext.useCase(new LogUseCase()).execute().then(() => { * transactionContext.exit(); * }); * }); * ``` * * As a result, This transaction does not affect to Store. * It means that Store can't received the payload of `LogUseCase`. * Finally, that commitment get cancelled. * * It is useful for logging UseCase. * Logging UseCase does not need to update store/view. * It only does log/send data to console/server. * * ### TODO: rollback is not implemented * * Rollback feature is generality implemented in the unit of work. * We want to know actual use case of rollback before implementing this. * */ transaction(name: string, transactionHandler: (transactionContext: TransactionContext) => Promise): Promise; /** * Register `handler` function to Context. * `handler` is called when each useCases will execute. * @deprecated * Use `context.events.onWillExecuteEachUseCase` insteadof it. */ onWillExecuteEachUseCase(handler: (payload: WillExecutedPayload, meta: DispatcherPayloadMeta) => void): () => void; /** * Register `handler` function to Context. * `handler` is called the `handler` with user-defined payload object when the UseCase dispatch with payload. * This `onDispatch` is not called at built-in event. It is filtered by Context. * If you want to *All* dispatched event and use listen directly your `dispatcher` object. * In other word, listen the dispatcher of `new Context({dispatcher})`. * * ### Example * * ```js * const dispatchUseCase = ({dispatcher}) => { * return () => dispatcher.dispatch({ type: "fired-payload" }); * }; * context.onDispatch((payload, meta) => { * console.log(payload); // { type: "fired-payload" } * }); * * context.useCase(dispatchUseCase).execute(); * ``` * * @deprecated * Use `context.events.onDispatch` insteadof it. */ onDispatch(handler: (payload: DispatchedPayload, meta: DispatcherPayloadMeta) => void): () => void; /** * `handler` is called when each useCases are executed. * @deprecated * Use `context.events.onDidExecuteEachUseCase` insteadof it. */ onDidExecuteEachUseCase(handler: (payload: DidExecutedPayload, meta: DispatcherPayloadMeta) => void): () => void; /** * `handler` is called when each useCases are completed. * This `handler` is always called asynchronously. * @deprecated * Use `context.events.onCompleteEachUseCase` insteadof it. */ onCompleteEachUseCase(handler: (payload: CompletedPayload, meta: DispatcherPayloadMeta) => void): () => void; /** * `handler` is called when some UseCase throw Error. * * Throwing Error is following case: * * - Throw exception in a UseCase * - Return rejected promise in a UseCase * - Call `UseCase#throwError(error)` * @deprecated * Use `context.events.onErrorDispatch` insteadof it. */ onErrorDispatch(handler: (payload: ErrorPayload, meta: DispatcherPayloadMeta) => void): () => void; /** * Release all events handler in Context. * You can call this when no more call event handler */ release(): void; }