diff --git a/packages/store/src/git-sync/git-sync-slice.test.ts b/packages/store/src/git-sync/git-sync-slice.test.ts new file mode 100644 index 00000000..ea7bf46e --- /dev/null +++ b/packages/store/src/git-sync/git-sync-slice.test.ts @@ -0,0 +1,146 @@ +import { describe, expect, it, vi } from "vitest" +import { createStore } from "zustand/vanilla" +import { type GitSyncSlice, prepareGitSyncSlice } from "./git-sync-slice" + +describe("git-sync-slice", () => { + it("emits a pulled-changes event instead of refreshing the workspace directly", async () => { + const sync = vi.fn().mockResolvedValue({ pulledChanges: true }) + const events = { + emit: vi.fn().mockResolvedValue(undefined), + subscribe: vi.fn(), + } + const store = createStore()((set, get, api) => + prepareGitSyncSlice( + { + loadSettings: vi.fn().mockResolvedValue({}), + saveSettings: vi.fn().mockResolvedValue(undefined), + createGitSyncCore: () => ({ + isGitRepository: vi.fn().mockResolvedValue(true), + getCurrentBranch: vi.fn().mockResolvedValue("main"), + hasChangesToCommit: vi.fn().mockResolvedValue(false), + getCurrentCommitHash: vi.fn().mockResolvedValue(null), + ensureGitignoreEntry: vi.fn().mockResolvedValue(undefined), + detectSyncStatus: vi.fn().mockResolvedValue("synced"), + sync, + }), + }, + { events: events as any }, + )(set, get, api), + ) + + store.setState((state) => ({ + ...state, + gitSyncState: { + ...state.gitSyncState, + workspacePath: "/ws", + isGitRepo: true, + status: "unsynced", + }, + })) + + await store.getState().performSync() + + expect(sync).toHaveBeenCalledTimes(1) + expect(events.emit).toHaveBeenCalledWith({ + type: "git-sync/pulled-changes", + workspacePath: "/ws", + }) + }) + + it("waits for the pulled-changes event handlers before resolving", async () => { + const sync = vi.fn().mockResolvedValue({ pulledChanges: true }) + let resolveEmit!: () => void + const emitPromise = new Promise((resolve) => { + resolveEmit = resolve + }) + const events = { + emit: vi.fn().mockReturnValue(emitPromise), + subscribe: vi.fn(), + } + const store = createStore()((set, get, api) => + prepareGitSyncSlice( + { + loadSettings: vi.fn().mockResolvedValue({}), + saveSettings: vi.fn().mockResolvedValue(undefined), + createGitSyncCore: () => ({ + isGitRepository: vi.fn().mockResolvedValue(true), + getCurrentBranch: vi.fn().mockResolvedValue("main"), + hasChangesToCommit: vi.fn().mockResolvedValue(false), + getCurrentCommitHash: vi.fn().mockResolvedValue(null), + ensureGitignoreEntry: vi.fn().mockResolvedValue(undefined), + detectSyncStatus: vi.fn().mockResolvedValue("synced"), + sync, + }), + }, + { events: events as any }, + )(set, get, api), + ) + + store.setState((state) => ({ + ...state, + gitSyncState: { + ...state.gitSyncState, + workspacePath: "/ws", + isGitRepo: true, + status: "unsynced", + }, + })) + + let resolved = false + const syncPromise = store + .getState() + .performSync() + .then(() => { + resolved = true + }) + + await Promise.resolve() + expect(resolved).toBe(false) + + resolveEmit() + await syncPromise + + expect(resolved).toBe(true) + }) + + it("sets an error state when the pulled-changes refresh fails", async () => { + const sync = vi.fn().mockResolvedValue({ pulledChanges: true }) + const events = { + emit: vi.fn().mockRejectedValue(new Error("refresh failed")), + subscribe: vi.fn(), + } + const store = createStore()((set, get, api) => + prepareGitSyncSlice( + { + loadSettings: vi.fn().mockResolvedValue({}), + saveSettings: vi.fn().mockResolvedValue(undefined), + createGitSyncCore: () => ({ + isGitRepository: vi.fn().mockResolvedValue(true), + getCurrentBranch: vi.fn().mockResolvedValue("main"), + hasChangesToCommit: vi.fn().mockResolvedValue(false), + getCurrentCommitHash: vi.fn().mockResolvedValue(null), + ensureGitignoreEntry: vi.fn().mockResolvedValue(undefined), + detectSyncStatus: vi.fn().mockResolvedValue("synced"), + sync, + }), + }, + { events: events as any }, + )(set, get, api), + ) + + store.setState((state) => ({ + ...state, + gitSyncState: { + ...state.gitSyncState, + workspacePath: "/ws", + isGitRepo: true, + status: "unsynced", + }, + })) + + await store.getState().performSync() + + expect(store.getState().gitSyncState.status).toBe("error") + expect(store.getState().gitSyncState.error).toBe("refresh failed") + }) +}) diff --git a/packages/store/src/git-sync/git-sync-slice.ts b/packages/store/src/git-sync/git-sync-slice.ts index 046c41df..0efa717b 100644 --- a/packages/store/src/git-sync/git-sync-slice.ts +++ b/packages/store/src/git-sync/git-sync-slice.ts @@ -1,7 +1,7 @@ import type { GitSyncCore, GitSyncStatus, SyncConfig } from "@mdit/git-sync" import type { StateCreator } from "zustand" +import type { StoreEventHub } from "../integrations/store-events" import type { WorkspaceSettings } from "../workspace/workspace-settings" -import type { WorkspaceSlice } from "../workspace/workspace-slice" export type { SyncConfig } from "@mdit/git-sync" @@ -56,16 +56,10 @@ const buildInitialGitSyncState = (): GitSyncState => ({ }) export const prepareGitSyncSlice = - ({ - loadSettings, - saveSettings, - createGitSyncCore, - }: GitSyncSliceDependencies): StateCreator< - GitSyncSlice & WorkspaceSlice, - [], - [], - GitSyncSlice - > => + ( + { loadSettings, saveSettings, createGitSyncCore }: GitSyncSliceDependencies, + { events }: { events: StoreEventHub }, + ): StateCreator => (set, get) => { const gitSyncCore = createGitSyncCore() @@ -204,9 +198,11 @@ export const prepareGitSyncSlice = }, })) - // Refresh workspace entries if pull merged changes if (result.pulledChanges) { - await get().refreshWorkspaceEntries() + await events.emit({ + type: "git-sync/pulled-changes", + workspacePath, + }) } } catch (error) { const message = diff --git a/packages/store/src/index.ts b/packages/store/src/index.ts index 4f5ed60b..b28f75e4 100644 --- a/packages/store/src/index.ts +++ b/packages/store/src/index.ts @@ -25,6 +25,9 @@ import { type IndexingSliceDependencies, prepareIndexingSlice, } from "./indexing/indexing-slice" +import { registerGitSyncWorkspaceIntegration } from "./integrations/register-git-sync-workspace-integration" +import { registerIndexingIntegration } from "./integrations/register-indexing-integration" +import { createStoreEventHub } from "./integrations/store-events" import type { TabSlice } from "./tab/tab-slice" import { prepareTabSlice, type TabSliceDependencies } from "./tab/tab-slice" import type { UISlice } from "./ui/ui-slice" @@ -59,18 +62,23 @@ export type MditStore = UseBoundStore> export const createMditStore = ( dependencies: MditStoreDependencies, ): MditStore => { + const events = createStoreEventHub() const createCollectionSlice = prepareCollectionSlice() const createEditorSlice = prepareEditorSlice() const createImageEditSlice = prepareImageEditSlice() const createAISettingsSlice = prepareAISettingsSlice(dependencies.aiSettings) - const createGitSyncSlice = prepareGitSyncSlice(dependencies.gitSync) + const createGitSyncSlice = prepareGitSyncSlice(dependencies.gitSync, { + events, + }) const createHotkeysSlice = prepareHotkeysSlice(dependencies.hotkeys) const createIndexingSlice = prepareIndexingSlice(dependencies.indexing) const createTabSlice = prepareTabSlice(dependencies.tab) const createUISlice = prepareUISlice(dependencies.ui) - const createWorkspaceSlice = prepareWorkspaceSlice(dependencies.workspace) + const createWorkspaceSlice = prepareWorkspaceSlice(dependencies.workspace, { + events, + }) - return create()((...args) => ({ + const store = create()((...args) => ({ ...createCollectionSlice(...args), ...createTabSlice(...args), ...createWorkspaceSlice(...args), @@ -82,4 +90,9 @@ export const createMditStore = ( ...createHotkeysSlice(...args), ...createUISlice(...args), })) + + registerIndexingIntegration(store, events) + registerGitSyncWorkspaceIntegration(store, events) + + return store } diff --git a/packages/store/src/integrations/integrations.test.ts b/packages/store/src/integrations/integrations.test.ts new file mode 100644 index 00000000..517eaeea --- /dev/null +++ b/packages/store/src/integrations/integrations.test.ts @@ -0,0 +1,128 @@ +import { describe, expect, it, vi } from "vitest" +import type { MditStore } from ".." +import { registerGitSyncWorkspaceIntegration } from "./register-git-sync-workspace-integration" +import { registerIndexingIntegration } from "./register-indexing-integration" +import { createStoreEventHub } from "./store-events" + +describe("store integrations", () => { + it("resets indexing state when workspace resets", async () => { + const events = createStoreEventHub() + const state = { + resetIndexingState: vi.fn(), + getIndexingConfig: vi.fn(), + } + const store = { + getState: () => state, + } as unknown as MditStore + + registerIndexingIntegration(store, events) + await events.emit({ + type: "workspace/reset", + workspacePath: "/ws", + }) + + expect(state.resetIndexingState).toHaveBeenCalledTimes(1) + }) + + it("preloads indexing config when workspace loads", async () => { + const events = createStoreEventHub() + const getIndexingConfig = vi.fn().mockResolvedValue(null) + const store = { + getState: () => ({ + resetIndexingState: vi.fn(), + getIndexingConfig, + }), + } as unknown as MditStore + + registerIndexingIntegration(store, events) + await events.emit({ + type: "workspace/loaded", + workspacePath: "/ws", + }) + + expect(getIndexingConfig).toHaveBeenCalledWith("/ws") + }) + + it("logs indexing preload failures without throwing", async () => { + const events = createStoreEventHub() + const preloadError = new Error("preload failed") + const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {}) + const store = { + getState: () => ({ + resetIndexingState: vi.fn(), + getIndexingConfig: vi.fn().mockRejectedValue(preloadError), + }), + } as unknown as MditStore + + registerIndexingIntegration(store, events) + await events.emit({ + type: "workspace/loaded", + workspacePath: "/ws", + }) + + expect(errorSpy).toHaveBeenCalledWith( + "Failed to preload indexing config:", + preloadError, + ) + + errorSpy.mockRestore() + }) + + it("refreshes the active workspace when sync pulled changes", async () => { + const events = createStoreEventHub() + const refreshWorkspaceEntries = vi.fn().mockResolvedValue(undefined) + const store = { + getState: () => ({ + workspacePath: "/ws", + refreshWorkspaceEntries, + }), + } as unknown as MditStore + + registerGitSyncWorkspaceIntegration(store, events) + await events.emit({ + type: "git-sync/pulled-changes", + workspacePath: "/ws", + }) + + expect(refreshWorkspaceEntries).toHaveBeenCalledTimes(1) + }) + + it("propagates workspace refresh failures for sync pulled changes", async () => { + const events = createStoreEventHub() + const refreshError = new Error("refresh failed") + const store = { + getState: () => ({ + workspacePath: "/ws", + refreshWorkspaceEntries: vi.fn().mockRejectedValue(refreshError), + }), + } as unknown as MditStore + + registerGitSyncWorkspaceIntegration(store, events) + + await expect( + events.emit({ + type: "git-sync/pulled-changes", + workspacePath: "/ws", + }), + ).rejects.toThrow("refresh failed") + }) + + it("ignores sync refresh events for a different workspace", async () => { + const events = createStoreEventHub() + const refreshWorkspaceEntries = vi.fn().mockResolvedValue(undefined) + const store = { + getState: () => ({ + workspacePath: "/other", + refreshWorkspaceEntries, + }), + } as unknown as MditStore + + registerGitSyncWorkspaceIntegration(store, events) + await events.emit({ + type: "git-sync/pulled-changes", + workspacePath: "/ws", + }) + + expect(refreshWorkspaceEntries).not.toHaveBeenCalled() + }) +}) diff --git a/packages/store/src/integrations/register-git-sync-workspace-integration.ts b/packages/store/src/integrations/register-git-sync-workspace-integration.ts new file mode 100644 index 00000000..e40e4049 --- /dev/null +++ b/packages/store/src/integrations/register-git-sync-workspace-integration.ts @@ -0,0 +1,19 @@ +import type { MditStore } from ".." +import type { StoreEventHub } from "./store-events" + +export const registerGitSyncWorkspaceIntegration = ( + store: MditStore, + events: StoreEventHub, +) => + events.subscribe(async (event) => { + if (event.type !== "git-sync/pulled-changes") { + return + } + + const state = store.getState() + if (state.workspacePath !== event.workspacePath) { + return + } + + await state.refreshWorkspaceEntries() + }) diff --git a/packages/store/src/integrations/register-indexing-integration.ts b/packages/store/src/integrations/register-indexing-integration.ts new file mode 100644 index 00000000..142269c2 --- /dev/null +++ b/packages/store/src/integrations/register-indexing-integration.ts @@ -0,0 +1,24 @@ +import type { MditStore } from ".." +import type { StoreEventHub } from "./store-events" + +export const registerIndexingIntegration = ( + store: MditStore, + events: StoreEventHub, +) => + events.subscribe((event) => { + const state = store.getState() + + if (event.type === "workspace/reset") { + state.resetIndexingState() + return + } + + if (event.type === "workspace/loaded") { + return state + .getIndexingConfig(event.workspacePath) + .then(() => undefined) + .catch((error) => { + console.error("Failed to preload indexing config:", error) + }) + } + }) diff --git a/packages/store/src/integrations/store-events.ts b/packages/store/src/integrations/store-events.ts new file mode 100644 index 00000000..8ff57668 --- /dev/null +++ b/packages/store/src/integrations/store-events.ts @@ -0,0 +1,42 @@ +export type StoreEvent = + | { type: "workspace/reset"; workspacePath: string | null } + | { type: "workspace/loaded"; workspacePath: string } + | { type: "git-sync/pulled-changes"; workspacePath: string } + +export type StoreEventListener = (event: StoreEvent) => void | Promise + +export type StoreEventHub = { + emit: (event: StoreEvent) => Promise + subscribe: (listener: StoreEventListener) => () => void +} + +export const createStoreEventHub = (): StoreEventHub => { + const listeners = new Set() + + return { + emit: async (event) => { + const pending = Array.from(listeners, (listener) => { + try { + return Promise.resolve(listener(event)) + } catch (error) { + return Promise.reject(error) + } + }) + + const settled = await Promise.allSettled(pending) + const rejected = settled.find( + (result): result is PromiseRejectedResult => + result.status === "rejected", + ) + if (rejected) { + throw rejected.reason + } + }, + subscribe: (listener) => { + listeners.add(listener) + return () => { + listeners.delete(listener) + } + }, + } +} diff --git a/packages/store/src/workspace/lifecycle/actions.test.ts b/packages/store/src/workspace/lifecycle/actions.test.ts index 53f94d82..4750e0eb 100644 --- a/packages/store/src/workspace/lifecycle/actions.test.ts +++ b/packages/store/src/workspace/lifecycle/actions.test.ts @@ -26,7 +26,7 @@ describe("lifecycle-actions", () => { }) it("setWorkspace unwatches existing watcher when workspace path changes", async () => { - const { context, deps, ports, setState, getState } = + const { context, deps, events, setState, getState } = createActionTestContext() const actions = createLifecycleActions(context) const unwatch = vi.fn() @@ -45,8 +45,14 @@ describe("lifecycle-actions", () => { expect(unwatch).toHaveBeenCalledTimes(1) expect(deps.historyRepository.touchWorkspace).toHaveBeenCalledWith("/new") expect(deps.historyRepository.listWorkspacePaths).toHaveBeenCalled() - expect(ports.indexing.resetIndexingState).toHaveBeenCalledTimes(1) - expect(ports.indexing.getIndexingConfig).toHaveBeenCalledWith("/new") + expect(events.emit).toHaveBeenCalledWith({ + type: "workspace/reset", + workspacePath: "/new", + }) + expect(events.emit).toHaveBeenCalledWith({ + type: "workspace/loaded", + workspacePath: "/new", + }) expect(getState().workspacePath).toBe("/new") expect(getState().unwatchFn).toBeNull() }) @@ -110,7 +116,7 @@ describe("lifecycle-actions", () => { }) it("clearWorkspace unwatches existing watcher", async () => { - const { context, deps, ports, setState, getState } = + const { context, deps, events, setState, getState } = createActionTestContext() const actions = createLifecycleActions(context) const unwatch = vi.fn() @@ -125,7 +131,10 @@ describe("lifecycle-actions", () => { expect(unwatch).toHaveBeenCalledTimes(1) expect(deps.historyRepository.removeWorkspace).toHaveBeenCalledWith("/old") - expect(ports.indexing.resetIndexingState).toHaveBeenCalledTimes(1) + expect(events.emit).toHaveBeenCalledWith({ + type: "workspace/reset", + workspacePath: null, + }) expect(getState().workspacePath).toBeNull() expect(getState().unwatchFn).toBeNull() }) @@ -174,7 +183,7 @@ describe("lifecycle-actions", () => { }) it("bootstrapWorkspace unwatches existing watcher when workspace path changes", async () => { - const { context, deps, ports, setState, getState } = + const { context, deps, events, setState, getState } = createActionTestContext() const actions = createLifecycleActions(context) const unwatch = vi.fn() @@ -188,34 +197,33 @@ describe("lifecycle-actions", () => { await bootstrapWorkspace(actions) expect(unwatch).toHaveBeenCalledTimes(1) - expect(ports.indexing.resetIndexingState).toHaveBeenCalledTimes(1) - expect(ports.indexing.getIndexingConfig).toHaveBeenCalledWith("/new") + expect(events.emit).toHaveBeenCalledWith({ + type: "workspace/reset", + workspacePath: "/new", + }) + expect(events.emit).toHaveBeenCalledWith({ + type: "workspace/loaded", + workspacePath: "/new", + }) expect(getState().workspacePath).toBe("/new") expect(getState().unwatchFn).toBeNull() }) - it("bootstrapWorkspace does not fail when indexing preload fails", async () => { - const { context, deps, ports, getState } = createActionTestContext() + it("bootstrapWorkspace emits a loaded event for indexing preload integration", async () => { + const { context, deps, events, getState } = createActionTestContext() const actions = createLifecycleActions(context) - const preloadError = new Error("preload failed") deps.historyRepository.listWorkspacePaths.mockResolvedValue(["/ws"]) deps.fileSystemRepository.isExistingDirectory.mockResolvedValue(true) - ports.indexing.getIndexingConfig.mockRejectedValue(preloadError) - - const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {}) await bootstrapWorkspace(actions) expect(getState().workspacePath).toBe("/ws") expect(getState().isTreeLoading).toBe(false) - expect(ports.indexing.getIndexingConfig).toHaveBeenCalledWith("/ws") - expect(errorSpy).toHaveBeenCalledWith( - "Failed to preload indexing config:", - preloadError, - ) - - errorSpy.mockRestore() + expect(events.emit).toHaveBeenCalledWith({ + type: "workspace/loaded", + workspacePath: "/ws", + }) }) it("bootstrapWorkspace preserves watcher when workspace path is unchanged", async () => { diff --git a/packages/store/src/workspace/lifecycle/domain.ts b/packages/store/src/workspace/lifecycle/domain.ts index 66c2feba..e458fed7 100644 --- a/packages/store/src/workspace/lifecycle/domain.ts +++ b/packages/store/src/workspace/lifecycle/domain.ts @@ -44,7 +44,6 @@ export const resetWorkspaceState = ( ) => { const workspacePath = options.workspacePath ?? null - ctx.ports.indexing.resetIndexingState() ctx.set( buildWorkspaceState({ workspacePath, @@ -55,6 +54,14 @@ export const resetWorkspaceState = ( }), ) ctx.ports.collection.resetCollectionPath() + void ctx.runtime.events + .emit({ + type: "workspace/reset", + workspacePath, + }) + .catch((error) => { + console.error("Failed to emit workspace reset event:", error) + }) } export const closeWorkspaceTabs = ( @@ -176,9 +183,14 @@ const bootstrapWorkspace = async ( }, }) ctx.set({ isTreeLoading: false }) - void ctx.ports.indexing.getIndexingConfig(workspacePath).catch((error) => { - console.error("Failed to preload indexing config:", error) - }) + void ctx.runtime.events + .emit({ + type: "workspace/loaded", + workspacePath, + }) + .catch((error) => { + console.error("Failed to emit workspace loaded event:", error) + }) if (options?.restoreLastOpenedFiles) { await restoreLastOpenedFileHistoryFromSettings( diff --git a/packages/store/src/workspace/shared/action-test-helpers.ts b/packages/store/src/workspace/shared/action-test-helpers.ts index c5975ff6..6e9400e7 100644 --- a/packages/store/src/workspace/shared/action-test-helpers.ts +++ b/packages/store/src/workspace/shared/action-test-helpers.ts @@ -78,6 +78,10 @@ export function createActionTestContext() { prune: vi.fn(), clearWorkspace: vi.fn(), } + const events = { + emit: vi.fn().mockResolvedValue(undefined), + subscribe: vi.fn(), + } let state: any const getOpenTabSnapshotsFromState = () => { @@ -132,10 +136,6 @@ export function createActionTestContext() { resetCollectionPath: vi.fn(), getCurrentCollectionPath: vi.fn(() => state.currentCollectionPath), }, - indexing: { - resetIndexingState: vi.fn(), - getIndexingConfig: vi.fn().mockResolvedValue(null), - }, } state = { @@ -208,6 +208,7 @@ export function createActionTestContext() { ports: ports as any, runtime: { originJournal: originJournal as any, + events: events as any, }, } const directoryUiActions = createDirectoryUiActions(context) @@ -237,6 +238,7 @@ export function createActionTestContext() { context, deps, ports, + events, originJournal, getState: () => state, setState: (patch: Record) => { diff --git a/packages/store/src/workspace/workspace-action-context.ts b/packages/store/src/workspace/workspace-action-context.ts index b4c83227..39cccf7b 100644 --- a/packages/store/src/workspace/workspace-action-context.ts +++ b/packages/store/src/workspace/workspace-action-context.ts @@ -1,5 +1,6 @@ import type { LocalMutationJournal } from "@mdit/local-fs-origin" import type { StoreApi } from "zustand" +import type { StoreEventHub } from "../integrations/store-events" import type { WorkspaceDependencies } from "./workspace-dependencies" import type { WorkspacePorts } from "./workspace-ports" import type { WorkspaceSlice } from "./workspace-slice" @@ -16,6 +17,7 @@ export type WorkspaceGetState< export type WorkspaceRuntime = { originJournal: LocalMutationJournal + events: StoreEventHub } export type WorkspaceActionContext< diff --git a/packages/store/src/workspace/workspace-ports.ts b/packages/store/src/workspace/workspace-ports.ts index 89d68ea3..aa16dc58 100644 --- a/packages/store/src/workspace/workspace-ports.ts +++ b/packages/store/src/workspace/workspace-ports.ts @@ -1,5 +1,4 @@ import type { CollectionSlice } from "../collection/collection-slice" -import type { IndexingSlice } from "../indexing/indexing-slice" import type { TabSlice } from "../tab/tab-slice" export type WorkspacePorts = { @@ -28,10 +27,9 @@ export type WorkspacePorts = { | "resetCollectionPath" | "getCurrentCollectionPath" > - indexing: Pick } -type WorkspacePortSource = TabSlice & CollectionSlice & IndexingSlice +type WorkspacePortSource = TabSlice & CollectionSlice export const createWorkspacePorts = ( get: () => WorkspacePortSource, @@ -61,8 +59,4 @@ export const createWorkspacePorts = ( resetCollectionPath: (...args) => get().resetCollectionPath(...args), getCurrentCollectionPath: () => get().getCurrentCollectionPath(), }, - indexing: { - resetIndexingState: () => get().resetIndexingState(), - getIndexingConfig: (...args) => get().getIndexingConfig(...args), - }, }) diff --git a/packages/store/src/workspace/workspace-slice.ts b/packages/store/src/workspace/workspace-slice.ts index d92d358f..e10fa270 100644 --- a/packages/store/src/workspace/workspace-slice.ts +++ b/packages/store/src/workspace/workspace-slice.ts @@ -4,7 +4,7 @@ import { } from "@mdit/local-fs-origin" import type { StateCreator } from "zustand" import type { CollectionSlice } from "../collection/collection-slice" -import type { IndexingSlice } from "../indexing/indexing-slice" +import type { StoreEventHub } from "../integrations/store-events" import type { TabSlice } from "../tab/tab-slice" import { createDirectoryUiActions, @@ -38,14 +38,12 @@ export type WorkspaceActions = WorkspaceTreeActions & export type WorkspaceSlice = WorkspaceState & WorkspaceActions -type WorkspaceSliceStoreState = WorkspaceSlice & - TabSlice & - CollectionSlice & - IndexingSlice +type WorkspaceSliceStoreState = WorkspaceSlice & TabSlice & CollectionSlice export const prepareWorkspaceSlice = ( dependencies: WorkspaceDependencies, + { events }: { events: StoreEventHub }, ): StateCreator => (set, get) => { const originJournal = createLocalMutationJournal({ @@ -58,6 +56,7 @@ export const prepareWorkspaceSlice = ports: createWorkspacePorts(get), runtime: { originJournal, + events, }, }