@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.4 → 3.9.0-ultramodern.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/dist/cjs/contextExtensions.js +74 -0
  2. package/dist/cjs/headRuntime.js +91 -0
  3. package/dist/cjs/helmetContext.js +54 -0
  4. package/dist/cjs/index.js +18 -4
  5. package/dist/cjs/prefetchPolicy.js +52 -0
  6. package/dist/cjs/request-policy/browser.js +139 -0
  7. package/dist/cjs/request-policy/client.js +234 -0
  8. package/dist/cjs/request-policy/node.js +149 -0
  9. package/dist/cjs/request-policy/policyCore.js +266 -0
  10. package/dist/cjs/request-policy/requestContext.js +137 -0
  11. package/dist/cjs/request-policy/traceparent.js +56 -0
  12. package/dist/cjs/request-policy/transport.js +234 -0
  13. package/dist/cjs/request-policy/types.js +57 -0
  14. package/dist/cjs/routeHydration.js +127 -0
  15. package/dist/cjs/routerPrefetchPolicy.js +153 -0
  16. package/dist/cjs/routerProvider.js +105 -0
  17. package/dist/cjs/routerState.js +167 -0
  18. package/dist/cjs/routerStatePlugin.js +86 -0
  19. package/dist/cjs/routerStateTypes.js +18 -0
  20. package/dist/cjs/runtimeContextProjection.js +112 -0
  21. package/dist/cjs/server-config/bffRuntime.js +18 -0
  22. package/dist/cjs/server-config/index.js +18 -0
  23. package/dist/cjs/server-config/serverTelemetry.js +18 -0
  24. package/dist/cjs/shellStream.js +34 -2
  25. package/dist/cjs/workspaceEvents.js +159 -0
  26. package/dist/esm/contextExtensions.mjs +33 -0
  27. package/dist/esm/headRuntime.mjs +43 -0
  28. package/dist/esm/helmetContext.mjs +13 -0
  29. package/dist/esm/index.mjs +2 -0
  30. package/dist/esm/prefetchPolicy.mjs +11 -0
  31. package/dist/esm/request-policy/browser.mjs +11 -0
  32. package/dist/esm/request-policy/client.mjs +195 -0
  33. package/dist/esm/request-policy/policyCore.mjs +171 -0
  34. package/dist/esm/request-policy/requestContext.mjs +75 -0
  35. package/dist/esm/request-policy/traceparent.mjs +18 -0
  36. package/dist/esm/request-policy/transport.mjs +196 -0
  37. package/dist/esm/request-policy/types.mjs +10 -0
  38. package/dist/esm/routeHydration.mjs +77 -0
  39. package/dist/esm/routerPrefetchPolicy.mjs +115 -0
  40. package/dist/esm/routerProvider.mjs +58 -0
  41. package/dist/esm/routerState.mjs +105 -0
  42. package/dist/esm/routerStatePlugin.mjs +48 -0
  43. package/dist/esm/routerStateTypes.mjs +0 -0
  44. package/dist/esm/runtimeContextProjection.mjs +74 -0
  45. package/dist/esm/server-config/bffRuntime.mjs +0 -0
  46. package/dist/esm/server-config/index.mjs +0 -0
  47. package/dist/esm/server-config/serverTelemetry.mjs +0 -0
  48. package/dist/esm/shellStream.mjs +34 -2
  49. package/dist/esm/workspaceEvents.mjs +66 -0
  50. package/dist/esm-node/contextExtensions.mjs +34 -0
  51. package/dist/esm-node/headRuntime.mjs +44 -0
  52. package/dist/esm-node/helmetContext.mjs +14 -0
  53. package/dist/esm-node/index.mjs +2 -0
  54. package/dist/esm-node/prefetchPolicy.mjs +12 -0
  55. package/dist/esm-node/request-policy/browser.mjs +12 -0
  56. package/dist/esm-node/request-policy/client.mjs +196 -0
  57. package/dist/esm-node/request-policy/node.mjs +22 -0
  58. package/dist/esm-node/request-policy/policyCore.mjs +172 -0
  59. package/dist/esm-node/request-policy/requestContext.mjs +76 -0
  60. package/dist/esm-node/request-policy/traceparent.mjs +19 -0
  61. package/dist/esm-node/request-policy/transport.mjs +197 -0
  62. package/dist/esm-node/request-policy/types.mjs +11 -0
  63. package/dist/esm-node/routeHydration.mjs +78 -0
  64. package/dist/esm-node/routerPrefetchPolicy.mjs +116 -0
  65. package/dist/esm-node/routerProvider.mjs +59 -0
  66. package/dist/esm-node/routerState.mjs +106 -0
  67. package/dist/esm-node/routerStatePlugin.mjs +49 -0
  68. package/dist/esm-node/routerStateTypes.mjs +1 -0
  69. package/dist/esm-node/runtimeContextProjection.mjs +75 -0
  70. package/dist/esm-node/server-config/bffRuntime.mjs +1 -0
  71. package/dist/esm-node/server-config/index.mjs +1 -0
  72. package/dist/esm-node/server-config/serverTelemetry.mjs +1 -0
  73. package/dist/esm-node/shellStream.mjs +34 -2
  74. package/dist/esm-node/workspaceEvents.mjs +67 -0
  75. package/dist/types/contextExtensions.d.ts +34 -0
  76. package/dist/types/headRuntime.d.ts +12 -0
  77. package/dist/types/helmetContext.d.ts +10 -0
  78. package/dist/types/index.d.ts +2 -0
  79. package/dist/types/prefetchPolicy.d.ts +4 -0
  80. package/dist/types/request-policy/browser.d.ts +10 -0
  81. package/dist/types/request-policy/client.d.ts +14 -0
  82. package/dist/types/request-policy/node.d.ts +10 -0
  83. package/dist/types/request-policy/policyCore.d.ts +107 -0
  84. package/dist/types/request-policy/requestContext.d.ts +22 -0
  85. package/dist/types/request-policy/traceparent.d.ts +10 -0
  86. package/dist/types/request-policy/transport.d.ts +12 -0
  87. package/dist/types/request-policy/types.d.ts +144 -0
  88. package/dist/types/routeHydration.d.ts +24 -0
  89. package/dist/types/routerPrefetchPolicy.d.ts +28 -0
  90. package/dist/types/routerProvider.d.ts +55 -0
  91. package/dist/types/routerState.d.ts +12 -0
  92. package/dist/types/routerStatePlugin.d.ts +19 -0
  93. package/dist/types/routerStateTypes.d.ts +40 -0
  94. package/dist/types/runtimeContextProjection.d.ts +30 -0
  95. package/dist/types/server-config/bffRuntime.d.ts +122 -0
  96. package/dist/types/server-config/index.d.ts +2 -0
  97. package/dist/types/server-config/serverTelemetry.d.ts +322 -0
  98. package/dist/types/workspaceEvents.d.ts +62 -0
  99. package/package.json +198 -13
  100. package/rslib.config.mts +17 -2
  101. package/rstest.config.mts +33 -2
  102. package/src/contextExtensions.ts +81 -0
  103. package/src/headRuntime.tsx +77 -0
  104. package/src/helmetContext.ts +31 -0
  105. package/src/index.ts +2 -0
  106. package/src/prefetchPolicy.ts +33 -0
  107. package/src/request-policy/browser.ts +22 -0
  108. package/src/request-policy/client.ts +404 -0
  109. package/src/request-policy/node.ts +41 -0
  110. package/src/request-policy/policyCore.ts +424 -0
  111. package/src/request-policy/requestContext.ts +149 -0
  112. package/src/request-policy/traceparent.ts +43 -0
  113. package/src/request-policy/transport.ts +349 -0
  114. package/src/request-policy/types.ts +229 -0
  115. package/src/routeHydration.ts +208 -0
  116. package/src/routerPrefetchPolicy.ts +195 -0
  117. package/src/routerProvider.ts +160 -0
  118. package/src/routerState.ts +208 -0
  119. package/src/routerStatePlugin.ts +86 -0
  120. package/src/routerStateTypes.ts +46 -0
  121. package/src/runtimeContextProjection.ts +105 -0
  122. package/src/server-config/bffRuntime.ts +136 -0
  123. package/src/server-config/index.ts +2 -0
  124. package/src/server-config/serverTelemetry.ts +337 -0
  125. package/src/shellStream.ts +55 -25
  126. package/src/workspaceEvents.ts +324 -0
  127. package/tests/buildIdentity.test.ts +2 -4
  128. package/tests/contextExtensions.test.ts +71 -0
  129. package/tests/headRuntime.test.tsx +369 -0
  130. package/tests/nativeSafeFailureCompatibility.test.ts +117 -0
  131. package/tests/prefetchPolicy.test.ts +51 -0
  132. package/tests/rendererHead.test.ts +1 -72
  133. package/tests/request-policy/browser-policy.test.ts +608 -0
  134. package/tests/request-policy/client-isolation.test.ts +149 -0
  135. package/tests/request-policy/identityBinding.test.ts +75 -0
  136. package/tests/request-policy/node-policy.test.ts +741 -0
  137. package/tests/request-policy/policyCore.test.ts +250 -0
  138. package/tests/request-policy/requestContext.test.ts +80 -0
  139. package/tests/request-policy/requestFactory.outbound-contract.test.ts +691 -0
  140. package/tests/request-policy/traceparent.test.ts +55 -0
  141. package/tests/request-policy/transport-retry.test.ts +370 -0
  142. package/tests/routeHydration.test.ts +180 -0
  143. package/tests/routerPrefetchPolicy.test.ts +102 -0
  144. package/tests/routerProvider.test.ts +29 -0
  145. package/tests/routerState.test.ts +157 -0
  146. package/tests/routerStatePlugin.test.ts +108 -0
  147. package/tests/runtimeContextProjection.test.ts +135 -0
  148. package/tests/shellStream.test.ts +37 -0
  149. package/tests/workspaceEvents.test.ts +391 -0
  150. package/dist/cjs/rscHtmlStream.js +0 -128
  151. package/dist/esm/rscHtmlStream.mjs +0 -86
  152. package/dist/esm-node/rscHtmlStream.mjs +0 -87
  153. package/dist/types/rscHtmlStream.d.ts +0 -4
  154. package/src/rscHtmlStream.ts +0 -142
  155. package/tests/rscHtmlStream.test.ts +0 -153
@@ -0,0 +1,208 @@
1
+ // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
+ import { createRuntimeContextExtension } from './contextExtensions';
3
+ import type {
4
+ InternalRouterRuntimeState,
5
+ InternalRouterServerSnapshot,
6
+ RouterRouteMatchSnapshot,
7
+ RouterServerPrepareResult,
8
+ } from './routerStateTypes';
9
+
10
+ export type {
11
+ BuiltInRouterFramework,
12
+ InternalRouterRuntimeState,
13
+ InternalRouterServerSnapshot,
14
+ RouterFramework,
15
+ RouterLifecyclePhase,
16
+ RouterRouteMatchSnapshot,
17
+ RouterServerPrepareResult,
18
+ } from './routerStateTypes';
19
+
20
+ /**
21
+ * Router runtime state is shared by every router provider (react-router,
22
+ * @modern-js/plugin-tanstack, ...) and consumed by the SSR pipeline. It lives
23
+ * in the runtime-context extension slot instead of ad-hoc fields on
24
+ * `TInternalRuntimeContext`.
25
+ */
26
+ const routerRuntimeStateExtension =
27
+ createRuntimeContextExtension<InternalRouterRuntimeState>(
28
+ '@modern-js/runtime:router-runtime-state',
29
+ );
30
+
31
+ /**
32
+ * The server snapshot is tracked separately: a later
33
+ * `applyRouterRuntimeState` call without a snapshot must not clear a
34
+ * previously captured one.
35
+ */
36
+ const routerServerSnapshotExtension =
37
+ createRuntimeContextExtension<InternalRouterServerSnapshot>(
38
+ '@modern-js/runtime:router-server-snapshot',
39
+ );
40
+
41
+ export function getRouterRuntimeState(
42
+ runtimeContext: object,
43
+ ): InternalRouterRuntimeState | undefined {
44
+ return routerRuntimeStateExtension.get(runtimeContext);
45
+ }
46
+
47
+ export function getRouterServerSnapshot(
48
+ runtimeContext: object,
49
+ ): InternalRouterServerSnapshot | undefined {
50
+ return routerServerSnapshotExtension.get(runtimeContext);
51
+ }
52
+
53
+ type RouterSnapshotLike = Partial<InternalRouterServerSnapshot>;
54
+
55
+ function toHydrationScripts(state: {
56
+ hydrationScript?: string;
57
+ hydrationScripts?: string[];
58
+ }) {
59
+ if (state.hydrationScripts?.length) {
60
+ return state.hydrationScripts;
61
+ }
62
+
63
+ return state.hydrationScript ? [state.hydrationScript] : undefined;
64
+ }
65
+
66
+ function getMatchedRouteIdsFromMatches(matches?: RouterRouteMatchSnapshot[]) {
67
+ const routeIds = matches
68
+ ?.map(match => match.assetRouteId ?? match.routeId)
69
+ .filter((routeId): routeId is string => typeof routeId === 'string');
70
+
71
+ return routeIds?.length ? routeIds : undefined;
72
+ }
73
+
74
+ export function createRouterServerSnapshot(
75
+ state: RouterSnapshotLike,
76
+ ): InternalRouterServerSnapshot {
77
+ const hydrationScripts = toHydrationScripts(state);
78
+ const matchedRouteIds =
79
+ state.matchedRouteIds ?? getMatchedRouteIdsFromMatches(state.matches);
80
+
81
+ return {
82
+ ...state,
83
+ ...(hydrationScripts?.length
84
+ ? {
85
+ hydrationScript: state.hydrationScript ?? hydrationScripts[0],
86
+ hydrationScripts,
87
+ }
88
+ : {}),
89
+ ...(matchedRouteIds ? { matchedRouteIds } : {}),
90
+ };
91
+ }
92
+
93
+ export function createRouterRuntimeState(
94
+ state: InternalRouterRuntimeState,
95
+ ): InternalRouterRuntimeState {
96
+ const hasSnapshotState =
97
+ Boolean(state.serverSnapshot) ||
98
+ Boolean(state.hydrationScript) ||
99
+ Boolean(state.hydrationScripts?.length) ||
100
+ Boolean(state.matchedRouteIds?.length) ||
101
+ Boolean(state.matches?.length);
102
+ const serverSnapshot = state.serverSnapshot
103
+ ? createRouterServerSnapshot({
104
+ ...state.serverSnapshot,
105
+ framework: state.serverSnapshot.framework ?? state.framework,
106
+ basename: state.serverSnapshot.basename ?? state.basename,
107
+ hydrationScript:
108
+ state.serverSnapshot.hydrationScript ?? state.hydrationScript,
109
+ hydrationScripts:
110
+ state.serverSnapshot.hydrationScripts ?? state.hydrationScripts,
111
+ matchedRouteIds:
112
+ state.serverSnapshot.matchedRouteIds ?? state.matchedRouteIds,
113
+ matches: state.serverSnapshot.matches ?? state.matches,
114
+ })
115
+ : hasSnapshotState
116
+ ? createRouterServerSnapshot({
117
+ framework: state.framework,
118
+ basename: state.basename,
119
+ hydrationScript: state.hydrationScript,
120
+ hydrationScripts: state.hydrationScripts,
121
+ matchedRouteIds: state.matchedRouteIds,
122
+ matches: state.matches,
123
+ })
124
+ : undefined;
125
+ const hydrationScripts = toHydrationScripts({
126
+ hydrationScript: state.hydrationScript ?? serverSnapshot?.hydrationScript,
127
+ hydrationScripts:
128
+ state.hydrationScripts ?? serverSnapshot?.hydrationScripts,
129
+ });
130
+ const matchedRouteIds =
131
+ state.matchedRouteIds ??
132
+ serverSnapshot?.matchedRouteIds ??
133
+ getMatchedRouteIdsFromMatches(state.matches);
134
+
135
+ return {
136
+ ...state,
137
+ ...(hydrationScripts?.length
138
+ ? {
139
+ hydrationScript: state.hydrationScript ?? hydrationScripts[0],
140
+ hydrationScripts,
141
+ }
142
+ : {}),
143
+ ...(matchedRouteIds ? { matchedRouteIds } : {}),
144
+ ...(serverSnapshot ? { serverSnapshot } : {}),
145
+ };
146
+ }
147
+
148
+ export function applyRouterRuntimeState<Context extends object>(
149
+ runtimeContext: Context,
150
+ state: InternalRouterRuntimeState,
151
+ ) {
152
+ const normalized = createRouterRuntimeState(state);
153
+ routerRuntimeStateExtension.set(runtimeContext, normalized);
154
+ if (normalized.serverSnapshot) {
155
+ routerServerSnapshotExtension.set(
156
+ runtimeContext,
157
+ normalized.serverSnapshot,
158
+ );
159
+ }
160
+
161
+ return runtimeContext;
162
+ }
163
+
164
+ export function applyRouterServerPrepareResult<Context extends object>(
165
+ runtimeContext: Context,
166
+ result: RouterServerPrepareResult,
167
+ ) {
168
+ const state = createRouterRuntimeState({
169
+ ...result.state,
170
+ cleanup: result.cleanup ?? result.state.cleanup,
171
+ serverSnapshot: result.snapshot ?? result.state.serverSnapshot,
172
+ });
173
+ applyRouterRuntimeState(runtimeContext, state);
174
+ return runtimeContext;
175
+ }
176
+
177
+ export function getRouterHydrationScripts(runtimeContext: object) {
178
+ const serverSnapshot = getRouterServerSnapshot(runtimeContext);
179
+ const runtimeState = getRouterRuntimeState(runtimeContext);
180
+ return (
181
+ serverSnapshot?.hydrationScripts ??
182
+ toHydrationScripts({
183
+ hydrationScript: serverSnapshot?.hydrationScript,
184
+ }) ??
185
+ runtimeState?.hydrationScripts ??
186
+ toHydrationScripts({
187
+ hydrationScript: runtimeState?.hydrationScript,
188
+ }) ??
189
+ []
190
+ );
191
+ }
192
+
193
+ export function getRouterMatchedRouteIds(runtimeContext: object) {
194
+ const serverSnapshot = getRouterServerSnapshot(runtimeContext);
195
+ const runtimeState = getRouterRuntimeState(runtimeContext);
196
+ return (
197
+ serverSnapshot?.matchedRouteIds ??
198
+ getMatchedRouteIdsFromMatches(serverSnapshot?.matches) ??
199
+ runtimeState?.matchedRouteIds ??
200
+ getMatchedRouteIdsFromMatches(runtimeState?.matches)
201
+ );
202
+ }
203
+
204
+ export async function cleanupRouterRuntimeState(runtimeContext: object) {
205
+ try {
206
+ await getRouterRuntimeState(runtimeContext)?.cleanup?.();
207
+ } catch {}
208
+ }
@@ -0,0 +1,86 @@
1
+ import { createRouterPrefetchPolicy } from './routerPrefetchPolicy';
2
+ import {
3
+ applyRouterRuntimeState,
4
+ createRouterServerSnapshot,
5
+ } from './routerState';
6
+
7
+ type ReactRouterServerContext = {
8
+ statusCode: number;
9
+ errors?: Record<string, unknown> | null;
10
+ loaderData: Record<string, unknown>;
11
+ matches: { route: { id?: string } }[];
12
+ };
13
+
14
+ type RouterLifecycleEvent = {
15
+ framework: string;
16
+ phase: string;
17
+ runtimeContext: object;
18
+ basename?: string;
19
+ router?: unknown;
20
+ };
21
+
22
+ /** Install fork policy through lifecycle hooks supplied by the native owner. */
23
+ export function createRouterStatePlugin<Hooks extends Record<string, unknown>>({
24
+ registryHooks,
25
+ }: {
26
+ registryHooks: Hooks;
27
+ }) {
28
+ return {
29
+ name: '@modern-js/router-runtime-policy',
30
+ registryHooks,
31
+ setup(api: {
32
+ onBeforeRender: (callback: (context: object) => void) => unknown;
33
+ onAfterCreateRouter: (
34
+ callback: (event: RouterLifecycleEvent) => void,
35
+ ) => unknown;
36
+ }) {
37
+ api.onBeforeRender(context => {
38
+ const runtimeContext = context as {
39
+ linkPrefetchPolicy?: ReturnType<typeof createRouterPrefetchPolicy>;
40
+ };
41
+ runtimeContext.linkPrefetchPolicy ??= createRouterPrefetchPolicy();
42
+ });
43
+ api.onAfterCreateRouter(event => {
44
+ if (event.framework !== 'react-router') {
45
+ return;
46
+ }
47
+ if (event.phase === 'ssr-prepare') {
48
+ const { routerContext } = event.runtimeContext as {
49
+ routerContext?: ReactRouterServerContext;
50
+ };
51
+ if (!routerContext) {
52
+ return;
53
+ }
54
+ const serverSnapshot = createRouterServerSnapshot({
55
+ framework: event.framework,
56
+ basename: event.basename,
57
+ statusCode: routerContext.statusCode,
58
+ errors: routerContext.errors ?? undefined,
59
+ routerData: {
60
+ loaderData: routerContext.loaderData,
61
+ errors: routerContext.errors ?? undefined,
62
+ },
63
+ matches: routerContext.matches.flatMap(match =>
64
+ typeof match.route.id === 'string'
65
+ ? [{ routeId: match.route.id }]
66
+ : [],
67
+ ),
68
+ });
69
+ applyRouterRuntimeState(event.runtimeContext, {
70
+ framework: event.framework,
71
+ basename: event.basename,
72
+ instance: event.router,
73
+ matchedRouteIds: serverSnapshot.matchedRouteIds,
74
+ serverSnapshot,
75
+ });
76
+ } else if (event.phase === 'client-create') {
77
+ applyRouterRuntimeState(event.runtimeContext, {
78
+ framework: event.framework,
79
+ basename: event.basename,
80
+ instance: event.router,
81
+ });
82
+ }
83
+ });
84
+ },
85
+ };
86
+ }
@@ -0,0 +1,46 @@
1
+ export type BuiltInRouterFramework = 'react-router' | 'tanstack';
2
+
3
+ export type RouterFramework = BuiltInRouterFramework | (string & {});
4
+
5
+ export interface RouterRouteMatchSnapshot {
6
+ routeId: string;
7
+ assetRouteId?: string;
8
+ pathname?: string;
9
+ params?: Record<string, string>;
10
+ }
11
+
12
+ export interface InternalRouterServerSnapshot {
13
+ framework?: RouterFramework;
14
+ basename?: string;
15
+ statusCode?: number;
16
+ errors?: Record<string, unknown>;
17
+ routerData?: {
18
+ loaderData?: Record<string, unknown>;
19
+ errors?: Record<string, unknown>;
20
+ };
21
+ hydrationScript?: string;
22
+ hydrationScripts?: string[];
23
+ matchedRouteIds?: string[];
24
+ matches?: RouterRouteMatchSnapshot[];
25
+ }
26
+
27
+ export interface InternalRouterRuntimeState {
28
+ framework: RouterFramework;
29
+ basename?: string;
30
+ instance?: unknown;
31
+ hydrationScript?: string;
32
+ hydrationScripts?: string[];
33
+ matchedRouteIds?: string[];
34
+ matches?: RouterRouteMatchSnapshot[];
35
+ serverSnapshot?: InternalRouterServerSnapshot;
36
+ cleanup?: () => void | Promise<void>;
37
+ }
38
+
39
+ export interface RouterServerPrepareResult {
40
+ state: InternalRouterRuntimeState;
41
+ snapshot?: InternalRouterServerSnapshot;
42
+ redirect?: Response;
43
+ cleanup?: () => void | Promise<void>;
44
+ }
45
+
46
+ export type RouterLifecyclePhase = 'ssr-prepare' | 'client-create' | 'hydrate';
@@ -0,0 +1,105 @@
1
+ import { stripRuntimeContextExtensions } from './contextExtensions';
2
+
3
+ // Only the transport fields consumed by this projection are required. This
4
+ // policy module does not depend on the native runtime or plugin API types.
5
+ interface ProjectionContext {
6
+ isBrowser?: boolean;
7
+ ssrContext?: {
8
+ request: {
9
+ url?: unknown;
10
+ userAgent?: unknown;
11
+ cookie?: unknown;
12
+ pathname?: unknown;
13
+ query?: object;
14
+ params?: object;
15
+ headers?: object;
16
+ host?: unknown;
17
+ referer?: unknown;
18
+ };
19
+ response: { locals?: unknown };
20
+ };
21
+ }
22
+
23
+ function createRscSafeRequestContext(
24
+ ssrContext: ProjectionContext['ssrContext'],
25
+ ) {
26
+ const requestContext = {
27
+ request: {
28
+ params: {},
29
+ pathname: '',
30
+ query: {},
31
+ headers: {},
32
+ host: '',
33
+ url: '',
34
+ },
35
+ response: {
36
+ setHeader() {},
37
+ status() {},
38
+ locals: {} as unknown,
39
+ },
40
+ };
41
+ Object.defineProperty(requestContext.response, 'setHeader', {
42
+ enumerable: false,
43
+ });
44
+ Object.defineProperty(requestContext.response, 'status', {
45
+ enumerable: false,
46
+ });
47
+
48
+ if (ssrContext === undefined) {
49
+ return requestContext;
50
+ }
51
+
52
+ const { request, response } = ssrContext;
53
+ return {
54
+ request: {
55
+ url: request.url,
56
+ userAgent: request.userAgent,
57
+ cookie: request.cookie,
58
+ pathname: request.pathname,
59
+ query: { ...request.query },
60
+ params: { ...request.params },
61
+ headers: { ...request.headers },
62
+ host: request.host,
63
+ referer: request.referer,
64
+ },
65
+ response: Object.assign(requestContext.response, {
66
+ locals: response.locals ?? {},
67
+ }),
68
+ };
69
+ }
70
+
71
+ export function projectRuntimeContext<
72
+ InternalContext extends ProjectionContext,
73
+ PublicContext extends ProjectionContext,
74
+ >(
75
+ projection: {
76
+ internalContext: InternalContext;
77
+ publicContext: PublicContext;
78
+ },
79
+ options: { context: InternalContext; isRsc: boolean },
80
+ ) {
81
+ let internalContext = projection.internalContext;
82
+ let publicContext = { ...projection.publicContext };
83
+ // Enumerable symbol slots deliberately survive internal SSR copies. Remove
84
+ // them only from the public projection, never from the original request.
85
+ stripRuntimeContextExtensions(publicContext);
86
+
87
+ if (options.isRsc && options.context.isBrowser === false) {
88
+ const requestContext = createRscSafeRequestContext(
89
+ options.context.ssrContext,
90
+ );
91
+ internalContext = {
92
+ ...internalContext,
93
+ context: requestContext,
94
+ requestContext,
95
+ };
96
+ delete internalContext.ssrContext;
97
+ publicContext = {
98
+ ...publicContext,
99
+ context: requestContext,
100
+ requestContext,
101
+ };
102
+ }
103
+
104
+ return { internalContext, publicContext };
105
+ }
@@ -0,0 +1,136 @@
1
+ export type BffRuntimeFramework = 'hono' | 'effect';
2
+
3
+ /** Fork BFF options, independent of any native server config interface. */
4
+ export interface BffRuntimeUserConfig {
5
+ runtimeFramework?: BffRuntimeFramework;
6
+ effect?: BffEffectUserConfig;
7
+ crossProjectPolicy?: BffCrossProjectPolicyUserConfig;
8
+ }
9
+
10
+ export interface BffCrossProjectPolicyUserConfig {
11
+ /**
12
+ * Enable cross-project envelope and operation-context policy checks.
13
+ *
14
+ * @default false
15
+ */
16
+ enabled?: boolean;
17
+ /**
18
+ * Require cross-project envelope header when policy is enabled.
19
+ *
20
+ * @default true
21
+ */
22
+ requireEnvelope?: boolean;
23
+ /**
24
+ * Require operation-context header when policy is enabled.
25
+ *
26
+ * @default true
27
+ */
28
+ requireOperationContext?: boolean;
29
+ /**
30
+ * Require operation-context detail header carrying schema/version metadata.
31
+ *
32
+ * @default true
33
+ */
34
+ requireOperationContextDetails?: boolean;
35
+ /**
36
+ * Require operation schema hash in operation-context details.
37
+ *
38
+ * @default true
39
+ */
40
+ requireOperationSchemaHash?: boolean;
41
+ /**
42
+ * Require operation version in operation-context details.
43
+ *
44
+ * @default true
45
+ */
46
+ requireOperationVersion?: boolean;
47
+ /**
48
+ * Optional allowlist of producer namespaces derived from requestId.
49
+ */
50
+ allowedNamespaces?: string[];
51
+ /**
52
+ * Optional hook deriving a verified producer identity (namespace) from
53
+ * request headers, e.g. an mTLS subject or gateway-verified JWT claim.
54
+ * When provided, namespace checks bind to this verified value instead of
55
+ * the client-asserted requestId namespace; returning `undefined` denies
56
+ * the request. Without it the namespace checks are advisory only.
57
+ */
58
+ verifyProducerIdentity?: (
59
+ headers: Record<string, unknown>,
60
+ ) => string | undefined;
61
+ /**
62
+ * Optional operation-contract map keyed by:
63
+ * - `${METHOD}:${routePath}`
64
+ * - `operation:${requestId}:${operationId}`
65
+ */
66
+ expectedOperationContracts?: Record<
67
+ string,
68
+ {
69
+ schemaHash?: string;
70
+ operationVersion?: number;
71
+ }
72
+ >;
73
+ /**
74
+ * Allow operations missing from expectedOperationContracts.
75
+ *
76
+ * @default false
77
+ */
78
+ allowUnknownOperations?: boolean;
79
+ /**
80
+ * HTTP status code used for denied requests.
81
+ *
82
+ * @default 403
83
+ */
84
+ denyStatus?: number;
85
+ }
86
+
87
+ export type BffEffectOpenApiUserConfig =
88
+ | boolean
89
+ | {
90
+ path?: string;
91
+ };
92
+
93
+ export interface BffEffectDataPlatformSelectionUserConfig {
94
+ maxDepth?: number;
95
+ maxFields?: number;
96
+ allowedLeafPaths?: string[];
97
+ }
98
+
99
+ export interface BffEffectDataPlatformBatchUserConfig {
100
+ enabled?: boolean;
101
+ endpoint?: `/${string}`;
102
+ maxBatchSize?: number;
103
+ maxBatchBytes?: number;
104
+ flushIntervalMs?: number;
105
+ maxConcurrency?: number;
106
+ requestTimeoutMs?: number;
107
+ allowedMethods?: string[];
108
+ }
109
+
110
+ export interface BffEffectDataPlatformUserConfig {
111
+ enabled?: boolean;
112
+ requireEnvelope?: boolean;
113
+ envelopeHeader?: string;
114
+ expectedNamespace?: string;
115
+ validateOrigin?: boolean;
116
+ requireTraceContext?: boolean;
117
+ selection?: BffEffectDataPlatformSelectionUserConfig;
118
+ batch?: BffEffectDataPlatformBatchUserConfig;
119
+ }
120
+
121
+ export interface BffEffectUserConfig {
122
+ entry?: string;
123
+ /**
124
+ * Enforce Effect-native API/runtime modules instead of raw request handlers.
125
+ *
126
+ * Effect API entries must export a `defineEffectBff(...)`
127
+ * definition or a `{ api, layer }` HttpApi module. Raw `handler` exports,
128
+ * default request handlers, and unbranded custom `createHandler` factories
129
+ * are rejected.
130
+ *
131
+ * @default true
132
+ */
133
+ strictEffectApproach?: true;
134
+ openapi?: BffEffectOpenApiUserConfig;
135
+ dataPlatform?: BffEffectDataPlatformUserConfig;
136
+ }
@@ -0,0 +1,2 @@
1
+ export type * from './bffRuntime';
2
+ export type * from './serverTelemetry';