diff --git a/src/localspace.ts b/src/localspace.ts index 6645a2a..5e0b8fc 100644 --- a/src/localspace.ts +++ b/src/localspace.ts @@ -44,6 +44,7 @@ const DefaultDriverOrder = [ DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.LOCALSTORAGE._driver, ]; +const PendingDefaultDriverDefinitions: Record> = {}; const OptionalDriverMethods = [ 'dropInstance', @@ -137,6 +138,30 @@ function callWhenReady( } as ReadyWrappedMethod; } +function defineDefaultDriverOnce( + definer: { defineDriver: (driver: Driver) => Promise }, + driver: Driver +): Promise { + const driverName = driver._driver; + + if (DefinedDrivers[driverName]) { + return Promise.resolve(); + } + + const pendingDefinition = PendingDefaultDriverDefinitions[driverName]; + if (pendingDefinition) { + return pendingDefinition; + } + + const definitionPromise = definer.defineDriver(driver).finally(() => { + if (PendingDefaultDriverDefinitions[driverName] === definitionPromise) { + delete PendingDefaultDriverDefinitions[driverName]; + } + }); + PendingDefaultDriverDefinitions[driverName] = definitionPromise; + return definitionPromise; +} + export class LocalSpace implements LocalSpaceInstance { readonly INDEXEDDB = 'asyncStorage'; readonly LOCALSTORAGE = 'localStorageWrapper'; @@ -169,16 +194,14 @@ export class LocalSpace implements LocalSpaceInstance { const driverName = driver._driver; (this as unknown as Record)[driverTypeKey] = driverName; - if (!DefinedDrivers[driverName]) { - driverInitializationPromises.push( - this.defineDriver(driver).catch((error) => { - console.warn( - `Failed to define LocalSpace driver "${driverName}"`, - error - ); - }) - ); - } + driverInitializationPromises.push( + defineDefaultDriverOnce(this, driver).catch((error) => { + console.warn( + `Failed to define LocalSpace driver "${driverName}"`, + error + ); + }) + ); } } diff --git a/test/driver-initialization.test.ts b/test/driver-initialization.test.ts index 19e8713..65bd097 100644 --- a/test/driver-initialization.test.ts +++ b/test/driver-initialization.test.ts @@ -75,4 +75,38 @@ describe('driver initialization ordering', () => { vi.resetModules(); } }); + + it('does not redefine default drivers when creating instances before singleton ready', async () => { + vi.resetModules(); + + const userAgentSpy = vi + .spyOn(window.navigator, 'userAgent', 'get') + .mockReturnValue('Safari/605.1.15'); + const consoleInfoSpy = vi + .spyOn(console, 'info') + .mockImplementation(() => {}); + + try { + const { default: localspace } = await import('../src/index'); + + const instances = Array.from({ length: 4 }, (_, index) => + localspace.createInstance({ + name: `singleton-race-${index}`, + storeName: `singleton_race_${index}`, + }) + ); + + await Promise.all(instances.map((instance) => instance.ready())); + + const redefineCalls = consoleInfoSpy.mock.calls.filter(([message]) => + typeof message === 'string' && + message.includes('Redefining LocalSpace driver') + ); + expect(redefineCalls).toHaveLength(0); + } finally { + consoleInfoSpy.mockRestore(); + userAgentSpy.mockRestore(); + vi.resetModules(); + } + }); });