Skip to content

Commit

Permalink
feat: support multiple stores registering the same thunk (#51)
Browse files Browse the repository at this point in the history
* feat(thunk): enable multiple stores to register thunks with unique identifiers

* refactor(store): remove unused getStoreId function
  • Loading branch information
VldMrgnn authored Nov 14, 2024
1 parent 794602b commit d0a8540
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 18 deletions.
32 changes: 19 additions & 13 deletions query/thunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ActionContext, API_ACTION_PREFIX, takeEvery } from "../action.ts";
import { compose } from "../compose.ts";
import { Callable, ensure, Ok, Operation, Signal } from "../deps.ts";
import { keepAlive, supervise } from "../fx/mod.ts";
import { IdContext } from "../store/store.ts";
import { createKey } from "./create-key.ts";
import { isFn, isObject } from "./util.ts";

Expand All @@ -15,7 +16,6 @@ import type {
Supervisor,
ThunkCtx,
} from "./types.ts";

export interface ThunksApi<Ctx extends ThunkCtx> {
use: (fn: Middleware<Ctx>) => void;
routes: () => Middleware<Ctx>;
Expand Down Expand Up @@ -82,6 +82,8 @@ export interface ThunksApi<Ctx extends ThunkCtx> {
): CreateActionWithPayload<Gtx, P>;
}

let id = 0;

/**
* Creates a middleware pipeline.
*
Expand Down Expand Up @@ -124,17 +126,17 @@ export function createThunks<Ctx extends ThunkCtx = ThunkCtx<any>>(
} = { supervisor: takeEvery },
): ThunksApi<Ctx> {
let signal: Signal<AnyAction, void> | undefined = undefined;
let storeId: number | undefined = undefined;
const middleware: Middleware<Ctx>[] = [];
const visors: { [key: string]: Callable<unknown> } = {};
const middlewareMap: { [key: string]: Middleware<Ctx> } = {};
let dynamicMiddlewareMap: { [key: string]: Middleware<Ctx> } = {};
const actionMap: {
[key: string]: CreateActionWithPayload<Ctx, any>;
} = {};
const thunkId = `${Date.now().toString(36)}-${
Math.random().toString(36).substring(2, 11)
}`;
let hasRegistered = false;
const thunkId = id++;

const storeMap = new Map<number, Signal<AnyAction, void>>();

function* defaultMiddleware(_: Ctx, next: Next) {
yield* next();
Expand Down Expand Up @@ -207,10 +209,10 @@ export function createThunks<Ctx extends ThunkCtx = ThunkCtx<any>>(

visors[name] = curVisor;

// If signal is available, register immediately, otherwise defer
if (signal) {
signal.send({
type: `${API_ACTION_PREFIX}REGISTER_THUNK_${thunkId}`,
// If signal is already referenced, register immediately, otherwise defer
for (const [storeId, storeSignal] of storeMap.entries()) {
storeSignal.send({
type: `${API_ACTION_PREFIX}REGISTER_THUNK_${storeId}_${thunkId}`,
payload: curVisor,
});
}
Expand Down Expand Up @@ -253,23 +255,27 @@ export function createThunks<Ctx extends ThunkCtx = ThunkCtx<any>>(
}

function* register() {
if (hasRegistered) {
storeId = yield* IdContext;
if (storeId && storeMap.has(storeId)) {
console.warn("This thunk instance is already registered.");
return;
}
hasRegistered = true;

signal = yield* ActionContext;
storeMap.set(storeId, signal);

yield* ensure(function* () {
hasRegistered = false;
if (storeId) {
storeMap.delete(storeId);
}
});

// Register any thunks created after signal is available
yield* keepAlive(Object.values(visors));

// Spawn a watcher for further thunk matchingPairs
yield* takeEvery(
`${API_ACTION_PREFIX}REGISTER_THUNK_${thunkId}`,
`${API_ACTION_PREFIX}REGISTER_THUNK_${storeId}_${thunkId}`,
watcher as any,
);
}
Expand Down
15 changes: 10 additions & 5 deletions store/store.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { ActionContext, API_ACTION_PREFIX, emit } from "../action.ts";
import { BaseMiddleware, compose } from "../compose.ts";
import {
createContext,
createScope,
createSignal,
enablePatches,
Ok,
produceWithPatches,
Scope,
} from "../deps.ts";
import { BaseMiddleware, compose } from "../compose.ts";
import type { AnyAction, AnyState, Next } from "../types.ts";
import type { FxStore, Listener, StoreUpdater, UpdaterCtx } from "./types.ts";
import { StoreContext, StoreUpdateContext } from "./context.ts";
import { ActionContext, emit } from "../action.ts";
import { API_ACTION_PREFIX } from "../action.ts";
import { createRun } from "./run.ts";

import type { AnyAction, AnyState, Next } from "../types.ts";
import type { FxStore, Listener, StoreUpdater, UpdaterCtx } from "./types.ts";
const stubMsg = "This is merely a stub, not implemented";

let id = 0;

// https://github.com/reduxjs/redux/blob/4a6d2fb227ba119d3498a43fab8f53fe008be64c/src/createStore.ts#L344
function observable() {
return {
Expand All @@ -34,6 +36,8 @@ export interface CreateStore<S extends AnyState> {
middleware?: BaseMiddleware<UpdaterCtx<S>>[];
}

export const IdContext = createContext("starfx:id", 0);

export function createStore<S extends AnyState>({
initialState,
scope: initScope,
Expand All @@ -53,6 +57,7 @@ export function createStore<S extends AnyState>({

const signal = createSignal<AnyAction, void>();
scope.set(ActionContext, signal);
scope.set(IdContext, id++);

function getScope() {
return scope;
Expand Down
26 changes: 26 additions & 0 deletions test/thunk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "../mod.ts";
import { createStore, updateStore } from "../store/mod.ts";
import { assertLike, asserts, describe, it } from "../test.ts";

import type { Next, ThunkCtx } from "../mod.ts";

// deno-lint-ignore no-explicit-any
Expand Down Expand Up @@ -625,3 +626,28 @@ it(
);
},
);

it(
tests,
"should allow multiple stores to register a thunk",
() => {
const api1 = createThunks<RoboCtx>();
api1.use(api1.routes());
const storeA = createStore({ initialState: {} });
const storeB = createStore({ initialState: {} });
storeA.run(api1.register);
storeB.run(api1.register);
let acc = "";
const action = api1.create("/users", function* () {
acc += "b";
});
storeA.dispatch(action());
storeB.dispatch(action());

asserts.assertEquals(
acc,
"bb",
"Expected 'bb' after first API call, but got: " + acc,
);
},
);

0 comments on commit d0a8540

Please sign in to comment.