@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,75 @@
1
+ import "node:module";
2
+ import { stripRuntimeContextExtensions } from "./contextExtensions.mjs";
3
+ function createRscSafeRequestContext(ssrContext) {
4
+ const requestContext = {
5
+ request: {
6
+ params: {},
7
+ pathname: '',
8
+ query: {},
9
+ headers: {},
10
+ host: '',
11
+ url: ''
12
+ },
13
+ response: {
14
+ setHeader () {},
15
+ status () {},
16
+ locals: {}
17
+ }
18
+ };
19
+ Object.defineProperty(requestContext.response, 'setHeader', {
20
+ enumerable: false
21
+ });
22
+ Object.defineProperty(requestContext.response, 'status', {
23
+ enumerable: false
24
+ });
25
+ if (void 0 === ssrContext) return requestContext;
26
+ const { request, response } = ssrContext;
27
+ return {
28
+ request: {
29
+ url: request.url,
30
+ userAgent: request.userAgent,
31
+ cookie: request.cookie,
32
+ pathname: request.pathname,
33
+ query: {
34
+ ...request.query
35
+ },
36
+ params: {
37
+ ...request.params
38
+ },
39
+ headers: {
40
+ ...request.headers
41
+ },
42
+ host: request.host,
43
+ referer: request.referer
44
+ },
45
+ response: Object.assign(requestContext.response, {
46
+ locals: response.locals ?? {}
47
+ })
48
+ };
49
+ }
50
+ function projectRuntimeContext(projection, options) {
51
+ let internalContext = projection.internalContext;
52
+ let publicContext = {
53
+ ...projection.publicContext
54
+ };
55
+ stripRuntimeContextExtensions(publicContext);
56
+ if (options.isRsc && false === options.context.isBrowser) {
57
+ const requestContext = createRscSafeRequestContext(options.context.ssrContext);
58
+ internalContext = {
59
+ ...internalContext,
60
+ context: requestContext,
61
+ requestContext
62
+ };
63
+ delete internalContext.ssrContext;
64
+ publicContext = {
65
+ ...publicContext,
66
+ context: requestContext,
67
+ requestContext
68
+ };
69
+ }
70
+ return {
71
+ internalContext,
72
+ publicContext
73
+ };
74
+ }
75
+ export { projectRuntimeContext };
@@ -0,0 +1 @@
1
+ import "node:module";
@@ -0,0 +1 @@
1
+ import "node:module";
@@ -0,0 +1 @@
1
+ import "node:module";
@@ -13,7 +13,7 @@ const createConservingWebShellStream = (source, context, marker, onMissingMarker
13
13
  markerFound = candidate.includes(marker);
14
14
  markerCarry = markerFound ? '' : candidate.slice(-(marker.length - 1));
15
15
  };
16
- return source.pipeThrough(new TransformStream({
16
+ const transform = new TransformStream({
17
17
  transform (chunk, controller) {
18
18
  const decoded = decoder.decode(chunk, {
19
19
  stream: true
@@ -34,6 +34,38 @@ const createConservingWebShellStream = (source, context, marker, onMissingMarker
34
34
  const output = headProcessor.finish(decoded);
35
35
  if (output) controller.enqueue(encoder.encode(output));
36
36
  }
37
- }));
37
+ });
38
+ const piping = source.pipeTo(transform.writable);
39
+ piping.catch(()=>{});
40
+ const reader = transform.readable.getReader();
41
+ return new ReadableStream({
42
+ async pull (controller) {
43
+ try {
44
+ const result = await reader.read();
45
+ if (result.done) {
46
+ await piping;
47
+ controller.close();
48
+ reader.releaseLock();
49
+ } else controller.enqueue(result.value);
50
+ } catch (error) {
51
+ await piping.catch(()=>{});
52
+ controller.error(error);
53
+ reader.releaseLock();
54
+ }
55
+ },
56
+ async cancel (reason) {
57
+ try {
58
+ await reader.cancel(reason);
59
+ } finally{
60
+ try {
61
+ await piping.catch((error)=>{
62
+ if (error !== reason) throw error;
63
+ });
64
+ } finally{
65
+ reader.releaseLock();
66
+ }
67
+ }
68
+ }
69
+ });
38
70
  };
39
71
  export { createConservingWebShellStream };
@@ -0,0 +1,67 @@
1
+ import "node:module";
2
+ const ultramodernWorkspaceEventNames = {
3
+ navigate: 'ultramodern:navigate',
4
+ performanceSignal: 'ultramodern:performance-signal',
5
+ remoteReady: 'ultramodern:remote-ready',
6
+ routeSettled: 'ultramodern:route-settled'
7
+ };
8
+ class UltramodernWorkspaceEventValidationError {
9
+ constructor(eventName, payload){
10
+ this.name = 'UltramodernWorkspaceEventValidationError';
11
+ this.message = `Invalid payload for UltraModern workspace event "${eventName}"`;
12
+ this.eventName = eventName;
13
+ this.payload = payload;
14
+ }
15
+ }
16
+ const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
17
+ const isNonEmptyString = (value)=>'string' == typeof value && value.trim().length > 0;
18
+ const isNonNegativeNumber = (value)=>'number' == typeof value && Number.isFinite(value) && value >= 0;
19
+ const isUltramodernWorkspaceLocale = (value)=>'en' === value || 'cs' === value;
20
+ const isPerformanceReadinessSignalId = (value)=>'bfcache' === value || 'core-web-vitals-rum' === value || 'duplicate-prefetch-warmup' === value || 'cache-policy-sanity' === value || 'save-data-behavior' === value || 'cloudflare-ssr-cache-hints' === value;
21
+ const isPerformanceReadinessSignalStatus = (value)=>'pass' === value || 'warn' === value || 'fail' === value;
22
+ const hasOptionalString = (value, key)=>void 0 === value[key] || isNonEmptyString(value[key]);
23
+ const hasOptionalBoolean = (value, key)=>void 0 === value[key] || 'boolean' == typeof value[key];
24
+ const hasOptionalRecord = (value, key)=>void 0 === value[key] || isRecord(value[key]);
25
+ const hasOptionalNonNegativeNumber = (value, key)=>void 0 === value[key] || isNonNegativeNumber(value[key]);
26
+ const hasOptionalLocale = (value, key)=>void 0 === value[key] || isUltramodernWorkspaceLocale(value[key]);
27
+ const isUltramodernNavigatePayload = (payload)=>isRecord(payload) && isNonEmptyString(payload.to) && hasOptionalBoolean(payload, 'replace') && hasOptionalRecord(payload, 'state');
28
+ const isUltramodernRouteSettledPayload = (payload)=>isRecord(payload) && isNonEmptyString(payload.pathname) && hasOptionalLocale(payload, 'locale') && hasOptionalString(payload, 'title');
29
+ const isUltramodernRemoteReadyPayload = (payload)=>isRecord(payload) && isNonEmptyString(payload.appId) && hasOptionalString(payload, 'build') && hasOptionalString(payload, 'surface') && hasOptionalString(payload, 'version');
30
+ const isUltramodernPerformanceSignalPayload = (payload)=>isRecord(payload) && isPerformanceReadinessSignalId(payload.signalId) && isPerformanceReadinessSignalStatus(payload.status) && hasOptionalNonNegativeNumber(payload, 'durationMs') && hasOptionalRecord(payload, 'detail');
31
+ const ultramodernWorkspaceEventValidators = {
32
+ [ultramodernWorkspaceEventNames.navigate]: isUltramodernNavigatePayload,
33
+ [ultramodernWorkspaceEventNames.performanceSignal]: isUltramodernPerformanceSignalPayload,
34
+ [ultramodernWorkspaceEventNames.remoteReady]: isUltramodernRemoteReadyPayload,
35
+ [ultramodernWorkspaceEventNames.routeSettled]: isUltramodernRouteSettledPayload
36
+ };
37
+ const isUltramodernWorkspaceEventPayload = (eventName, payload)=>ultramodernWorkspaceEventValidators[eventName](payload);
38
+ const assertUltramodernWorkspaceEventPayload = (eventName, payload)=>{
39
+ if (!isUltramodernWorkspaceEventPayload(eventName, payload)) throw new UltramodernWorkspaceEventValidationError(eventName, payload);
40
+ return payload;
41
+ };
42
+ const createUltramodernWorkspaceEvent = (eventName, payload)=>new CustomEvent(eventName, {
43
+ bubbles: true,
44
+ composed: true,
45
+ detail: assertUltramodernWorkspaceEventPayload(eventName, payload)
46
+ });
47
+ const dispatchUltramodernWorkspaceEvent = (target, eventName, payload)=>target.dispatchEvent(createUltramodernWorkspaceEvent(eventName, payload));
48
+ const onUltramodernWorkspaceEvent = (target, eventName, handler)=>{
49
+ const listener = (event)=>{
50
+ if (!('detail' in event)) throw new UltramodernWorkspaceEventValidationError(eventName, void 0);
51
+ const customEvent = event;
52
+ handler(assertUltramodernWorkspaceEventPayload(eventName, customEvent.detail), customEvent);
53
+ };
54
+ target.addEventListener(eventName, listener);
55
+ return ()=>{
56
+ target.removeEventListener(eventName, listener);
57
+ };
58
+ };
59
+ const dispatchUltramodernNavigate = (target, payload)=>dispatchUltramodernWorkspaceEvent(target, ultramodernWorkspaceEventNames.navigate, payload);
60
+ const dispatchUltramodernRouteSettled = (target, payload)=>dispatchUltramodernWorkspaceEvent(target, ultramodernWorkspaceEventNames.routeSettled, payload);
61
+ const dispatchUltramodernRemoteReady = (target, payload)=>dispatchUltramodernWorkspaceEvent(target, ultramodernWorkspaceEventNames.remoteReady, payload);
62
+ const dispatchUltramodernPerformanceSignal = (target, payload)=>dispatchUltramodernWorkspaceEvent(target, ultramodernWorkspaceEventNames.performanceSignal, payload);
63
+ const onUltramodernNavigate = (target, handler)=>onUltramodernWorkspaceEvent(target, ultramodernWorkspaceEventNames.navigate, handler);
64
+ const onUltramodernRouteSettled = (target, handler)=>onUltramodernWorkspaceEvent(target, ultramodernWorkspaceEventNames.routeSettled, handler);
65
+ const onUltramodernRemoteReady = (target, handler)=>onUltramodernWorkspaceEvent(target, ultramodernWorkspaceEventNames.remoteReady, handler);
66
+ const onUltramodernPerformanceSignal = (target, handler)=>onUltramodernWorkspaceEvent(target, ultramodernWorkspaceEventNames.performanceSignal, handler);
67
+ export { UltramodernWorkspaceEventValidationError, assertUltramodernWorkspaceEventPayload, createUltramodernWorkspaceEvent, dispatchUltramodernNavigate, dispatchUltramodernPerformanceSignal, dispatchUltramodernRemoteReady, dispatchUltramodernRouteSettled, dispatchUltramodernWorkspaceEvent, isUltramodernNavigatePayload, isUltramodernPerformanceSignalPayload, isUltramodernRemoteReadyPayload, isUltramodernRouteSettledPayload, isUltramodernWorkspaceEventPayload, onUltramodernNavigate, onUltramodernPerformanceSignal, onUltramodernRemoteReady, onUltramodernRouteSettled, onUltramodernWorkspaceEvent, ultramodernWorkspaceEventNames };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Fork-owned runtime-context extension mechanism.
3
+ *
4
+ * Plugins must not add ad-hoc fields to `TRuntimeContext` /
5
+ * `TInternalRuntimeContext`. Instead they store state in a single internal
6
+ * slot (a `Map` keyed by symbol) attached to the context object under a
7
+ * symbol property, so nothing leaks into string-key enumeration
8
+ * (`Object.keys`, `JSON.stringify`). Object spreads preserve the slot.
9
+ *
10
+ * `Symbol.for` is used for both the slot and the extension keys so that the
11
+ * mechanism stays coherent even if this module is instantiated twice
12
+ * (e.g. esm/cjs interop in test runners).
13
+ */
14
+ export interface RuntimeContextExtension<T> {
15
+ /** Stable identity of this extension. */
16
+ readonly key: symbol;
17
+ get(context: object): T | undefined;
18
+ set(context: object, value: T): void;
19
+ remove(context: object): void;
20
+ }
21
+ /**
22
+ * Removes the extension slot from a context object.
23
+ *
24
+ * The symbol slot crosses object spreads while remaining absent from string-key
25
+ * enumeration and JSON. Callers can still explicitly clear the original context.
26
+ */
27
+ export declare function stripRuntimeContextExtensions(context: object): void;
28
+ /**
29
+ * Creates a typed accessor pair over the shared extension slot.
30
+ *
31
+ * The `id` must be globally unique (it is interned via `Symbol.for`); use a
32
+ * package-prefixed name such as `@modern-js/plugin-tanstack:router-state`.
33
+ */
34
+ export declare function createRuntimeContextExtension<T>(id: string): RuntimeContextExtension<T>;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { Helmet as AsyncHelmet, type HelmetProps } from 'react-helmet-async';
3
+ import type { HelmetContextSlot } from './helmetContext.js';
4
+ export interface HeadRenderContext {
5
+ runtimeContext: object;
6
+ helmetContext: HelmetContextSlot;
7
+ }
8
+ export declare function createHeadRuntime(): {
9
+ Head: (props: React.PropsWithChildren<HelmetProps>) => React.CElement<React.PropsWithChildren<HelmetProps>, AsyncHelmet> | React.ReactElement<{}, string | React.JSXElementConstructor<any>> | null;
10
+ wrapServerRoot(root: React.ReactNode, state: HeadRenderContext): React.JSX.Element;
11
+ wrapClientRoot(root: React.ReactNode): React.JSX.Element;
12
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Fork-owned helmet state, stored via the runtime-context extension slot
3
+ * instead of an ad-hoc `_helmetContext` field on `TInternalRuntimeContext`.
4
+ */
5
+ import type { HelmetServerState } from 'react-helmet-async';
6
+ export interface HelmetContextSlot {
7
+ helmet?: HelmetServerState | null;
8
+ }
9
+ export declare function getHelmetContext(context: object): HelmetContextSlot | undefined;
10
+ export declare function ensureHelmetContext(context: object): HelmetContextSlot;
@@ -1,4 +1,6 @@
1
1
  export * from './html.js';
2
2
  export * from './navigationWarmup.js';
3
+ export * from './prefetchPolicy.js';
3
4
  export * from './rendererHead.js';
5
+ export * from './routeHydration.js';
4
6
  export * from './shellStream.js';
@@ -0,0 +1,4 @@
1
+ type PrefetchBehavior = 'intent' | 'render' | 'viewport' | 'none';
2
+ export declare function resolvePreloadFromPrefetch(prefetch: PrefetchBehavior | undefined, preload: unknown): {} | null;
3
+ export declare function normalizePreloadBehavior(preload: PrefetchBehavior | false | undefined, prefetch: PrefetchBehavior): PrefetchBehavior;
4
+ export {};
@@ -0,0 +1,10 @@
1
+ /** Create a browser producer client with its own configuration and policy. */
2
+ export declare const createClient: () => {
3
+ configure: (options: import("./types.js").IOptions<typeof fetch>) => void;
4
+ createRequest: import("./types.js").RequestCreator<typeof fetch>;
5
+ createUploader: import("./types.js").UploadCreator;
6
+ };
7
+ export declare const configure: (options: import("./types.js").IOptions<typeof fetch>) => void, createRequest: import("./types.js").RequestCreator<typeof fetch>, createUploader: import("./types.js").UploadCreator;
8
+ export { CrossOriginEnvelopePolicyError, IdentityBindingViolationError, OperationContractViolationError, ProducerClientNotInitializedError, ProducerDomainNotConfiguredError, } from './policyCore.js';
9
+ export * from './requestContext.js';
10
+ export * from './types.js';
@@ -0,0 +1,14 @@
1
+ import type { RequestClient } from '@modern-js/create-request/client';
2
+ import type { IOptions, RequestCreator, TransportTarget, UploadCreator } from './types.js';
3
+ type HeaderMap = Record<string, any>;
4
+ type PolicyEnvironment = {
5
+ target: TransportTarget;
6
+ resolveSourceOrigin: (incomingHeaders: HeaderMap) => string | undefined;
7
+ };
8
+ /** Bind fork producer policy to one isolated native request client. */
9
+ export declare const createPolicyClient: <F>(nativeClient: RequestClient<F>, environment: PolicyEnvironment) => {
10
+ configure: (options: IOptions<F>) => void;
11
+ createRequest: RequestCreator<F>;
12
+ createUploader: UploadCreator;
13
+ };
14
+ export {};
@@ -0,0 +1,10 @@
1
+ /** Create a server producer client with its own configuration and policy. */
2
+ export declare const createClient: () => {
3
+ configure: (options: import("./types.js").IOptions<typeof fetch>) => void;
4
+ createRequest: import("./types.js").RequestCreator<typeof fetch>;
5
+ createUploader: import("./types.js").UploadCreator;
6
+ };
7
+ export declare const configure: (options: import("./types.js").IOptions<typeof fetch>) => void, createRequest: import("./types.js").RequestCreator<typeof fetch>, createUploader: import("./types.js").UploadCreator;
8
+ export { CrossOriginEnvelopePolicyError, IdentityBindingViolationError, OperationContractViolationError, ProducerClientNotInitializedError, ProducerDomainNotConfiguredError, } from './policyCore.js';
9
+ export * from './requestContext.js';
10
+ export * from './types.js';
@@ -0,0 +1,107 @@
1
+ import type { IdentityBindingViolation, OperationContext, OperationContractOptions, OperationContractViolation, TransportTarget } from './types.js';
2
+ export declare const TRACEPARENT_HEADER = "traceparent";
3
+ export declare const isSecuredRequestId: (requestId: string) => boolean;
4
+ export declare const isEmptyDomain: (domain?: string) => boolean;
5
+ export declare const firstHeaderValue: (value: unknown) => any;
6
+ export declare const findHeaderKey: (headers: Record<string, any>, header: string) => string | undefined;
7
+ export declare const readHeader: (headers: Record<string, any>, header: string) => any;
8
+ export declare const writeHeader: (headers: Record<string, any>, header: string, value: unknown) => void;
9
+ export declare const deleteHeader: (headers: Record<string, any>, header: string) => void;
10
+ export declare const toOrigin: (value?: string) => string | undefined;
11
+ export declare const parseTraceparentValue: (value: unknown) => import("./traceparent.js").TraceparentContext | undefined;
12
+ export declare const buildOperationContext: ({ requestId, method, path, operationContext, traceparent, }: {
13
+ requestId: string;
14
+ method: string;
15
+ path: string;
16
+ operationContext?: OperationContext | undefined;
17
+ traceparent?: unknown;
18
+ }) => {
19
+ requestId: string;
20
+ operationId: string;
21
+ routePath: string;
22
+ method: string;
23
+ schemaHash?: string | undefined;
24
+ operationVersion?: number | undefined;
25
+ traceparent?: string | undefined;
26
+ traceId?: string | undefined;
27
+ spanId?: string | undefined;
28
+ };
29
+ type OperationContextPayload = ReturnType<typeof buildOperationContext>;
30
+ export declare class ProducerClientNotInitializedError extends Error {
31
+ readonly code = "BFF_PRODUCER_CLIENT_NOT_INITIALIZED";
32
+ constructor(requestId: string);
33
+ }
34
+ export declare class ProducerDomainNotConfiguredError extends Error {
35
+ readonly code = "BFF_PRODUCER_DOMAIN_NOT_CONFIGURED";
36
+ constructor(requestId: string);
37
+ }
38
+ export declare class CrossOriginEnvelopePolicyError extends Error {
39
+ readonly code = "BFF_CROSS_ORIGIN_ENVELOPE_NOT_ALLOWED";
40
+ constructor(requestId: string, sourceOrigin?: string, targetOrigin?: string);
41
+ }
42
+ export declare class IdentityBindingViolationError extends Error {
43
+ readonly code = "BFF_IDENTITY_BINDING_VIOLATION";
44
+ readonly violation: IdentityBindingViolation;
45
+ constructor(violation: IdentityBindingViolation);
46
+ }
47
+ export declare class OperationContractViolationError extends Error {
48
+ readonly code = "BFF_OPERATION_CONTRACT_VIOLATION";
49
+ readonly violation: OperationContractViolation;
50
+ constructor(violation: OperationContractViolation);
51
+ }
52
+ export declare const validateOperationContract: ({ requestId, target, contextPayload, operationContract, }: {
53
+ requestId: string;
54
+ target: TransportTarget;
55
+ contextPayload: OperationContextPayload;
56
+ operationContract: OperationContractOptions | undefined;
57
+ }) => void;
58
+ export declare const resolveConfiguredRequest: <F>(configuredRequests: Map<string, F>, requestId: string, fallback: F) => F;
59
+ /**
60
+ * Builds the JSON value for the `x-modernjs-bff-envelope` header and throws
61
+ * when the cross-origin envelope policy denies the flow. Returns the
62
+ * serialized envelope.
63
+ */
64
+ export declare const buildEnvelopeHeaderValue: ({ requestId, target, sourceOrigin, targetOrigin, traceContext, allowCrossOriginEnvelope, }: {
65
+ requestId: string;
66
+ target: TransportTarget;
67
+ sourceOrigin: string | undefined;
68
+ targetOrigin: string | undefined;
69
+ traceContext: {
70
+ traceId: string;
71
+ spanId: string;
72
+ } | null | undefined;
73
+ allowCrossOriginEnvelope: boolean | ((options: {
74
+ requestId: string;
75
+ sourceOrigin?: string;
76
+ targetOrigin?: string;
77
+ target: TransportTarget;
78
+ }) => boolean) | undefined;
79
+ }) => string;
80
+ /**
81
+ * Derives the operation context for a secured requestId, validates it against
82
+ * the configured operation contract and writes the `x-operation-id` and
83
+ * `x-modernjs-bff-operation-context` headers (without clobbering caller
84
+ * provided values for the id header).
85
+ */
86
+ export declare const attachOperationContextHeaders: ({ headers, requestId, target, method, path, operationContext, operationContract, operationContextHeader, operationContextDetailHeader, }: {
87
+ headers: Record<string, any>;
88
+ requestId: string;
89
+ target: TransportTarget;
90
+ method: string;
91
+ path: string;
92
+ operationContext: OperationContext | undefined;
93
+ operationContract: OperationContractOptions | undefined;
94
+ operationContextHeader: string;
95
+ operationContextDetailHeader: string;
96
+ }) => {
97
+ requestId: string;
98
+ operationId: string;
99
+ routePath: string;
100
+ method: string;
101
+ schemaHash?: string | undefined;
102
+ operationVersion?: number | undefined;
103
+ traceparent?: string | undefined;
104
+ traceId?: string | undefined;
105
+ spanId?: string | undefined;
106
+ };
107
+ export {};
@@ -0,0 +1,22 @@
1
+ import type { OperationContext } from './types.js';
2
+ export declare const BFF_LOCALE_HEADER = "accept-language";
3
+ export declare const BFF_TRACEPARENT_HEADER = "traceparent";
4
+ export type RequestContextInput = {
5
+ headers?: Record<string, unknown>;
6
+ locale?: string;
7
+ traceparent?: string;
8
+ operationContext?: OperationContext;
9
+ };
10
+ export type RequestContextSnapshot = {
11
+ headers: Record<string, string>;
12
+ locale?: string;
13
+ traceparent?: string;
14
+ traceId?: string;
15
+ spanId?: string;
16
+ operationContext?: OperationContext;
17
+ };
18
+ export declare function createRequestContextSnapshot(input?: RequestContextInput): RequestContextSnapshot;
19
+ export declare function createRequestContextHeaders(input?: RequestContextInput): Record<string, string>;
20
+ export { parseTraceparent, type TraceparentContext } from './traceparent.js';
21
+ export type * from './types.js';
22
+ export { BFF_DEFAULT_PROTECTED_IDENTITY_HEADERS, BFF_ENVELOPE_HEADER, BFF_OPERATION_CONTEXT_DETAIL_HEADER, BFF_OPERATION_CONTEXT_HEADER, } from './types.js';
@@ -0,0 +1,10 @@
1
+ export type TraceparentContext = {
2
+ traceId: string;
3
+ spanId: string;
4
+ sampled: boolean;
5
+ };
6
+ /**
7
+ * Parse a W3C trace context `traceparent` header (version 00).
8
+ * All-zero trace ids and span ids are rejected per the W3C spec.
9
+ */
10
+ export declare function parseTraceparent(traceparent: string | undefined | null): TraceparentContext | undefined;
@@ -0,0 +1,12 @@
1
+ import type { TransportResilienceOptions, TransportTarget } from './types.js';
2
+ type ExecuteWithResilienceOptions = {
3
+ requestId: string;
4
+ target: TransportTarget;
5
+ method: string;
6
+ url: string;
7
+ init: Record<string, any>;
8
+ fetcher: (...args: any[]) => Promise<any>;
9
+ transport?: TransportResilienceOptions;
10
+ };
11
+ export declare const executeWithResilience: ({ requestId, target, method, url, init, fetcher, transport, }: ExecuteWithResilienceOptions) => Promise<any>;
12
+ export {};
@@ -0,0 +1,144 @@
1
+ import type { HttpMethodDecider, IOptions as NativeOptions, RequestCreatorOptions as NativeRequestCreatorOptions, UploadCreatorOptions as NativeUploadCreatorOptions, Sender } from '@modern-js/create-request/client';
2
+ export type { BFFRequestPayload, HttpMethodDecider, Sender, } from '@modern-js/create-request/client';
3
+ export declare const BFF_ENVELOPE_HEADER = "x-modernjs-bff-envelope";
4
+ export declare const BFF_OPERATION_CONTEXT_HEADER = "x-operation-id";
5
+ export declare const BFF_OPERATION_CONTEXT_DETAIL_HEADER = "x-modernjs-bff-operation-context";
6
+ export declare const BFF_DEFAULT_PROTECTED_IDENTITY_HEADERS: readonly ['x-tenant-id', 'x-subject-id', 'x-user-id', "x-operation-id"];
7
+ export type ResolveHeadersOptions = {
8
+ requestId: string;
9
+ allowedHeaders: string[];
10
+ incomingHeaders: Record<string, any>;
11
+ };
12
+ export type ResolveHeaders = (options: ResolveHeadersOptions) => Record<string, any> | void;
13
+ export type AllowCrossOriginEnvelopeOptions = {
14
+ requestId: string;
15
+ sourceOrigin?: string;
16
+ targetOrigin?: string;
17
+ target: 'server' | 'browser';
18
+ };
19
+ export type AllowCrossOriginEnvelope = boolean | ((options: AllowCrossOriginEnvelopeOptions) => boolean);
20
+ export type TransportTarget = 'server' | 'browser';
21
+ export type RetryDecisionContext = {
22
+ requestId: string;
23
+ target: TransportTarget;
24
+ method: string;
25
+ url: string;
26
+ attempt: number;
27
+ maxAttempts: number;
28
+ error: unknown;
29
+ statusCode?: number;
30
+ };
31
+ export type RetryBackoffOptions = {
32
+ retries?: number;
33
+ baseDelayMs?: number;
34
+ maxDelayMs?: number;
35
+ jitterRatio?: number;
36
+ retryableStatusCodes?: number[];
37
+ shouldRetry?: (context: RetryDecisionContext) => boolean;
38
+ };
39
+ export type DegradedModeReason = 'timeout' | 'retry' | 'retry_exhausted';
40
+ export type DegradedModeEvent = {
41
+ requestId: string;
42
+ target: TransportTarget;
43
+ method: string;
44
+ url: string;
45
+ reason: DegradedModeReason;
46
+ attempt: number;
47
+ maxAttempts: number;
48
+ timeoutMs?: number;
49
+ backoffMs?: number;
50
+ statusCode?: number;
51
+ error?: unknown;
52
+ };
53
+ export type TransportResilienceOptions = {
54
+ timeoutMs?: number;
55
+ retry?: RetryBackoffOptions;
56
+ onDegraded?: (event: DegradedModeEvent) => void;
57
+ };
58
+ export type IdentityBindingViolationReason = 'client_override_blocked' | 'client_override_rejected';
59
+ export type IdentityBindingViolation = {
60
+ requestId: string;
61
+ target: TransportTarget;
62
+ header: string;
63
+ reason: IdentityBindingViolationReason;
64
+ attemptedValue?: unknown;
65
+ derivedValue?: unknown;
66
+ };
67
+ export type DeriveIdentityHeadersOptions = {
68
+ requestId: string;
69
+ target: TransportTarget;
70
+ incomingHeaders: Record<string, any>;
71
+ protectedHeaders: string[];
72
+ };
73
+ export type IdentityBindingOptions = {
74
+ enabled?: boolean;
75
+ strict?: boolean;
76
+ protectedHeaders?: string[];
77
+ deriveHeaders?: (options: DeriveIdentityHeadersOptions) => Record<string, any> | void;
78
+ onViolation?: (violation: IdentityBindingViolation) => void;
79
+ };
80
+ export type OperationContractViolationReason = 'missing_schema_hash' | 'missing_operation_version';
81
+ export type OperationContractViolation = {
82
+ requestId: string;
83
+ target: TransportTarget;
84
+ operationId: string;
85
+ reason: OperationContractViolationReason;
86
+ routePath?: string;
87
+ method?: string;
88
+ schemaHash?: string;
89
+ operationVersion?: number;
90
+ };
91
+ export type CrossProjectOperationContract = {
92
+ schemaHash?: string;
93
+ operationVersion?: number;
94
+ };
95
+ export type CrossProjectPolicyViolationReason = 'missing_envelope' | 'invalid_envelope' | 'missing_request_id' | 'namespace_not_allowed' | 'missing_operation_context' | 'operation_context_mismatch' | 'missing_operation_context_details' | 'invalid_operation_context_details' | 'operation_context_details_request_id_mismatch' | 'missing_operation_schema_hash' | 'missing_operation_version' | 'unknown_operation_contract' | 'operation_schema_hash_mismatch' | 'operation_version_mismatch' | 'producer_identity_mismatch';
96
+ export type CrossProjectPolicyViolation = {
97
+ code: 'BFF_CROSS_PROJECT_POLICY_DENIED';
98
+ reason: CrossProjectPolicyViolationReason;
99
+ message: string;
100
+ status: number;
101
+ };
102
+ export type OperationContractOptions = {
103
+ enabled?: boolean;
104
+ strict?: boolean;
105
+ requireSchemaHash?: boolean;
106
+ requireOperationVersion?: boolean;
107
+ onViolation?: (violation: OperationContractViolation) => void;
108
+ };
109
+ export type OperationContextSource = 'client' | 'server' | 'generated-client' | 'effect-adapter' | 'data-platform' | 'unknown';
110
+ export type OperationContext = {
111
+ requestId?: string;
112
+ operationId?: string;
113
+ routePath?: string;
114
+ method?: string;
115
+ schemaHash?: string;
116
+ operationVersion?: number;
117
+ locale?: string;
118
+ traceparent?: string;
119
+ traceId?: string;
120
+ spanId?: string;
121
+ source?: OperationContextSource;
122
+ scope?: Record<string, unknown>;
123
+ sessionClaims?: Record<string, unknown>;
124
+ attributes?: Record<string, unknown>;
125
+ };
126
+ export type RequestCreatorOptions<F = typeof fetch> = Omit<NativeRequestCreatorOptions<F>, 'hooks'> & {
127
+ operationContext?: OperationContext;
128
+ };
129
+ export type RequestCreator<F = typeof fetch> = {
130
+ (options: RequestCreatorOptions<F>): Sender;
131
+ (path: string, method: string, port: number, httpMethodDecider?: HttpMethodDecider, fetch?: F, requestId?: string, operationContext?: OperationContext): Sender;
132
+ };
133
+ export type UploadCreatorOptions = Omit<NativeUploadCreatorOptions, 'hooks'> & {
134
+ operationContext?: OperationContext;
135
+ };
136
+ export type UploadCreator = (options: UploadCreatorOptions) => Sender;
137
+ export type IOptions<F = typeof fetch> = NativeOptions<F> & {
138
+ resolveHeaders?: ResolveHeaders;
139
+ transport?: TransportResilienceOptions;
140
+ requireEnvelope?: boolean;
141
+ allowCrossOriginEnvelope?: AllowCrossOriginEnvelope;
142
+ identityBinding?: IdentityBindingOptions;
143
+ operationContract?: OperationContractOptions;
144
+ };
@@ -0,0 +1,24 @@
1
+ type RouteAssetManifest = {
2
+ assets?: string[];
3
+ };
4
+ export type RouteManifestLike = {
5
+ routeAssets?: Record<string, RouteAssetManifest | undefined>;
6
+ };
7
+ export type ScriptChunkLike = {
8
+ filename?: string;
9
+ url?: string;
10
+ };
11
+ export declare function getMatchedRouteChunks<T>(routeManifest: RouteManifestLike | undefined, matchedRouteIds: string[], routeAssetToChunk: (asset: string) => T): T[];
12
+ export declare const orderHydrationScriptChunks: <T extends ScriptChunkLike>({ asyncEntryChunks, collectedChunks, matchedRouteChunks, entryName, }: {
13
+ asyncEntryChunks: T[];
14
+ collectedChunks: T[];
15
+ matchedRouteChunks: T[];
16
+ entryName: string;
17
+ }) => T[];
18
+ export declare function injectBeforeHydrationEntryScript(template: string, scripts: string, entryName?: string): string;
19
+ export declare function replaceChunkJsPlaceholder(template: string, scripts: string, entryName: string | undefined, placeholder: string): string;
20
+ export declare function createRouteHydrationScriptTags(routeManifest: RouteManifestLike | undefined, matchedRouteIds: string[], entryName: string, options?: {
21
+ nonce?: string;
22
+ template?: string;
23
+ }): string;
24
+ export {};