merge: 최신 main의 갱신 비용 변경을 tRPC 전송에 통합
# Conflicts: # app/game-api/src/server.ts # app/game-frontend/e2e/mainNavigation.spec.ts # app/game-frontend/src/utils/trpc.ts
This commit is contained in:
@@ -20,9 +20,7 @@ export const resolveDashboardRefreshPlan = (
|
||||
identity: DashboardReadModelIdentity
|
||||
): DashboardRefreshPlan => resolveRealtimeReadModelInvalidation(changes, identity);
|
||||
|
||||
export const resolveDashboardContextBundleInclude = (
|
||||
plan: DashboardRefreshPlan
|
||||
): DashboardContextBundleInclude => ({
|
||||
export const resolveDashboardContextBundleInclude = (plan: DashboardRefreshPlan): DashboardContextBundleInclude => ({
|
||||
context: plan.context,
|
||||
commandTable: plan.commands,
|
||||
boardAccess: plan.boardAccess,
|
||||
@@ -31,12 +29,12 @@ export const resolveDashboardContextBundleInclude = (
|
||||
type TimerHandle = ReturnType<typeof setTimeout>;
|
||||
|
||||
export interface MergedReadModelRefreshQueue {
|
||||
request(invalidation: RealtimeReadModelInvalidation): void;
|
||||
request(invalidation: RealtimeReadModelInvalidation, refreshGrant: string): void;
|
||||
cancelPending(): void;
|
||||
}
|
||||
|
||||
export const createMergedReadModelRefreshQueue = (
|
||||
refresh: (invalidation: RealtimeReadModelInvalidation) => Promise<void>,
|
||||
refresh: (invalidation: RealtimeReadModelInvalidation, refreshGrant: string) => Promise<void>,
|
||||
options: {
|
||||
minIntervalMs?: number;
|
||||
now?: () => number;
|
||||
@@ -49,6 +47,7 @@ export const createMergedReadModelRefreshQueue = (
|
||||
const setTimer = options.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
||||
const clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));
|
||||
let pending = createEmptyRealtimeReadModelInvalidation();
|
||||
let pendingRefreshGrant = '';
|
||||
let hasPending = false;
|
||||
let running = false;
|
||||
let timer: TimerHandle | null = null;
|
||||
@@ -65,11 +64,13 @@ export const createMergedReadModelRefreshQueue = (
|
||||
return;
|
||||
}
|
||||
const next = pending;
|
||||
const nextRefreshGrant = pendingRefreshGrant;
|
||||
pending = createEmptyRealtimeReadModelInvalidation();
|
||||
pendingRefreshGrant = '';
|
||||
hasPending = false;
|
||||
running = true;
|
||||
lastStartedAt = now();
|
||||
void refresh(next).finally(() => {
|
||||
void refresh(next, nextRefreshGrant).finally(() => {
|
||||
running = false;
|
||||
schedule();
|
||||
});
|
||||
@@ -77,14 +78,16 @@ export const createMergedReadModelRefreshQueue = (
|
||||
};
|
||||
|
||||
return {
|
||||
request: (invalidation) => {
|
||||
request: (invalidation, refreshGrant) => {
|
||||
pending = hasPending ? mergeRealtimeReadModelInvalidations(pending, invalidation) : invalidation;
|
||||
pendingRefreshGrant = refreshGrant;
|
||||
hasPending = true;
|
||||
schedule();
|
||||
},
|
||||
cancelPending: () => {
|
||||
hasPending = false;
|
||||
pending = createEmptyRealtimeReadModelInvalidation();
|
||||
pendingRefreshGrant = '';
|
||||
if (timer !== null) {
|
||||
clearTimer(timer);
|
||||
timer = null;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
export const REALTIME_ACCESS_GRANT_CONTEXT_KEY = 'realtimeAccessGrant';
|
||||
|
||||
export const createRealtimeRequestOptions = (refreshGrant: string | null | undefined) =>
|
||||
refreshGrant
|
||||
? {
|
||||
context: {
|
||||
[REALTIME_ACCESS_GRANT_CONTEXT_KEY]: refreshGrant,
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
|
||||
export const resolveBatchRealtimeAccessGrant = (
|
||||
operations: ReadonlyArray<{ context: Record<string, unknown> }>
|
||||
): string | undefined => {
|
||||
if (operations.length === 0) return undefined;
|
||||
const first = operations[0]?.context[REALTIME_ACCESS_GRANT_CONTEXT_KEY];
|
||||
if (typeof first !== 'string' || first.length === 0) return undefined;
|
||||
return operations.every((operation) => operation.context[REALTIME_ACCESS_GRANT_CONTEXT_KEY] === first)
|
||||
? first
|
||||
: undefined;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { trpcJsonBodyHttpClientOptions } from '@sammo-ts/common';
|
||||
import { REALTIME_ACCESS_GRANT_HEADER, trpcJsonBodyHttpClientOptions } from '@sammo-ts/common';
|
||||
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||
import type { AppRouter } from '@sammo-ts/game-api';
|
||||
import { resolveBatchRealtimeAccessGrant } from './realtimeAccessGrant';
|
||||
|
||||
const getGameToken = (): string | null => {
|
||||
if (typeof window === 'undefined') {
|
||||
@@ -15,9 +16,13 @@ export const trpc = createTRPCProxyClient<AppRouter>({
|
||||
httpBatchLink({
|
||||
url: import.meta.env.VITE_GAME_API_URL ?? '/api/trpc',
|
||||
...trpcJsonBodyHttpClientOptions,
|
||||
headers() {
|
||||
headers({ opList }) {
|
||||
const token = getGameToken();
|
||||
return token ? { authorization: `Bearer ${token}` } : {};
|
||||
const refreshGrant = resolveBatchRealtimeAccessGrant(opList);
|
||||
return {
|
||||
...(token ? { authorization: `Bearer ${token}` } : {}),
|
||||
...(refreshGrant ? { [REALTIME_ACCESS_GRANT_HEADER]: refreshGrant } : {}),
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user