diff --git a/index.ts b/index.ts index f9cfe29b..5871791c 100644 --- a/index.ts +++ b/index.ts @@ -1675,6 +1675,21 @@ const pluginVersion = getPluginVersion(); // hook/tool registration for the new API instance" regression that rwmjhb identified. let _registeredApis = new WeakSet(); +// Dual-track registration: alongside WeakSet (GC-safe), use a Map for explicit +// rollback tracking and test inspection. WeakSet handles GC safety; Map provides +// manual clearability and _getRegisteredApisForTest() export. +// Track: _registeredApisMap (explicit claim/rollback) + _registeredApis (WeakSet guard) +let _registeredApisMap = new Map(); + +/** + * Returns the internal registration Map — for unit test inspection only. + * Do NOT mutate from outside the plugin. + * @public (test API) + */ +export function _getRegisteredApisForTest(): Map { + return _registeredApisMap; +} + // ============================================================================ // Hook Event Deduplication (Phase 1) // ============================================================================ @@ -1930,21 +1945,24 @@ const memoryLanceDBProPlugin = { // - Memory heap growth from repeated resource creation (~9 calls/process) // - Accumulated session Maps being lost on re-registration // - // IMPORTANT: _registeredApis.add(api) is called AFTER successful init. - // This ensures that if _initPluginState throws, the api is NOT in the - // WeakSet, allowing a subsequent register() call with the same api to retry. - // (The old placement — before init — caused permanent breakage on init failure.) + // Dual-track claim: we record registration BEFORE attempting init so that + // if init fails, we can explicitly roll back the Map entry — enabling a + // subsequent register() retry with the same API object. + // - _registeredApis (WeakSet): GC-safe singleton guard (Phase 2 guard) + // - _registeredApisMap (Map): explicit claim/rollback for test inspection // ======================================================================== + _registeredApis.add(api); // claim before init (Phase 2 singleton guard) + _registeredApisMap.set(api, true); // dual-track: explicit claim for rollback let singleton: typeof _singletonState; try { if (!_singletonState) { _singletonState = _initPluginState(api); } singleton = _singletonState; } catch (err) { api.logger.error(`memory-lancedb-pro: _initPluginState failed — ${String(err)}`); + _registeredApis.delete(api); // dual-track rollback: WeakSet un-claim + _registeredApisMap.delete(api); // dual-track rollback: Map un-claim throw err; } - _registeredApis.add(api); - const { config, resolvedDbPath, @@ -4326,6 +4344,7 @@ export { getDefaultMdMirrorDir }; */ export function resetRegistration() { _registeredApis = new WeakSet(); + _registeredApisMap.clear(); // dual-track: clear Map alongside WeakSet _singletonState = null; _hookEventDedup.clear(); }