Files
2026-08-16 14:58:47 +08:00

73 lines
2.5 KiB
TypeScript

import '@/lib/persistence/bootstrap';
import { BrowserDocumentStore, type DocumentStore } from '@openmaic/storage';
import type { AppScene } from '@/lib/types/stage';
import { registerDocumentStorageResetHook, resolveConfiguredDocumentStore } from './config';
import type { AppStage } from './persistence-types';
import {
resetPlainJsonDocumentWritesForTests,
withPlainJsonDocumentWrites,
} from './plain-json-store';
import { validateAppScene, validateAppStage } from './validators';
export {
configureDocumentStorage,
isDocumentStorageConfigured,
resetDocumentStorageForTests,
} from './config';
export type {
DocumentStorageOptions,
DocumentStorageValidators,
DocumentStoreFactory,
} from './config';
const DOCUMENT_DB_NAME = 'maic-documents';
export interface DocumentStoreDeps {
/** A complete store override takes precedence over browser construction. */
store?: DocumentStore<AppScene, AppStage>;
/** IndexedDB factory for isolated browser tests. */
indexedDB?: IDBFactory;
/** Database name override for isolated browser tests. */
dbName?: string;
}
let defaultStore: DocumentStore<AppScene, AppStage> | undefined;
registerDocumentStorageResetHook(() => {
defaultStore = undefined;
resetPlainJsonDocumentWritesForTests();
});
function createBrowserStore(
deps: Omit<DocumentStoreDeps, 'store'>,
): DocumentStore<AppScene, AppStage> {
// Capability probe, not an environment probe: node test runners inject a
// fake `indexedDB` global; a true server render has neither and must throw.
if (!deps.indexedDB && typeof indexedDB === 'undefined') {
throw new Error('Document persistence requires IndexedDB (client-only)');
}
return withPlainJsonDocumentWrites(
new BrowserDocumentStore<AppScene, AppStage>({
indexedDB: deps.indexedDB,
dbName: deps.dbName ?? DOCUMENT_DB_NAME,
validateScene: validateAppScene,
validateStage: validateAppStage,
}),
);
}
/** Resolve the app document store without opening IndexedDB at module import. */
export function getDocumentStore(deps: DocumentStoreDeps = {}): DocumentStore<AppScene, AppStage> {
if (deps.store) return withPlainJsonDocumentWrites(deps.store);
if (deps.indexedDB || deps.dbName) return createBrowserStore(deps);
// `??=` assigns only after resolution succeeds: if a configured factory
// throws, the next call retries it rather than caching the failure.
return (defaultStore ??= (() => {
const configured = resolveConfiguredDocumentStore();
return configured ? withPlainJsonDocumentWrites(configured) : createBrowserStore({});
})());
}