@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.2 → 3.9.0-ultramodern.21

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 (251) hide show
  1. package/dist/cjs/buildIdentity.js +73 -0
  2. package/dist/cjs/contextExtensions.js +74 -0
  3. package/dist/cjs/headRuntime.js +91 -0
  4. package/dist/cjs/helmetContext.js +54 -0
  5. package/dist/cjs/index.js +18 -4
  6. package/dist/cjs/localisedLoaderIdentity.js +51 -0
  7. package/dist/cjs/localisedUrls/config.js +49 -0
  8. package/dist/cjs/localisedUrls/index.js +95 -0
  9. package/dist/cjs/localisedUrls/normalise.js +87 -0
  10. package/dist/cjs/localisedUrls/pathname.js +68 -0
  11. package/dist/cjs/localisedUrls/patterns.js +214 -0
  12. package/dist/cjs/localisedUrls/redirect.js +100 -0
  13. package/dist/cjs/localisedUrls/resolve.js +78 -0
  14. package/dist/cjs/localisedUrls/routeIdentity.js +92 -0
  15. package/dist/cjs/localisedUrls/routes.js +217 -0
  16. package/dist/cjs/localisedUrls/types.js +18 -0
  17. package/dist/cjs/prefetchPolicy.js +52 -0
  18. package/dist/cjs/rendererHead.js +18 -5
  19. package/dist/cjs/request-policy/browser.js +139 -0
  20. package/dist/cjs/request-policy/client.js +234 -0
  21. package/dist/cjs/request-policy/node.js +149 -0
  22. package/dist/cjs/request-policy/policyCore.js +266 -0
  23. package/dist/cjs/request-policy/requestContext.js +137 -0
  24. package/dist/cjs/request-policy/traceparent.js +56 -0
  25. package/dist/cjs/request-policy/transport.js +234 -0
  26. package/dist/cjs/request-policy/types.js +57 -0
  27. package/dist/cjs/routeHydration.js +127 -0
  28. package/dist/cjs/routerPrefetchPolicy.js +153 -0
  29. package/dist/cjs/routerProvider.js +105 -0
  30. package/dist/cjs/routerState.js +175 -0
  31. package/dist/cjs/routerStatePlugin.js +113 -0
  32. package/dist/cjs/routerStateTypes.js +18 -0
  33. package/dist/cjs/rsc-disabled/rscDisabledClientBrowserRuntime.js +69 -0
  34. package/dist/cjs/rsc-disabled/rscDisabledClientEdgeRuntime.js +61 -0
  35. package/dist/cjs/rsc-disabled/rscDisabledClientNodeRuntime.js +65 -0
  36. package/dist/cjs/rsc-disabled/rscDisabledRuntimeError.js +42 -0
  37. package/dist/cjs/rsc-disabled/rscDisabledServerEdgeRuntime.js +93 -0
  38. package/dist/cjs/rsc-disabled/rscDisabledServerNodeRuntime.js +101 -0
  39. package/dist/cjs/runtimeContextProjection.js +112 -0
  40. package/dist/cjs/server-config/bffRuntime.js +18 -0
  41. package/dist/cjs/server-config/index.js +18 -0
  42. package/dist/cjs/server-config/serverTelemetry.js +18 -0
  43. package/dist/cjs/shellStream.js +34 -2
  44. package/dist/cjs/workspaceEvents.js +159 -0
  45. package/dist/esm/buildIdentity.mjs +35 -0
  46. package/dist/esm/contextExtensions.mjs +33 -0
  47. package/dist/esm/headRuntime.mjs +43 -0
  48. package/dist/esm/helmetContext.mjs +13 -0
  49. package/dist/esm/index.mjs +2 -0
  50. package/dist/esm/localisedLoaderIdentity.mjs +13 -0
  51. package/dist/esm/localisedUrls/config.mjs +11 -0
  52. package/dist/esm/localisedUrls/index.mjs +8 -0
  53. package/dist/esm/localisedUrls/normalise.mjs +37 -0
  54. package/dist/esm/localisedUrls/pathname.mjs +24 -0
  55. package/dist/esm/localisedUrls/patterns.mjs +152 -0
  56. package/dist/esm/localisedUrls/redirect.mjs +53 -0
  57. package/dist/esm/localisedUrls/resolve.mjs +37 -0
  58. package/dist/esm/localisedUrls/routeIdentity.mjs +54 -0
  59. package/dist/esm/localisedUrls/routes.mjs +176 -0
  60. package/dist/esm/localisedUrls/types.mjs +0 -0
  61. package/dist/esm/prefetchPolicy.mjs +11 -0
  62. package/dist/esm/rendererHead.mjs +14 -5
  63. package/dist/esm/request-policy/browser.mjs +11 -0
  64. package/dist/esm/request-policy/client.mjs +195 -0
  65. package/dist/esm/request-policy/policyCore.mjs +171 -0
  66. package/dist/esm/request-policy/requestContext.mjs +75 -0
  67. package/dist/esm/request-policy/traceparent.mjs +18 -0
  68. package/dist/esm/request-policy/transport.mjs +196 -0
  69. package/dist/esm/request-policy/types.mjs +10 -0
  70. package/dist/esm/routeHydration.mjs +77 -0
  71. package/dist/esm/routerPrefetchPolicy.mjs +115 -0
  72. package/dist/esm/routerProvider.mjs +58 -0
  73. package/dist/esm/routerState.mjs +113 -0
  74. package/dist/esm/routerStatePlugin.mjs +75 -0
  75. package/dist/esm/routerStateTypes.mjs +0 -0
  76. package/dist/esm/rsc-disabled/rscDisabledClientBrowserRuntime.mjs +10 -0
  77. package/dist/esm/rsc-disabled/rscDisabledClientEdgeRuntime.mjs +8 -0
  78. package/dist/esm/rsc-disabled/rscDisabledClientNodeRuntime.mjs +9 -0
  79. package/dist/esm/rsc-disabled/rscDisabledRuntimeError.mjs +4 -0
  80. package/dist/esm/rsc-disabled/rscDisabledServerEdgeRuntime.mjs +16 -0
  81. package/dist/esm/rsc-disabled/rscDisabledServerNodeRuntime.mjs +18 -0
  82. package/dist/esm/runtimeContextProjection.mjs +74 -0
  83. package/dist/esm/server-config/bffRuntime.mjs +0 -0
  84. package/dist/esm/server-config/index.mjs +0 -0
  85. package/dist/esm/server-config/serverTelemetry.mjs +0 -0
  86. package/dist/esm/shellStream.mjs +34 -2
  87. package/dist/esm/workspaceEvents.mjs +66 -0
  88. package/dist/esm-node/buildIdentity.mjs +36 -0
  89. package/dist/esm-node/contextExtensions.mjs +34 -0
  90. package/dist/esm-node/headRuntime.mjs +44 -0
  91. package/dist/esm-node/helmetContext.mjs +14 -0
  92. package/dist/esm-node/index.mjs +2 -0
  93. package/dist/esm-node/localisedLoaderIdentity.mjs +14 -0
  94. package/dist/esm-node/localisedUrls/config.mjs +12 -0
  95. package/dist/esm-node/localisedUrls/index.mjs +9 -0
  96. package/dist/esm-node/localisedUrls/normalise.mjs +38 -0
  97. package/dist/esm-node/localisedUrls/pathname.mjs +25 -0
  98. package/dist/esm-node/localisedUrls/patterns.mjs +153 -0
  99. package/dist/esm-node/localisedUrls/redirect.mjs +54 -0
  100. package/dist/esm-node/localisedUrls/resolve.mjs +38 -0
  101. package/dist/esm-node/localisedUrls/routeIdentity.mjs +55 -0
  102. package/dist/esm-node/localisedUrls/routes.mjs +177 -0
  103. package/dist/esm-node/localisedUrls/types.mjs +1 -0
  104. package/dist/esm-node/prefetchPolicy.mjs +12 -0
  105. package/dist/esm-node/rendererHead.mjs +14 -5
  106. package/dist/esm-node/request-policy/browser.mjs +12 -0
  107. package/dist/esm-node/request-policy/client.mjs +196 -0
  108. package/dist/esm-node/request-policy/node.mjs +22 -0
  109. package/dist/esm-node/request-policy/policyCore.mjs +172 -0
  110. package/dist/esm-node/request-policy/requestContext.mjs +76 -0
  111. package/dist/esm-node/request-policy/traceparent.mjs +19 -0
  112. package/dist/esm-node/request-policy/transport.mjs +197 -0
  113. package/dist/esm-node/request-policy/types.mjs +11 -0
  114. package/dist/esm-node/routeHydration.mjs +78 -0
  115. package/dist/esm-node/routerPrefetchPolicy.mjs +116 -0
  116. package/dist/esm-node/routerProvider.mjs +59 -0
  117. package/dist/esm-node/routerState.mjs +114 -0
  118. package/dist/esm-node/routerStatePlugin.mjs +76 -0
  119. package/dist/esm-node/routerStateTypes.mjs +1 -0
  120. package/dist/esm-node/rsc-disabled/rscDisabledClientBrowserRuntime.mjs +11 -0
  121. package/dist/esm-node/rsc-disabled/rscDisabledClientEdgeRuntime.mjs +9 -0
  122. package/dist/esm-node/rsc-disabled/rscDisabledClientNodeRuntime.mjs +10 -0
  123. package/dist/esm-node/rsc-disabled/rscDisabledRuntimeError.mjs +5 -0
  124. package/dist/esm-node/rsc-disabled/rscDisabledServerEdgeRuntime.mjs +17 -0
  125. package/dist/esm-node/rsc-disabled/rscDisabledServerNodeRuntime.mjs +19 -0
  126. package/dist/esm-node/runtimeContextProjection.mjs +75 -0
  127. package/dist/esm-node/server-config/bffRuntime.mjs +1 -0
  128. package/dist/esm-node/server-config/index.mjs +1 -0
  129. package/dist/esm-node/server-config/serverTelemetry.mjs +1 -0
  130. package/dist/esm-node/shellStream.mjs +34 -2
  131. package/dist/esm-node/workspaceEvents.mjs +67 -0
  132. package/dist/types/buildIdentity.d.ts +37 -0
  133. package/dist/types/contextExtensions.d.ts +34 -0
  134. package/dist/types/headRuntime.d.ts +12 -0
  135. package/dist/types/helmetCompat.d.ts +1 -1
  136. package/dist/types/helmetContext.d.ts +10 -0
  137. package/dist/types/index.d.ts +2 -0
  138. package/dist/types/localisedLoaderIdentity.d.ts +10 -0
  139. package/dist/types/localisedUrls/config.d.ts +9 -0
  140. package/dist/types/localisedUrls/index.d.ts +10 -0
  141. package/dist/types/localisedUrls/normalise.d.ts +10 -0
  142. package/dist/types/localisedUrls/pathname.d.ts +4 -0
  143. package/dist/types/localisedUrls/patterns.d.ts +11 -0
  144. package/dist/types/localisedUrls/redirect.d.ts +19 -0
  145. package/dist/types/localisedUrls/resolve.d.ts +7 -0
  146. package/dist/types/localisedUrls/routeIdentity.d.ts +22 -0
  147. package/dist/types/localisedUrls/routes.d.ts +3 -0
  148. package/dist/types/localisedUrls/types.d.ts +14 -0
  149. package/dist/types/prefetchPolicy.d.ts +4 -0
  150. package/dist/types/rendererHead.d.ts +5 -3
  151. package/dist/types/request-policy/browser.d.ts +10 -0
  152. package/dist/types/request-policy/client.d.ts +14 -0
  153. package/dist/types/request-policy/node.d.ts +10 -0
  154. package/dist/types/request-policy/policyCore.d.ts +107 -0
  155. package/dist/types/request-policy/requestContext.d.ts +22 -0
  156. package/dist/types/request-policy/traceparent.d.ts +10 -0
  157. package/dist/types/request-policy/transport.d.ts +12 -0
  158. package/dist/types/request-policy/types.d.ts +144 -0
  159. package/dist/types/routeHydration.d.ts +24 -0
  160. package/dist/types/routerPrefetchPolicy.d.ts +28 -0
  161. package/dist/types/routerProvider.d.ts +55 -0
  162. package/dist/types/routerState.d.ts +17 -0
  163. package/dist/types/routerStatePlugin.d.ts +19 -0
  164. package/dist/types/routerStateTypes.d.ts +70 -0
  165. package/dist/types/rsc-disabled/rscDisabledClientBrowserRuntime.d.ts +9 -0
  166. package/dist/types/rsc-disabled/rscDisabledClientEdgeRuntime.d.ts +7 -0
  167. package/dist/types/rsc-disabled/rscDisabledClientNodeRuntime.d.ts +8 -0
  168. package/dist/types/rsc-disabled/rscDisabledRuntimeError.d.ts +1 -0
  169. package/dist/types/rsc-disabled/rscDisabledServerEdgeRuntime.d.ts +15 -0
  170. package/dist/types/rsc-disabled/rscDisabledServerNodeRuntime.d.ts +17 -0
  171. package/dist/types/runtimeContextProjection.d.ts +30 -0
  172. package/dist/types/server-config/bffRuntime.d.ts +122 -0
  173. package/dist/types/server-config/index.d.ts +2 -0
  174. package/dist/types/server-config/serverTelemetry.d.ts +322 -0
  175. package/dist/types/workspaceEvents.d.ts +62 -0
  176. package/package.json +240 -15
  177. package/rslib.config.mts +17 -2
  178. package/rstest.config.mts +33 -2
  179. package/src/buildIdentity.ts +61 -0
  180. package/src/contextExtensions.ts +81 -0
  181. package/src/headRuntime.tsx +77 -0
  182. package/src/helmetCompat.ts +1 -1
  183. package/src/helmetContext.ts +31 -0
  184. package/src/index.ts +2 -0
  185. package/src/localisedLoaderIdentity.ts +30 -0
  186. package/src/localisedUrls/config.ts +18 -0
  187. package/src/localisedUrls/index.ts +25 -0
  188. package/src/localisedUrls/normalise.ts +62 -0
  189. package/src/localisedUrls/pathname.ts +60 -0
  190. package/src/localisedUrls/patterns.ts +283 -0
  191. package/src/localisedUrls/redirect.ts +107 -0
  192. package/src/localisedUrls/resolve.ts +106 -0
  193. package/src/localisedUrls/routeIdentity.ts +83 -0
  194. package/src/localisedUrls/routes.ts +451 -0
  195. package/src/localisedUrls/types.ts +18 -0
  196. package/src/prefetchPolicy.ts +33 -0
  197. package/src/rendererHead.ts +25 -8
  198. package/src/request-policy/browser.ts +22 -0
  199. package/src/request-policy/client.ts +404 -0
  200. package/src/request-policy/node.ts +41 -0
  201. package/src/request-policy/policyCore.ts +424 -0
  202. package/src/request-policy/requestContext.ts +149 -0
  203. package/src/request-policy/traceparent.ts +43 -0
  204. package/src/request-policy/transport.ts +349 -0
  205. package/src/request-policy/types.ts +229 -0
  206. package/src/routeHydration.ts +208 -0
  207. package/src/routerPrefetchPolicy.ts +195 -0
  208. package/src/routerProvider.ts +160 -0
  209. package/src/routerState.ts +220 -0
  210. package/src/routerStatePlugin.ts +125 -0
  211. package/src/routerStateTypes.ts +76 -0
  212. package/src/rsc-disabled/rscDisabledClientBrowserRuntime.ts +10 -0
  213. package/src/rsc-disabled/rscDisabledClientEdgeRuntime.ts +8 -0
  214. package/src/rsc-disabled/rscDisabledClientNodeRuntime.ts +9 -0
  215. package/src/rsc-disabled/rscDisabledRuntimeError.ts +3 -0
  216. package/src/rsc-disabled/rscDisabledServerEdgeRuntime.ts +16 -0
  217. package/src/rsc-disabled/rscDisabledServerNodeRuntime.ts +18 -0
  218. package/src/runtimeContextProjection.ts +105 -0
  219. package/src/server-config/bffRuntime.ts +136 -0
  220. package/src/server-config/index.ts +2 -0
  221. package/src/server-config/serverTelemetry.ts +337 -0
  222. package/src/shellStream.ts +55 -25
  223. package/src/workspaceEvents.ts +324 -0
  224. package/tests/buildIdentity.test.ts +51 -0
  225. package/tests/contextExtensions.test.ts +27 -0
  226. package/tests/headRuntime.test.tsx +229 -0
  227. package/tests/localisedUrls.test.ts +560 -0
  228. package/tests/rendererHead.test.ts +52 -121
  229. package/tests/request-policy/browser-policy.test.ts +108 -0
  230. package/tests/request-policy/client-isolation.test.ts +109 -0
  231. package/tests/request-policy/node-policy.test.ts +447 -0
  232. package/tests/request-policy/policyCore.test.ts +129 -0
  233. package/tests/request-policy/requestContext.test.ts +66 -0
  234. package/tests/request-policy/requestFactory.outbound-contract.test.ts +290 -0
  235. package/tests/request-policy/traceparent.test.ts +52 -0
  236. package/tests/request-policy/transport-retry.test.ts +367 -0
  237. package/tests/routeHydration.test.ts +180 -0
  238. package/tests/routerPrefetchPolicy.test.ts +102 -0
  239. package/tests/routerProvider.test.ts +29 -0
  240. package/tests/routerState.test.ts +77 -0
  241. package/tests/routerStatePlugin.test.ts +63 -0
  242. package/tests/runtimeContextProjection.test.ts +83 -0
  243. package/tests/safeFailure.test.ts +10 -28
  244. package/tests/shellStream.test.ts +39 -2
  245. package/tests/workspaceEvents.test.ts +221 -0
  246. package/dist/cjs/rscHtmlStream.js +0 -128
  247. package/dist/esm/rscHtmlStream.mjs +0 -86
  248. package/dist/esm-node/rscHtmlStream.mjs +0 -87
  249. package/dist/types/rscHtmlStream.d.ts +0 -4
  250. package/src/rscHtmlStream.ts +0 -142
  251. package/tests/rscHtmlStream.test.ts +0 -153
@@ -5,14 +5,10 @@ import {
5
5
  collectHeadRecord,
6
6
  completeHeadRender,
7
7
  createHeadChunkProcessor,
8
- createWebHeadMarkerStripper,
8
+ LATE_HEAD_MESSAGE,
9
9
  publishHeadRender,
10
10
  } from '../src';
11
- import {
12
- createNodeHeadMarkerStripper,
13
- createOnceErrorReporter,
14
- pipeNodeHeadStream,
15
- } from '../src/node';
11
+ import { createNodeHeadMarkerStripper, pipeNodeHeadStream } from '../src/node';
16
12
 
17
13
  const marker = (props: Record<string, string>) =>
18
14
  `<template data-modern-helmet="${props['data-modern-helmet']}"></template>`;
@@ -21,7 +17,7 @@ describe('renderer head transactions', () => {
21
17
  it('commits only markers observed in completed output', () => {
22
18
  const context = {};
23
19
  let published: string[] = [];
24
- beginHeadRender(context);
20
+ beginHeadRender(context, () => {});
25
21
  const committed = collectHeadRecord(
26
22
  context,
27
23
  () => 'committed',
@@ -41,28 +37,59 @@ describe('renderer head transactions', () => {
41
37
  expect(published).toEqual(['committed']);
42
38
  });
43
39
 
44
- it('preserves user templates that are not current transaction markers', () => {
40
+ it('reports head records whose markers commit after the seal', () => {
45
41
  const context = {};
46
- beginHeadRender(context);
47
- const userTemplate =
48
- '<template data-modern-helmet="h00000000-0000-0000-0000-000000000000000000000000"></template>';
42
+ const reports: string[] = [];
43
+ let published: string[] = [];
44
+ const publish = (records: string[]) => {
45
+ published = records;
46
+ };
47
+ beginHeadRender(context, message => reports.push(message));
48
+ const shell = collectHeadRecord(context, () => 'shell', publish)!;
49
+ // A boundary rendered before the seal but flushed after it.
50
+ const flushedLate = collectHeadRecord(context, () => 'flushed', publish)!;
51
+ const processor = createHeadChunkProcessor(context);
52
+ expect(processor.push(`a${marker(shell)}`)).toBe('a');
53
+ publishHeadRender(context);
54
+ // A boundary rendered after the seal.
55
+ const renderedLate = collectHeadRecord(context, () => 'rendered', publish)!;
56
+ // A retry that renders a Helmet and then suspends again never commits.
57
+ collectHeadRecord(context, () => 'discarded', publish);
58
+ expect(reports).toEqual([]);
49
59
 
50
- expect(completeHeadRender(context, userTemplate)).toBe(userTemplate);
60
+ expect(processor.push(`b${marker(flushedLate)}${marker(shell)}`)).toBe('b');
61
+ expect(processor.finish(`c${marker(renderedLate)}`)).toBe('c');
62
+
63
+ expect(published).toEqual(['shell']);
64
+ expect(reports).toEqual([LATE_HEAD_MESSAGE]);
51
65
  });
52
66
 
53
- it('does not retain ordinary streaming content', () => {
67
+ it('does not report a late Helmet whose boundary never commits', () => {
54
68
  const context = {};
55
- beginHeadRender(context);
56
- const processor = createHeadChunkProcessor(context);
57
- const content = 'x'.repeat(128 * 1024);
69
+ const reports: string[] = [];
70
+ beginHeadRender(context, message => reports.push(message));
71
+ publishHeadRender(context);
72
+ collectHeadRecord(
73
+ context,
74
+ () => 'discarded',
75
+ () => {},
76
+ );
77
+ expect(completeHeadRender(context, 'done')).toBe('done');
78
+ expect(reports).toEqual([]);
79
+ });
58
80
 
59
- expect(processor.push(content)).toBe(content);
60
- expect(processor.finish()).toBe('');
81
+ it('preserves user templates that are not current transaction markers', () => {
82
+ const context = {};
83
+ beginHeadRender(context, () => {});
84
+ const userTemplate =
85
+ '<template data-modern-helmet="h00000000-0000-0000-0000-000000000000000000000000"></template>';
86
+
87
+ expect(completeHeadRender(context, userTemplate)).toBe(userTemplate);
61
88
  });
62
89
 
63
90
  it('strips markers split across chunks without corrupting unicode', () => {
64
91
  const sampleContext = {};
65
- beginHeadRender(sampleContext);
92
+ beginHeadRender(sampleContext, () => {});
66
93
  const sampleProps = collectHeadRecord(
67
94
  sampleContext,
68
95
  () => 'head',
@@ -72,7 +99,7 @@ describe('renderer head transactions', () => {
72
99
 
73
100
  for (let split = 1; split < sampleHtml.length; split += 1) {
74
101
  const context = {};
75
- beginHeadRender(context);
102
+ beginHeadRender(context, () => {});
76
103
  const props = collectHeadRecord(
77
104
  context,
78
105
  () => 'head',
@@ -87,41 +114,6 @@ describe('renderer head transactions', () => {
87
114
  }
88
115
  });
89
116
 
90
- it('freezes the published shell and ignores late records', () => {
91
- const context = {};
92
- let published: string[] = [];
93
- beginHeadRender(context);
94
- const shell = collectHeadRecord(
95
- context,
96
- () => 'shell',
97
- records => {
98
- published = records;
99
- },
100
- )!;
101
- const late = collectHeadRecord(
102
- context,
103
- () => 'late',
104
- records => {
105
- published = records;
106
- },
107
- )!;
108
- const processor = createHeadChunkProcessor(context);
109
- expect(processor.push(marker(shell))).toBe('');
110
- publishHeadRender(context);
111
-
112
- expect(
113
- collectHeadRecord(
114
- context,
115
- () => 'late',
116
- records => {
117
- published = records;
118
- },
119
- ),
120
- ).toBeNull();
121
- expect(processor.finish(marker(late))).toBe('');
122
- expect(published).toEqual(['shell']);
123
- });
124
-
125
117
  it('clears the previous snapshot while rendering and restores it on abort', () => {
126
118
  const context = {};
127
119
  let published: string[] = [];
@@ -129,52 +121,22 @@ describe('renderer head transactions', () => {
129
121
  published = records;
130
122
  };
131
123
 
132
- beginHeadRender(context);
124
+ beginHeadRender(context, () => {});
133
125
  const previous = collectHeadRecord(context, () => 'previous', publish)!;
134
126
  completeHeadRender(context, marker(previous));
135
127
  expect(published).toEqual(['previous']);
136
128
 
137
- beginHeadRender(context);
129
+ beginHeadRender(context, () => {});
138
130
  expect(published).toEqual([]);
139
131
  collectHeadRecord(context, () => 'provisional', publish);
140
132
  abortHeadRender(context);
141
133
  expect(published).toEqual(['previous']);
142
134
  });
143
135
 
144
- it('strips split markers from a web byte stream', async () => {
145
- const context = {};
146
- let published: string[] = [];
147
- beginHeadRender(context);
148
- const props = collectHeadRecord(
149
- context,
150
- () => 'web',
151
- records => {
152
- published = records;
153
- },
154
- )!;
155
- const html = `č${marker(props)}尾`;
156
- const encoder = new TextEncoder();
157
- const encoded = encoder.encode(html);
158
- const source = new ReadableStream<Uint8Array>({
159
- start(controller) {
160
- controller.enqueue(encoded.slice(0, 1));
161
- controller.enqueue(encoded.slice(1, 17));
162
- controller.enqueue(encoded.slice(17));
163
- controller.close();
164
- },
165
- });
166
-
167
- const output = await new Response(
168
- source.pipeThrough(createWebHeadMarkerStripper(context)),
169
- ).text();
170
- expect(output).toBe('č尾');
171
- expect(published).toEqual(['web']);
172
- });
173
-
174
136
  it('strips split markers from a Node stream', async () => {
175
137
  const context = {};
176
138
  let published: string[] = [];
177
- beginHeadRender(context);
139
+ beginHeadRender(context, () => {});
178
140
  const props = collectHeadRecord(
179
141
  context,
180
142
  () => 'node',
@@ -208,7 +170,7 @@ describe('renderer head transactions', () => {
208
170
  const prefix = Buffer.from('x'.repeat(9 * 1024));
209
171
  const unicodeTail = Buffer.from('č尾');
210
172
  const input = Buffer.concat([prefix, unicodeTail]);
211
- beginHeadRender(context);
173
+ beginHeadRender(context, () => {});
212
174
  const stripper = createNodeHeadMarkerStripper(context, terminalMarker);
213
175
  const output = new Promise<string>((resolve, reject) => {
214
176
  let rendered = '';
@@ -233,7 +195,7 @@ describe('renderer head transactions', () => {
233
195
  const terminalMarker = '<!-- shell stream end -->';
234
196
  const input = Buffer.from(`shell${terminalMarker}č尾`);
235
197
  const markerOffset = Buffer.byteLength('shell') + 7;
236
- beginHeadRender(context);
198
+ beginHeadRender(context, () => {});
237
199
  const stripper = createNodeHeadMarkerStripper(context, terminalMarker);
238
200
  const output = new Promise<string>((resolve, reject) => {
239
201
  let rendered = '';
@@ -265,35 +227,4 @@ describe('renderer head transactions', () => {
265
227
  await expect(output).rejects.toThrow('source failed');
266
228
  expect(onError).toHaveBeenCalledTimes(1);
267
229
  });
268
-
269
- it('rejects the destination and reports a stripper error exactly once', async () => {
270
- const source = new PassThrough();
271
- const destination = new PassThrough();
272
- const output = new Response(
273
- Readable.toWeb(destination) as ReadableStream<Uint8Array>,
274
- ).text();
275
- const onError = rstest.fn();
276
- pipeNodeHeadStream({
277
- source,
278
- destination,
279
- context: Object.freeze({}),
280
- onError,
281
- });
282
-
283
- source.end('trigger frozen-context failure');
284
-
285
- await expect(output).rejects.toThrow('object is not extensible');
286
- expect(onError).toHaveBeenCalledTimes(1);
287
- });
288
-
289
- it('reports a shared render and stream failure only once', () => {
290
- const onError = rstest.fn();
291
- const reportError = createOnceErrorReporter(onError);
292
- const failure = new Error('shared failure');
293
-
294
- reportError(failure);
295
- reportError(failure);
296
-
297
- expect(onError).toHaveBeenCalledExactlyOnceWith(failure);
298
- });
299
230
  });
@@ -0,0 +1,108 @@
1
+ import {
2
+ configure,
3
+ createRequest,
4
+ } from '@modern-js/runtime-extensions/request-policy/client';
5
+ import nock from 'nock';
6
+
7
+ describe('configure', () => {
8
+ const path = '/api';
9
+ const method = 'GET';
10
+ const response = {
11
+ code: 200,
12
+ data: {
13
+ message: 'hello Modernjs',
14
+ },
15
+ };
16
+
17
+ test('should allow explicit cross-origin policy and attach envelope header', async () => {
18
+ const producer = 'producer-browser-policy';
19
+ const producerUrl = 'https://producer.internal';
20
+ const previousEnv = process.env.NODE_ENV;
21
+ process.env.NODE_ENV = 'production';
22
+
23
+ try {
24
+ nock(producerUrl).get(path).reply(200, response);
25
+ const customRequest = rs.fn(
26
+ (requestPath: RequestInfo, init?: RequestInit) =>
27
+ fetch(requestPath, init),
28
+ );
29
+
30
+ configure({
31
+ request: customRequest,
32
+ requestId: producer,
33
+ operationContract: {
34
+ enabled: false,
35
+ },
36
+ setDomain: () => producerUrl,
37
+ allowCrossOriginEnvelope: true,
38
+ });
39
+
40
+ const request = createRequest(
41
+ path,
42
+ method,
43
+ 8080,
44
+ undefined,
45
+ undefined,
46
+ producer,
47
+ );
48
+ const res = await request();
49
+
50
+ const headers = customRequest.mock.calls[0][1].headers as Record<
51
+ string,
52
+ string
53
+ >;
54
+ const envelope = JSON.parse(headers['x-modernjs-bff-envelope']);
55
+ expect(envelope.requestId).toBe(producer);
56
+ expect(envelope.target).toBe('browser');
57
+ expect(res instanceof Response).toBe(true);
58
+ } finally {
59
+ process.env.NODE_ENV = previousEnv;
60
+ }
61
+ });
62
+
63
+ test('should not require process to exist in browser runtime', async () => {
64
+ const previousProcess = globalThis.process;
65
+ const customRequest = rs.fn(() =>
66
+ Promise.resolve(
67
+ new Response(
68
+ JSON.stringify({
69
+ code: 200,
70
+ data: {
71
+ message: 'ok',
72
+ },
73
+ }),
74
+ ),
75
+ ),
76
+ );
77
+
78
+ try {
79
+ Object.defineProperty(globalThis, 'process', {
80
+ configurable: true,
81
+ value: undefined,
82
+ });
83
+ configure({
84
+ request: customRequest,
85
+ });
86
+
87
+ const request = createRequest({
88
+ path,
89
+ method,
90
+ port: 8080,
91
+ });
92
+ const response = await request();
93
+ const data = await response.json();
94
+
95
+ expect(data).toStrictEqual({
96
+ code: 200,
97
+ data: {
98
+ message: 'ok',
99
+ },
100
+ });
101
+ } finally {
102
+ Object.defineProperty(globalThis, 'process', {
103
+ configurable: true,
104
+ value: previousProcess,
105
+ });
106
+ }
107
+ });
108
+ });
@@ -0,0 +1,109 @@
1
+ import { createClient as createNativeClient } from '@modern-js/create-request/server';
2
+ import * as server from '@modern-js/runtime-extensions/request-policy/server';
3
+
4
+ const options = {
5
+ path: '/api',
6
+ method: 'GET',
7
+ port: 8080,
8
+ requestId: 'shared-producer',
9
+ };
10
+
11
+ describe('isolated producer policy clients', () => {
12
+ test('does not share producer initialization or request configuration between instances', async () => {
13
+ const first = server.createClient();
14
+ const second = server.createClient();
15
+ const firstRequest = rs.fn(async () => new Response('first'));
16
+ const secondRequest = rs.fn(async () => new Response('second'));
17
+ first.configure({
18
+ requestId: options.requestId,
19
+ request: firstRequest,
20
+ setDomain: () => 'https://first.example',
21
+ operationContract: { enabled: false },
22
+ });
23
+ expect(() => second.createRequest(options)()).toThrow(
24
+ server.ProducerClientNotInitializedError,
25
+ );
26
+ second.configure({
27
+ requestId: options.requestId,
28
+ request: secondRequest,
29
+ setDomain: () => 'https://second.example',
30
+ operationContract: { enabled: false },
31
+ });
32
+ expect(await (await first.createRequest(options)()).text()).toBe('first');
33
+ expect(await (await second.createRequest(options)()).text()).toBe('second');
34
+ expect(firstRequest).toHaveBeenCalledWith(
35
+ 'https://first.example/api',
36
+ expect.any(Object),
37
+ );
38
+ expect(secondRequest).toHaveBeenCalledWith(
39
+ 'https://second.example/api',
40
+ expect.any(Object),
41
+ );
42
+ });
43
+
44
+ test('does not initialize producer policy by configuring a separate native client', () => {
45
+ createNativeClient().configure({
46
+ requestId: options.requestId,
47
+ request: rs.fn(),
48
+ });
49
+ const producer = server.createClient();
50
+ expect(() => producer.createRequest(options)()).toThrow(
51
+ server.ProducerClientNotInitializedError,
52
+ );
53
+ });
54
+
55
+ test('retains secured behavior for an explicitly empty requestId', () => {
56
+ const client = server.createClient();
57
+ const request = rs.fn();
58
+ client.configure({
59
+ requestId: '',
60
+ request,
61
+ setDomain: () => 'https://producer.example',
62
+ });
63
+ expect(() => client.createRequest({ ...options, requestId: '' })()).toThrow(
64
+ server.OperationContractViolationError,
65
+ );
66
+ expect(request).not.toHaveBeenCalled();
67
+ });
68
+
69
+ test('calls setDomain once after interceptor and keeps native and policy state on rejection', async () => {
70
+ const client = server.createClient();
71
+ const order: string[] = [];
72
+ const request = rs.fn(async () => new Response('original'));
73
+ const setDomain = rs.fn(() => {
74
+ order.push('domain');
75
+ return 'https://original.example';
76
+ });
77
+ client.configure({
78
+ requestId: options.requestId,
79
+ interceptor: () => {
80
+ order.push('interceptor');
81
+ return request;
82
+ },
83
+ setDomain,
84
+ requireEnvelope: false,
85
+ operationContract: { enabled: false },
86
+ });
87
+ const replacement = rs.fn();
88
+ expect(() =>
89
+ client.configure({
90
+ requestId: options.requestId,
91
+ request: replacement,
92
+ setDomain: () => '',
93
+ }),
94
+ ).toThrow(server.ProducerDomainNotConfiguredError);
95
+ const response = await client.createRequest(options)();
96
+ expect(await response.text()).toBe('original');
97
+ expect(order).toEqual(['interceptor', 'domain']);
98
+ expect(setDomain).toHaveBeenCalledTimes(1);
99
+ expect(request).toHaveBeenCalledWith(
100
+ 'https://original.example/api',
101
+ expect.objectContaining({
102
+ headers: expect.not.objectContaining({
103
+ 'x-modernjs-bff-envelope': expect.anything(),
104
+ }),
105
+ }),
106
+ );
107
+ expect(replacement).not.toHaveBeenCalled();
108
+ });
109
+ });