Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions scripts/lint/client-bundle-baseline.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"note": "Known server modules reachable from a browser entrypoint (#3670). Burn down, never grow. Regenerate with: deno run --allow-read --allow-write scripts/lint/audit-client-bundle.ts --update",
"entrypoints": {
"src/index.client.ts": [
"src/platform/adapters/fs/veryfront/request-context.ts"
]
"src/index.client.ts": []
}
}
8 changes: 4 additions & 4 deletions src/config/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { getHostEnv } from "#veryfront/platform/compat/process/env.ts";
import { LRUCache } from "#veryfront/utils/lru-wrapper.ts";
import { registerLRUCache } from "#veryfront/cache/registry.ts";
import { VERYFRONT_CONFIG_FILES } from "./config-files.ts";
import { getCurrentRequestContext } from "#veryfront/platform/adapters/fs/veryfront/request-context.ts";
import { currentRequestContext } from "#veryfront/platform/request-context-access.ts";
import type { ModuleLexer } from "#veryfront/extensions/bundler/module-lexer.ts";
import { tryResolve as tryResolveContract } from "#veryfront/extensions/contracts.ts";
import { importFirstPartyExtensionModule } from "#veryfront/extensions/first-party-import.ts";
Expand Down Expand Up @@ -1935,7 +1935,7 @@ interface InternalGetConfigOptions extends GetConfigOptions {
}

function getVirtualConfigSourceContext(): VirtualConfigSourceContext | undefined {
const source = getCurrentRequestContext();
const source = currentRequestContext();
if (!source) return undefined;

return {
Expand Down Expand Up @@ -2026,7 +2026,7 @@ function assertMatchingVirtualConfigSource(

function assertMatchingHostedProjectIdentity(
cacheKey: string,
actual: ReturnType<typeof getCurrentRequestContext>,
actual: ReturnType<typeof currentRequestContext>,
): void {
if (!actual) {
throw CACHE_INVARIANT_VIOLATION.create({
Expand Down Expand Up @@ -2144,7 +2144,7 @@ function getConfigInternal(
assertMatchingVirtualConfigSource(options.sourceContext, ambientSourceContext);
}
if (hostedMultiProjectFilesystem) {
assertMatchingHostedProjectIdentity(options!.cacheKey!, getCurrentRequestContext());
assertMatchingHostedProjectIdentity(options!.cacheKey!, currentRequestContext());
}
const sourceContext = hasQualifiedCacheIdentity
? options.sourceContext ?? ambientSourceContext
Expand Down
6 changes: 6 additions & 0 deletions src/platform/adapters/fs/veryfront/request-context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { AsyncLocalStorage } from "node:async_hooks";

import { registerRequestContextAccessor } from "#veryfront/platform/request-context-access.ts";

export interface RequestContext {
projectSlug: string;
projectId?: string;
Expand All @@ -25,6 +27,10 @@ export function getCurrentRequestContext(): RequestContext | null {
return asyncLocalStorage.getStore() ?? null;
}

// Shared client/server code reads the context through the client-safe holder;
// loading this module is what makes the real accessor available there.
registerRequestContextAccessor(getCurrentRequestContext);

/**
* Wraps a callback to preserve the current AsyncLocalStorage context.
*
Expand Down
35 changes: 35 additions & 0 deletions src/platform/request-context-access.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Client-safe access to the hosted request context.
*
* The real context lives in an AsyncLocalStorage inside
* `adapters/fs/veryfront/request-context.ts`, whose module-scope
* `node:async_hooks` import must stay out of browser bundles. Shared
* client/server code (the config loader's hosted-identity assertions) reads
* the context through this holder instead: the server module registers its
* accessor when it loads, and in the browser nothing ever registers, so
* `currentRequestContext()` returns null — the correct answer there, since a
* hosted request context only exists while the server VFS adapter runs a
* request.
*
* Registration cannot be observed "too early": the only writer of the
* context (`multi-project-adapter.ts`'s `asyncLocalStorage.run`) imports the
* server module, so any populated context implies the accessor is in place.
*/

import type { RequestContext } from "#veryfront/platform/adapters/fs/veryfront/request-context.ts";

export type { RequestContext };

let accessor: (() => RequestContext | null) | undefined;

/** Called by the server request-context module when it loads. */
export function registerRequestContextAccessor(
fn: () => RequestContext | null,
): void {
accessor = fn;
}

/** The current hosted request context, or null outside a server request. */
export function currentRequestContext(): RequestContext | null {
return accessor?.() ?? null;
}