@@ -7,6 +7,7 @@ import { RandomBytesDep, SymmetricCryptoDecryptError } from "../Crypto.js";
77import { eqArrayNumber } from "../Eq.js" ;
88import { TransferableError } from "../Error.js" ;
99import { exhaustiveCheck } from "../Function.js" ;
10+ import { createMultiton , Multiton } from "../Multiton.js" ;
1011import { err , ok , Result } from "../Result.js" ;
1112import { isSqlMutation , SafeSql , SqliteError , SqliteQuery } from "../Sqlite.js" ;
1213import { createStore , StoreSubscribe } from "../Store.js" ;
@@ -19,6 +20,7 @@ import {
1920 InferType ,
2021 Mnemonic ,
2122 ObjectType ,
23+ SimpleName ,
2224 ValidMutationSize ,
2325 ValidMutationSizeError ,
2426} from "../Type.js" ;
@@ -92,7 +94,7 @@ export interface EvoluConfig extends Partial<DbConfig> {
9294 readonly reloadUrl ?: string ;
9395}
9496
95- export interface Evolu < S extends EvoluSchema = EvoluSchema > {
97+ export interface Evolu < S extends EvoluSchema = EvoluSchema > extends Disposable {
9698 /**
9799 * Subscribe to {@link EvoluError} changes.
98100 *
@@ -457,8 +459,12 @@ export type EvoluDeps = ConsoleDep &
457459 ReloadAppDep &
458460 TimeDep ;
459461
460- const evoluInstances = new Map < string , InternalEvoluInstance > ( ) ;
462+ const evoluInstances = createMultiton < SimpleName , InternalEvoluInstance > ( ) ;
461463
464+ /**
465+ * Unique identifier for the current browser tab or app instance, lazily
466+ * initialized on first use to distinguish between multiple tabs.
467+ */
462468let tabId : Id | null = null ;
463469
464470/**
@@ -496,35 +502,25 @@ let tabId: Id | null = null;
496502 *
497503 * ### Instance Caching
498504 *
499- * Evolu caches instances by {@link EvoluConfig} name to enable hot reloading and
500- * multitenancy. Multiple calls to `createEvolu` with the same name return the
501- * same instance, preserving database connections and state across module
502- * reloads during development. This ensures a seamless developer experience
503- * where edits don't interrupt ongoing sync or lose in-memory state.
504- *
505- * For testing, either dispose of instances after each test (TODO: implement
506- * dispose method) or use unique instance names to ensure proper isolation
507- * between test cases.
505+ * `createEvolu` caches instances using {@link Multiton} by {@link EvoluConfig}
506+ * name to enable hot reloading and prevent database corruption from multiple
507+ * connections. For testing, use unique instance names to ensure proper
508+ * isolation.
508509 */
509510export const createEvolu =
510511 ( deps : EvoluDeps ) =>
511512 < S extends EvoluSchema > (
512513 schema : ValidateSchema < S > extends never ? S : ValidateSchema < S > ,
513514 config ?: EvoluConfig ,
514- ) : Evolu < S > => {
515- const name = config ?. name ?? defaultDbConfig . name ;
516- let evolu = evoluInstances . get ( name ) ;
517-
518- if ( evolu == null ) {
519- evolu = createEvoluInstance ( deps ) ( schema as EvoluSchema , config ) ;
520- evoluInstances . set ( name , evolu ) ;
521- } else {
522- // Hot reloading. Note that indexes are intentionally omitted.
523- evolu . ensureSchema ( schema as EvoluSchema ) ;
524- }
525-
526- return evolu as Evolu < S > ;
527- } ;
515+ ) : Evolu < S > =>
516+ evoluInstances . ensure (
517+ config ?. name ?? defaultDbConfig . name ,
518+ ( ) => createEvoluInstance ( deps ) ( schema as EvoluSchema , config ) ,
519+ ( evolu ) => {
520+ // Hot reloading. Note that indexes are intentionally omitted.
521+ evolu . ensureSchema ( schema as EvoluSchema ) ;
522+ } ,
523+ ) as Evolu < S > ;
528524
529525const createEvoluInstance =
530526 ( deps : EvoluDeps ) =>
@@ -943,6 +939,11 @@ const createEvoluInstance =
943939
944940 return unuse ;
945941 } ,
942+
943+ /** Disposal is not implemented yet. */
944+ [ Symbol . dispose ] : ( ) => {
945+ throw new Error ( "Evolu instance disposal is not yet implemented" ) ;
946+ } ,
946947 } ;
947948
948949 return evolu ;
0 commit comments