Extract storage defaults registry

master
dev 5 months ago
parent e6333d4c82
commit aa8a7403a5

@ -15,6 +15,7 @@ Estado al cierre:
- `arts/perm/client.ts` delega transporte HTTP/JSON en `client-http.ts`; el cliente queda centrado en cache, snapshot y fallback.
- `arts/timr/engine-timers.ts` delega `TimerHandle` cancel/reschedule en `timer-handle.ts`.
- `arts/stor/engine-storage.ts` delega IDs de adapter, bus keys y suscripciones cross-tab en `adapter-registry.ts`.
- `arts/stor/engine-storage.ts` delega el registro de defaults conflictivos en `defaults-registry.ts`.
- Integracion total ampliada: `Auth.signOut()` valida anonimizacion, invalidacion de `Permissions` y evento `Cache.invalidate`.
- Tanda focalizada verde: `npx vitest run src/arts/conn src/libs/cach src/arts/cach src/svrs/cach src/arts/auth src/svrs/auth src/libs/auth src/arts/aapp/test/ecosystem.integration.test.ts` -> 19 archivos, 82 tests.
- `/test/ecosystem` revisado en navegador: carga sin errores de consola, `ar` cambia a `rtl`, Formats se actualiza por locale, Perm cambia con rol `viewer`, Cach re-scopea por locale y Conn loopback publica/recibe.

@ -0,0 +1,53 @@
import { STORAGE_OP_READ } from './consts';
import { STORAGE_ERRORS } from './errors';
import type { StorageErrorContext } from './types';
export interface RegisterStorageDefaultInput {
readonly registryKey: string;
readonly key: string;
readonly fullKey: string;
readonly initialDefault: unknown;
readonly adapterName: string;
report(ctx: Omit<StorageErrorContext, 'adapter'>, adapterName: string): void;
}
export interface StorageDefaultsRegistry {
register(input: RegisterStorageDefaultInput): () => void;
}
export function createStorageDefaultsRegistry(): StorageDefaultsRegistry {
const defaults = new Map<string, { value: unknown; refs: number }>();
function register(input: RegisterStorageDefaultInput): () => void {
const previous = defaults.get(input.registryKey);
if (previous !== undefined) {
try {
if (JSON.stringify(previous.value) !== JSON.stringify(input.initialDefault)) {
input.report(
{
key: input.key,
fullKey: input.fullKey,
op: STORAGE_OP_READ,
error: new Error(STORAGE_ERRORS.ENTRY_DEFAULTS_MISMATCH(input.key))
},
input.adapterName
);
}
} catch {
/* circular default — skip the diagnostic */
}
previous.refs += 1;
} else {
defaults.set(input.registryKey, { value: input.initialDefault, refs: 1 });
}
return () => {
const slot = defaults.get(input.registryKey);
if (slot === undefined) return;
slot.refs -= 1;
if (slot.refs <= 0) defaults.delete(input.registryKey);
};
}
return { register };
}

@ -1,5 +1,6 @@
import { createMemoryAdapter } from './adapters/memory';
import { createStorageAdapterRegistry } from './adapter-registry';
import { createStorageDefaultsRegistry } from './defaults-registry';
import {
DEFAULT_VERSION,
ENTRY_CHANGE_SOURCE_LOCAL,
@ -53,12 +54,7 @@ export function createEngineStorage(options: EngineStorageOptions = {}): EngineS
const onError = options.onError;
const bus: EntryBus = createEntryBus();
const adapterRegistry = createStorageAdapterRegistry(bus);
// First-write-wins registry for conflict diagnostics. Refcounted so the
// entry is forgotten once every live registration is disposed — avoids
// false positives when a key is re-created with a different default
// after the previous owner cleaned up.
const registeredDefaults = new Map<string, { value: unknown; refs: number }>();
const defaultsRegistry = createStorageDefaultsRegistry();
const entryDisposers = new Set<() => void>();
// Track every entry created so `clear()` can wipe only what this storage
// owns and notify the bus uniformly. `key` is the original (pre-namespace)
@ -114,27 +110,14 @@ export function createEngineStorage(options: EngineStorageOptions = {}): EngineS
// it is disposed; otherwise re-creating the same key with a different
// default would emit a stale warning even when nobody owns it anymore.
const registryKey = adapterRegistry.defaultsKeyFor(entryAdapter, fullKey);
const previous = registeredDefaults.get(registryKey);
if (previous !== undefined) {
try {
if (JSON.stringify(previous.value) !== JSON.stringify(initialDefault)) {
report(
{
key,
fullKey,
op: STORAGE_OP_READ,
error: new Error(STORAGE_ERRORS.ENTRY_DEFAULTS_MISMATCH(key))
},
entryAdapter.name
);
}
} catch {
/* circular default — skip the diagnostic */
}
previous.refs += 1;
} else {
registeredDefaults.set(registryKey, { value: initialDefault, refs: 1 });
}
const unregisterDefault = defaultsRegistry.register({
registryKey,
key,
fullKey,
initialDefault,
adapterName: entryAdapter.name,
report
});
const isRaw = entryOptions.raw === true;
const version = isRaw ? DEFAULT_VERSION : (entryOptions.version ?? DEFAULT_VERSION);
@ -320,11 +303,7 @@ export function createEngineStorage(options: EngineStorageOptions = {}): EngineS
registeredEntries.add(registration);
const dispose = (): void => {
registeredEntries.delete(registration);
const slot = registeredDefaults.get(registryKey);
if (slot !== undefined) {
slot.refs -= 1;
if (slot.refs <= 0) registeredDefaults.delete(registryKey);
}
unregisterDefault();
localDisposers.splice(0).forEach((fn) => fn());
};
entryDisposers.add(dispose);

Loading…
Cancel
Save

Powered by TurnKey Linux.