feat: 기능 추가 과정중
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import type { AuthSessionPort } from "../../application/ports/auth-session-port.ts";
|
||||
import type { QueryInvalidationCoordinator } from "../../contracts/query-invalidation.ts";
|
||||
import {
|
||||
createRuntimeIdentityRegistry,
|
||||
type RuntimeIdentityRegistry,
|
||||
} from "../../contracts/query-keys.ts";
|
||||
import type {
|
||||
CacheScopeSnapshot,
|
||||
ServerStateScopeRuntime,
|
||||
} from "../../contracts/server-state-scope.ts";
|
||||
|
||||
export function createServerStateScopeRuntime(dependencies: Readonly<{
|
||||
session: Pick<AuthSessionPort, "subscribe">;
|
||||
queryInvalidation: QueryInvalidationCoordinator;
|
||||
tokenFactory?: () => string;
|
||||
}>): ServerStateScopeRuntime {
|
||||
const listeners = new Set<() => void>();
|
||||
let generation = 1;
|
||||
let identities = newIdentityRegistry(dependencies.tokenFactory);
|
||||
let fingerprint = scopeFingerprint(dependencies.tokenFactory);
|
||||
let disposed = false;
|
||||
let resetChain = Promise.resolve();
|
||||
|
||||
function createSnapshot(): CacheScopeSnapshot {
|
||||
const capturedGeneration = generation;
|
||||
const capturedIdentities = identities;
|
||||
return Object.freeze({
|
||||
generation: capturedGeneration,
|
||||
fingerprint,
|
||||
identities: capturedIdentities,
|
||||
isCurrent: () =>
|
||||
!disposed &&
|
||||
generation === capturedGeneration &&
|
||||
identities === capturedIdentities,
|
||||
});
|
||||
}
|
||||
let currentSnapshot = createSnapshot();
|
||||
|
||||
const unsubscribe = dependencies.session.subscribe(() => {
|
||||
if (disposed) return;
|
||||
const previousIdentities = identities;
|
||||
const targetGeneration = ++generation;
|
||||
resetChain = resetChain
|
||||
.then(() => dependencies.queryInvalidation.resetLocal())
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
previousIdentities.close();
|
||||
if (disposed || generation !== targetGeneration) return;
|
||||
identities = newIdentityRegistry(dependencies.tokenFactory);
|
||||
fingerprint = scopeFingerprint(dependencies.tokenFactory);
|
||||
currentSnapshot = createSnapshot();
|
||||
for (const listener of listeners) listener();
|
||||
});
|
||||
});
|
||||
|
||||
return Object.freeze({
|
||||
getSnapshot: () => currentSnapshot,
|
||||
subscribe(listener) {
|
||||
listeners.add(listener);
|
||||
return () => listeners.delete(listener);
|
||||
},
|
||||
dispose() {
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
unsubscribe();
|
||||
listeners.clear();
|
||||
identities.close();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function newIdentityRegistry(
|
||||
tokenFactory: (() => string) | undefined,
|
||||
): RuntimeIdentityRegistry {
|
||||
return createRuntimeIdentityRegistry({
|
||||
...(tokenFactory ? { tokenFactory } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
function scopeFingerprint(tokenFactory: (() => string) | undefined): string {
|
||||
const candidate = tokenFactory?.() ?? crypto.randomUUID();
|
||||
if (!/^[A-Za-z0-9._:-]{16,128}$/.test(candidate)) {
|
||||
throw new TypeError("Invalid cache scope fingerprint.");
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
Reference in New Issue
Block a user