fix(server): enforce server-only usage for runtime config resolvers

Add explicit runtime checks to prevent runtime config functions from being called on the client. This guards against accidental client-side invocation by throwing errors if executed outside the server environment.
This commit is contained in:
Richard R 2026-05-14 13:07:43 -06:00
parent fb39723089
commit ca4228970b
2 changed files with 11 additions and 3 deletions

View file

@ -1,4 +1,3 @@
import 'server-only';
import { cache } from 'react'; import { cache } from 'react';
import { getResolvedRuntimeConfig } from '@/lib/server/runtime-config'; import { getResolvedRuntimeConfig } from '@/lib/server/runtime-config';
@ -8,6 +7,8 @@ import { getResolvedRuntimeConfig } from '@/lib/server/runtime-config';
* (e.g. layout + page) need the same config in one render. * (e.g. layout + page) need the same config in one render.
*/ */
export const getResolvedRuntimeConfigForRsc = cache(async () => { export const getResolvedRuntimeConfigForRsc = cache(async () => {
if (typeof window !== 'undefined') {
throw new Error('getResolvedRuntimeConfigForRsc must be called on the server');
}
return getResolvedRuntimeConfig(); return getResolvedRuntimeConfig();
}); });

View file

@ -1,4 +1,3 @@
import 'server-only';
import { ensureAdminSeed } from '@/lib/server/admin/seed'; import { ensureAdminSeed } from '@/lib/server/admin/seed';
import { import {
getRuntimeConfig, getRuntimeConfig,
@ -10,12 +9,19 @@ import {
export type ResolvedRuntimeConfig = RuntimeConfig; export type ResolvedRuntimeConfig = RuntimeConfig;
function assertServerRuntime(caller: string): void {
if (typeof window !== 'undefined') {
throw new Error(`${caller} must be called on the server`);
}
}
/** /**
* Returns the resolved site-wide runtime config in the shape consumed by * Returns the resolved site-wide runtime config in the shape consumed by
* the client via SSR injection. Triggers the boot-time seed on first call * the client via SSR injection. Triggers the boot-time seed on first call
* so env values land in the DB before the first read. * so env values land in the DB before the first read.
*/ */
export async function getResolvedRuntimeConfig(): Promise<ResolvedRuntimeConfig> { export async function getResolvedRuntimeConfig(): Promise<ResolvedRuntimeConfig> {
assertServerRuntime('getResolvedRuntimeConfig');
await ensureAdminSeed(); await ensureAdminSeed();
return getRuntimeConfig(); return getRuntimeConfig();
} }
@ -24,6 +30,7 @@ export async function getResolvedRuntimeConfigWithSources(): Promise<{
values: RuntimeConfig; values: RuntimeConfig;
sources: Record<RuntimeConfigKey, RuntimeConfigSource | 'default'>; sources: Record<RuntimeConfigKey, RuntimeConfigSource | 'default'>;
}> { }> {
assertServerRuntime('getResolvedRuntimeConfigWithSources');
await ensureAdminSeed(); await ensureAdminSeed();
return getRuntimeConfigWithSources(); return getRuntimeConfigWithSources();
} }