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

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
@@ -0,0 +1,221 @@
1
+ import * as contracts from '../src/workspaceEvents';
2
+
3
+ const cases = [
4
+ {
5
+ name: 'ultramodern:navigate',
6
+ valid: { to: '/dashboard', replace: false, state: { from: 'shell' } },
7
+ invalid: [
8
+ {},
9
+ { to: '' },
10
+ { to: ' ' },
11
+ { to: 42 },
12
+ { to: '/', replace: 'false' },
13
+ { to: '/', state: [] },
14
+ { to: '/', state: null },
15
+ ],
16
+ validate: contracts.isUltramodernNavigatePayload,
17
+ dispatch: contracts.dispatchUltramodernNavigate,
18
+ on: contracts.onUltramodernNavigate,
19
+ },
20
+ {
21
+ name: 'ultramodern:route-settled',
22
+ valid: { pathname: '/cs', locale: 'cs', title: 'Home' },
23
+ invalid: [
24
+ {},
25
+ { pathname: ' ' },
26
+ { pathname: '/', locale: 'de' },
27
+ { pathname: '/', locale: null },
28
+ { pathname: '/', title: '' },
29
+ { pathname: '/', title: 42 },
30
+ ],
31
+ validate: contracts.isUltramodernRouteSettledPayload,
32
+ dispatch: contracts.dispatchUltramodernRouteSettled,
33
+ on: contracts.onUltramodernRouteSettled,
34
+ },
35
+ {
36
+ name: 'ultramodern:remote-ready',
37
+ valid: { appId: 'catalog', build: 'a', surface: 'ui', version: '1' },
38
+ invalid: [
39
+ {},
40
+ { appId: 42 },
41
+ { appId: ' ' },
42
+ { appId: 'catalog', build: '' },
43
+ { appId: 'catalog', surface: null },
44
+ { appId: 'catalog', version: 1 },
45
+ ],
46
+ validate: contracts.isUltramodernRemoteReadyPayload,
47
+ dispatch: contracts.dispatchUltramodernRemoteReady,
48
+ on: contracts.onUltramodernRemoteReady,
49
+ },
50
+ {
51
+ name: 'ultramodern:performance-signal',
52
+ valid: {
53
+ signalId: 'bfcache',
54
+ status: 'pass',
55
+ durationMs: 0,
56
+ detail: { source: 'browser' },
57
+ },
58
+ invalid: [
59
+ {},
60
+ { signalId: 'unknown', status: 'pass' },
61
+ { signalId: 'bfcache', status: 'unknown' },
62
+ ...[-1, Number.NaN, Number.POSITIVE_INFINITY, '1', null].map(
63
+ durationMs => ({ signalId: 'bfcache', status: 'pass', durationMs }),
64
+ ),
65
+ { signalId: 'bfcache', status: 'pass', detail: [] },
66
+ { signalId: 'bfcache', status: 'pass', detail: null },
67
+ ],
68
+ validate: contracts.isUltramodernPerformanceSignalPayload,
69
+ dispatch: contracts.dispatchUltramodernPerformanceSignal,
70
+ on: contracts.onUltramodernPerformanceSignal,
71
+ },
72
+ ] as const;
73
+
74
+ describe('workspace event contracts', () => {
75
+ for (const { name, valid, invalid, validate, dispatch, on } of cases) {
76
+ it(`${name} preserves schema validation and error data`, () => {
77
+ expect(validate(valid)).toBe(true);
78
+ expect(
79
+ contracts.assertUltramodernWorkspaceEventPayload(name, valid),
80
+ ).toBe(valid);
81
+ for (const payload of [null, undefined, [], false, 'text', ...invalid]) {
82
+ expect(validate(payload)).toBe(false);
83
+ expect(
84
+ contracts.isUltramodernWorkspaceEventPayload(name, payload),
85
+ ).toBe(false);
86
+ let error: unknown;
87
+ try {
88
+ contracts.assertUltramodernWorkspaceEventPayload(name, payload);
89
+ } catch (caught) {
90
+ error = caught;
91
+ }
92
+ expect(error).toBeInstanceOf(
93
+ contracts.UltramodernWorkspaceEventValidationError,
94
+ );
95
+ expect(error).toMatchObject({
96
+ name: 'UltramodernWorkspaceEventValidationError',
97
+ eventName: name,
98
+ payload,
99
+ });
100
+ }
101
+ });
102
+
103
+ it(`${name} uses native dispatch, preserves payload identity and cleans up`, () => {
104
+ const target = new EventTarget();
105
+ const received: unknown[] = [];
106
+ const handler = (payload: unknown, event: CustomEvent<unknown>) => {
107
+ expect(event).toBeInstanceOf(CustomEvent);
108
+ expect(event.type).toBe(name);
109
+ expect(event.bubbles).toBe(true);
110
+ expect(event.composed).toBe(true);
111
+ expect(event.cancelable).toBe(false);
112
+ expect(event.target).toBe(target);
113
+ expect(event.detail).toBe(payload);
114
+ received.push(payload);
115
+ };
116
+ const unsubscribe = on(target, handler);
117
+ // Each table row correlates the convenience dispatch with its own schema.
118
+ const dispatchPayload = dispatch as (
119
+ target: EventTarget,
120
+ payload: typeof valid,
121
+ ) => boolean;
122
+ expect(dispatchPayload(target, valid)).toBe(true);
123
+ expect(received).toEqual([valid]);
124
+ expect(received[0]).toBe(valid);
125
+ unsubscribe();
126
+ unsubscribe();
127
+ dispatchPayload(target, valid);
128
+ expect(received).toEqual([valid]);
129
+ });
130
+ }
131
+
132
+ it('accepts consumer-owned fields, explicit undefined optionals and every signal id', () => {
133
+ const payload = {
134
+ to: '/raw',
135
+ replace: undefined,
136
+ state: undefined,
137
+ extension: 'consumer-owned',
138
+ };
139
+ expect(
140
+ contracts.assertUltramodernWorkspaceEventPayload(
141
+ 'ultramodern:navigate',
142
+ payload,
143
+ ),
144
+ ).toBe(payload);
145
+ expect(
146
+ contracts.isUltramodernRouteSettledPayload({
147
+ pathname: '/',
148
+ locale: undefined,
149
+ title: undefined,
150
+ }),
151
+ ).toBe(true);
152
+ for (const signalId of [
153
+ 'bfcache',
154
+ 'core-web-vitals-rum',
155
+ 'duplicate-prefetch-warmup',
156
+ 'cache-policy-sanity',
157
+ 'save-data-behavior',
158
+ 'cloudflare-ssr-cache-hints',
159
+ ]) {
160
+ expect(
161
+ contracts.isUltramodernPerformanceSignalPayload({
162
+ signalId,
163
+ status: 'warn',
164
+ }),
165
+ ).toBe(true);
166
+ }
167
+ });
168
+
169
+ it('accepts detail-carrying events from another realm', () => {
170
+ const target = new EventTarget();
171
+ const add = rstest.spyOn(target, 'addEventListener');
172
+ const handler = rstest.fn();
173
+ const unsubscribe = contracts.onUltramodernNavigate(target, handler);
174
+ const listener = add.mock.calls[0]?.[1] as EventListener;
175
+ // A CustomEvent from an iframe/foreign realm fails `instanceof`; the
176
+ // listener must duck-type on `detail` instead of rejecting it.
177
+ const payload = { to: '/foreign' };
178
+ listener({ type: 'ultramodern:navigate', detail: payload } as never);
179
+ expect(handler).toHaveBeenCalledTimes(1);
180
+ expect(handler.mock.calls[0]?.[0]).toBe(payload);
181
+ unsubscribe();
182
+ add.mockRestore();
183
+ });
184
+
185
+ it('rejects invalid dispatch before notifying listeners', () => {
186
+ const target = new EventTarget();
187
+ const handler = rstest.fn();
188
+ const unsubscribe = contracts.onUltramodernNavigate(target, handler);
189
+ expect(() =>
190
+ contracts.dispatchUltramodernNavigate(target, { to: '' }),
191
+ ).toThrow();
192
+ expect(handler).not.toHaveBeenCalled();
193
+ unsubscribe();
194
+ });
195
+
196
+ it('validates incoming event details and removes only its own listener', () => {
197
+ const target = new EventTarget();
198
+ const add = rstest.spyOn(target, 'addEventListener');
199
+ const remove = rstest.spyOn(target, 'removeEventListener');
200
+ const handler = rstest.fn();
201
+ const first = contracts.onUltramodernNavigate(target, handler);
202
+ const listener = add.mock.calls[0]?.[1] as EventListener;
203
+ for (const event of [
204
+ new Event('ultramodern:navigate'),
205
+ new CustomEvent('ultramodern:navigate', { detail: { to: '' } }),
206
+ ]) {
207
+ // Native dispatch reports listener errors asynchronously; invoke the
208
+ // registered listener directly to inspect the precise validation failure.
209
+ expect(() => listener(event)).toThrow();
210
+ }
211
+ expect(handler).not.toHaveBeenCalled();
212
+ const second = contracts.onUltramodernNavigate(target, handler);
213
+ first();
214
+ expect(remove).toHaveBeenCalledWith('ultramodern:navigate', listener);
215
+ contracts.dispatchUltramodernNavigate(target, { to: '/still-subscribed' });
216
+ expect(handler).toHaveBeenCalledTimes(1);
217
+ second();
218
+ add.mockRestore();
219
+ remove.mockRestore();
220
+ });
221
+ });
@@ -1,128 +0,0 @@
1
- "use strict";
2
- var __webpack_require__ = {};
3
- (()=>{
4
- __webpack_require__.d = (exports1, getters, values)=>{
5
- var define = (defs, kind)=>{
6
- for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
7
- enumerable: true,
8
- [kind]: defs[key]
9
- });
10
- };
11
- define(getters, "get");
12
- define(values, "value");
13
- };
14
- })();
15
- (()=>{
16
- __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
17
- })();
18
- (()=>{
19
- __webpack_require__.r = (exports1)=>{
20
- if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
21
- value: 'Module'
22
- });
23
- Object.defineProperty(exports1, '__esModule', {
24
- value: true
25
- });
26
- };
27
- })();
28
- var __webpack_exports__ = {};
29
- __webpack_require__.r(__webpack_exports__);
30
- const encoder = new TextEncoder();
31
- const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
32
- const closingTagsPrefixPattern = /(?:<\/body>\s*(?:<\/html>\s*|<\/h(?:t(?:m(?:l>?)?)?)?|<\/?|<)?|<\/b(?:o(?:d(?:y>?)?)?)?|<\/?|<[^>]*)$/i;
33
- const RSC_HTML_CLOSING_TAIL_LIMIT = 8192;
34
- function injectRSCPayload(rscStream, { injectClosingTags = true } = {}) {
35
- const decoder = new TextDecoder();
36
- let retainedTail = '';
37
- let committedClosingBody = false;
38
- let committedHtmlClose = false;
39
- let committedProbe = '';
40
- let flightPromise;
41
- const startFlight = (controller)=>{
42
- if (!flightPromise) {
43
- flightPromise = writeRSCStream(rscStream, controller);
44
- flightPromise.catch((error)=>controller.error(error));
45
- }
46
- return flightPromise;
47
- };
48
- const enqueueHtml = (value, controller)=>{
49
- if (value) controller.enqueue(encoder.encode(value));
50
- };
51
- const processHtml = async (value, controller)=>{
52
- if (!value) return;
53
- if (committedClosingBody) {
54
- const probe = committedProbe + value;
55
- committedHtmlClose ||= /<\/html>/i.test(probe);
56
- committedProbe = probe.slice(-6);
57
- enqueueHtml(value, controller);
58
- return;
59
- }
60
- const html = retainedTail + value;
61
- retainedTail = '';
62
- const suffix = html.match(closingTagsPrefixPattern)?.[0] ?? '';
63
- const prefix = suffix ? html.slice(0, -suffix.length) : html;
64
- enqueueHtml(prefix, controller);
65
- if (!suffix) return void startFlight(controller);
66
- if (suffix.length <= RSC_HTML_CLOSING_TAIL_LIMIT) {
67
- retainedTail = suffix;
68
- return;
69
- }
70
- await startFlight(controller);
71
- if (/<\/body>/i.test(suffix)) {
72
- committedClosingBody = true;
73
- committedHtmlClose = /<\/html>/i.test(suffix);
74
- committedProbe = suffix.slice(-6);
75
- }
76
- enqueueHtml(suffix, controller);
77
- };
78
- return new TransformStream({
79
- async transform (chunk, controller) {
80
- await processHtml(decoder.decode(chunk, {
81
- stream: true
82
- }), controller);
83
- },
84
- async flush (controller) {
85
- await processHtml(decoder.decode(), controller);
86
- await startFlight(controller);
87
- const retainedClosingTags = closingTagsPattern.test(retainedTail);
88
- enqueueHtml(retainedTail, controller);
89
- if (!injectClosingTags) return;
90
- if (committedClosingBody) {
91
- if (!committedHtmlClose) enqueueHtml('</html>', controller);
92
- } else if (!retainedClosingTags) enqueueHtml('</body></html>', controller);
93
- }
94
- });
95
- }
96
- async function writeRSCStream(rscStream, controller) {
97
- const decoder = new TextDecoder('utf-8', {
98
- fatal: true
99
- });
100
- for await (const chunk of rscStream)try {
101
- writeChunk(JSON.stringify(decoder.decode(chunk, {
102
- stream: true
103
- })), controller);
104
- } catch {
105
- const base64 = JSON.stringify(btoa(String.fromCodePoint(...chunk)));
106
- writeChunk(`Uint8Array.from(atob(${base64}), m => m.codePointAt(0))`, controller);
107
- }
108
- const remaining = decoder.decode();
109
- if (remaining) writeChunk(JSON.stringify(remaining), controller);
110
- }
111
- function writeChunk(chunk, controller) {
112
- const script = `(self.__FLIGHT_DATA||=[]).push(${chunk})`;
113
- controller.enqueue(encoder.encode(`<script>${script.replace(/<!--/g, '<\\!--').replace(/<\/(script)/gi, '</\\$1')}</script>`));
114
- }
115
- __webpack_require__.d(__webpack_exports__, {
116
- injectRSCPayload: ()=>injectRSCPayload
117
- }, {
118
- RSC_HTML_CLOSING_TAIL_LIMIT: RSC_HTML_CLOSING_TAIL_LIMIT
119
- });
120
- exports.RSC_HTML_CLOSING_TAIL_LIMIT = __webpack_exports__.RSC_HTML_CLOSING_TAIL_LIMIT;
121
- exports.injectRSCPayload = __webpack_exports__.injectRSCPayload;
122
- for(var __rspack_i in __webpack_exports__)if (-1 === [
123
- "RSC_HTML_CLOSING_TAIL_LIMIT",
124
- "injectRSCPayload"
125
- ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
126
- Object.defineProperty(exports, '__esModule', {
127
- value: true
128
- });
@@ -1,86 +0,0 @@
1
- const encoder = new TextEncoder();
2
- const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
3
- const closingTagsPrefixPattern = /(?:<\/body>\s*(?:<\/html>\s*|<\/h(?:t(?:m(?:l>?)?)?)?|<\/?|<)?|<\/b(?:o(?:d(?:y>?)?)?)?|<\/?|<[^>]*)$/i;
4
- const RSC_HTML_CLOSING_TAIL_LIMIT = 8192;
5
- function injectRSCPayload(rscStream, { injectClosingTags = true } = {}) {
6
- const decoder = new TextDecoder();
7
- let retainedTail = '';
8
- let committedClosingBody = false;
9
- let committedHtmlClose = false;
10
- let committedProbe = '';
11
- let flightPromise;
12
- const startFlight = (controller)=>{
13
- if (!flightPromise) {
14
- flightPromise = writeRSCStream(rscStream, controller);
15
- flightPromise.catch((error)=>controller.error(error));
16
- }
17
- return flightPromise;
18
- };
19
- const enqueueHtml = (value, controller)=>{
20
- if (value) controller.enqueue(encoder.encode(value));
21
- };
22
- const processHtml = async (value, controller)=>{
23
- if (!value) return;
24
- if (committedClosingBody) {
25
- const probe = committedProbe + value;
26
- committedHtmlClose ||= /<\/html>/i.test(probe);
27
- committedProbe = probe.slice(-6);
28
- enqueueHtml(value, controller);
29
- return;
30
- }
31
- const html = retainedTail + value;
32
- retainedTail = '';
33
- const suffix = html.match(closingTagsPrefixPattern)?.[0] ?? '';
34
- const prefix = suffix ? html.slice(0, -suffix.length) : html;
35
- enqueueHtml(prefix, controller);
36
- if (!suffix) return void startFlight(controller);
37
- if (suffix.length <= RSC_HTML_CLOSING_TAIL_LIMIT) {
38
- retainedTail = suffix;
39
- return;
40
- }
41
- await startFlight(controller);
42
- if (/<\/body>/i.test(suffix)) {
43
- committedClosingBody = true;
44
- committedHtmlClose = /<\/html>/i.test(suffix);
45
- committedProbe = suffix.slice(-6);
46
- }
47
- enqueueHtml(suffix, controller);
48
- };
49
- return new TransformStream({
50
- async transform (chunk, controller) {
51
- await processHtml(decoder.decode(chunk, {
52
- stream: true
53
- }), controller);
54
- },
55
- async flush (controller) {
56
- await processHtml(decoder.decode(), controller);
57
- await startFlight(controller);
58
- const retainedClosingTags = closingTagsPattern.test(retainedTail);
59
- enqueueHtml(retainedTail, controller);
60
- if (!injectClosingTags) return;
61
- if (committedClosingBody) {
62
- if (!committedHtmlClose) enqueueHtml('</html>', controller);
63
- } else if (!retainedClosingTags) enqueueHtml('</body></html>', controller);
64
- }
65
- });
66
- }
67
- async function writeRSCStream(rscStream, controller) {
68
- const decoder = new TextDecoder('utf-8', {
69
- fatal: true
70
- });
71
- for await (const chunk of rscStream)try {
72
- writeChunk(JSON.stringify(decoder.decode(chunk, {
73
- stream: true
74
- })), controller);
75
- } catch {
76
- const base64 = JSON.stringify(btoa(String.fromCodePoint(...chunk)));
77
- writeChunk(`Uint8Array.from(atob(${base64}), m => m.codePointAt(0))`, controller);
78
- }
79
- const remaining = decoder.decode();
80
- if (remaining) writeChunk(JSON.stringify(remaining), controller);
81
- }
82
- function writeChunk(chunk, controller) {
83
- const script = `(self.__FLIGHT_DATA||=[]).push(${chunk})`;
84
- controller.enqueue(encoder.encode(`<script>${script.replace(/<!--/g, '<\\!--').replace(/<\/(script)/gi, '</\\$1')}</script>`));
85
- }
86
- export { RSC_HTML_CLOSING_TAIL_LIMIT, injectRSCPayload };
@@ -1,87 +0,0 @@
1
- import "node:module";
2
- const encoder = new TextEncoder();
3
- const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
4
- const closingTagsPrefixPattern = /(?:<\/body>\s*(?:<\/html>\s*|<\/h(?:t(?:m(?:l>?)?)?)?|<\/?|<)?|<\/b(?:o(?:d(?:y>?)?)?)?|<\/?|<[^>]*)$/i;
5
- const RSC_HTML_CLOSING_TAIL_LIMIT = 8192;
6
- function injectRSCPayload(rscStream, { injectClosingTags = true } = {}) {
7
- const decoder = new TextDecoder();
8
- let retainedTail = '';
9
- let committedClosingBody = false;
10
- let committedHtmlClose = false;
11
- let committedProbe = '';
12
- let flightPromise;
13
- const startFlight = (controller)=>{
14
- if (!flightPromise) {
15
- flightPromise = writeRSCStream(rscStream, controller);
16
- flightPromise.catch((error)=>controller.error(error));
17
- }
18
- return flightPromise;
19
- };
20
- const enqueueHtml = (value, controller)=>{
21
- if (value) controller.enqueue(encoder.encode(value));
22
- };
23
- const processHtml = async (value, controller)=>{
24
- if (!value) return;
25
- if (committedClosingBody) {
26
- const probe = committedProbe + value;
27
- committedHtmlClose ||= /<\/html>/i.test(probe);
28
- committedProbe = probe.slice(-6);
29
- enqueueHtml(value, controller);
30
- return;
31
- }
32
- const html = retainedTail + value;
33
- retainedTail = '';
34
- const suffix = html.match(closingTagsPrefixPattern)?.[0] ?? '';
35
- const prefix = suffix ? html.slice(0, -suffix.length) : html;
36
- enqueueHtml(prefix, controller);
37
- if (!suffix) return void startFlight(controller);
38
- if (suffix.length <= RSC_HTML_CLOSING_TAIL_LIMIT) {
39
- retainedTail = suffix;
40
- return;
41
- }
42
- await startFlight(controller);
43
- if (/<\/body>/i.test(suffix)) {
44
- committedClosingBody = true;
45
- committedHtmlClose = /<\/html>/i.test(suffix);
46
- committedProbe = suffix.slice(-6);
47
- }
48
- enqueueHtml(suffix, controller);
49
- };
50
- return new TransformStream({
51
- async transform (chunk, controller) {
52
- await processHtml(decoder.decode(chunk, {
53
- stream: true
54
- }), controller);
55
- },
56
- async flush (controller) {
57
- await processHtml(decoder.decode(), controller);
58
- await startFlight(controller);
59
- const retainedClosingTags = closingTagsPattern.test(retainedTail);
60
- enqueueHtml(retainedTail, controller);
61
- if (!injectClosingTags) return;
62
- if (committedClosingBody) {
63
- if (!committedHtmlClose) enqueueHtml('</html>', controller);
64
- } else if (!retainedClosingTags) enqueueHtml('</body></html>', controller);
65
- }
66
- });
67
- }
68
- async function writeRSCStream(rscStream, controller) {
69
- const decoder = new TextDecoder('utf-8', {
70
- fatal: true
71
- });
72
- for await (const chunk of rscStream)try {
73
- writeChunk(JSON.stringify(decoder.decode(chunk, {
74
- stream: true
75
- })), controller);
76
- } catch {
77
- const base64 = JSON.stringify(btoa(String.fromCodePoint(...chunk)));
78
- writeChunk(`Uint8Array.from(atob(${base64}), m => m.codePointAt(0))`, controller);
79
- }
80
- const remaining = decoder.decode();
81
- if (remaining) writeChunk(JSON.stringify(remaining), controller);
82
- }
83
- function writeChunk(chunk, controller) {
84
- const script = `(self.__FLIGHT_DATA||=[]).push(${chunk})`;
85
- controller.enqueue(encoder.encode(`<script>${script.replace(/<!--/g, '<\\!--').replace(/<\/(script)/gi, '</\\$1')}</script>`));
86
- }
87
- export { RSC_HTML_CLOSING_TAIL_LIMIT, injectRSCPayload };
@@ -1,4 +0,0 @@
1
- export declare const RSC_HTML_CLOSING_TAIL_LIMIT: number;
2
- export declare function injectRSCPayload(rscStream: ReadableStream<Uint8Array>, { injectClosingTags }?: {
3
- injectClosingTags?: boolean;
4
- }): TransformStream<Uint8Array, Uint8Array>;
@@ -1,142 +0,0 @@
1
- /**
2
- * Forked and modified from https://github.com/devongovett/rsc-html-stream.
3
- * License: https://github.com/devongovett/rsc-html-stream/blob/main/LICENSE
4
- */
5
- const encoder = new TextEncoder();
6
- const closingTagsPattern = /<\/body>\s*<\/html>\s*$/i;
7
- const closingTagsPrefixPattern =
8
- /(?:<\/body>\s*(?:<\/html>\s*|<\/h(?:t(?:m(?:l>?)?)?)?|<\/?|<)?|<\/b(?:o(?:d(?:y>?)?)?)?|<\/?|<[^>]*)$/i;
9
-
10
- export const RSC_HTML_CLOSING_TAIL_LIMIT = 8 * 1024;
11
-
12
- export function injectRSCPayload(
13
- rscStream: ReadableStream<Uint8Array>,
14
- { injectClosingTags = true }: { injectClosingTags?: boolean } = {},
15
- ): TransformStream<Uint8Array, Uint8Array> {
16
- const decoder = new TextDecoder();
17
- let retainedTail = '';
18
- let committedClosingBody = false;
19
- let committedHtmlClose = false;
20
- let committedProbe = '';
21
- let flightPromise: Promise<void> | undefined;
22
-
23
- const startFlight = (
24
- controller: TransformStreamDefaultController<Uint8Array>,
25
- ) => {
26
- if (!flightPromise) {
27
- flightPromise = writeRSCStream(rscStream, controller);
28
- void flightPromise.catch(error => controller.error(error));
29
- }
30
- return flightPromise;
31
- };
32
-
33
- const enqueueHtml = (
34
- value: string,
35
- controller: TransformStreamDefaultController<Uint8Array>,
36
- ) => {
37
- if (value) {
38
- controller.enqueue(encoder.encode(value));
39
- }
40
- };
41
-
42
- const processHtml = async (
43
- value: string,
44
- controller: TransformStreamDefaultController<Uint8Array>,
45
- ) => {
46
- if (!value) {
47
- return;
48
- }
49
-
50
- if (committedClosingBody) {
51
- const probe = committedProbe + value;
52
- committedHtmlClose ||= /<\/html>/i.test(probe);
53
- committedProbe = probe.slice(-6);
54
- enqueueHtml(value, controller);
55
- return;
56
- }
57
-
58
- const html = retainedTail + value;
59
- retainedTail = '';
60
- const suffix = html.match(closingTagsPrefixPattern)?.[0] ?? '';
61
- const prefix = suffix ? html.slice(0, -suffix.length) : html;
62
- enqueueHtml(prefix, controller);
63
-
64
- if (!suffix) {
65
- void startFlight(controller);
66
- return;
67
- }
68
- if (suffix.length <= RSC_HTML_CLOSING_TAIL_LIMIT) {
69
- retainedTail = suffix;
70
- return;
71
- }
72
-
73
- await startFlight(controller);
74
- if (/<\/body>/i.test(suffix)) {
75
- committedClosingBody = true;
76
- committedHtmlClose = /<\/html>/i.test(suffix);
77
- committedProbe = suffix.slice(-6);
78
- }
79
- enqueueHtml(suffix, controller);
80
- };
81
-
82
- return new TransformStream({
83
- async transform(chunk, controller) {
84
- await processHtml(decoder.decode(chunk, { stream: true }), controller);
85
- },
86
- async flush(controller) {
87
- await processHtml(decoder.decode(), controller);
88
- await startFlight(controller);
89
-
90
- const retainedClosingTags = closingTagsPattern.test(retainedTail);
91
- enqueueHtml(retainedTail, controller);
92
- if (!injectClosingTags) {
93
- return;
94
- }
95
- if (committedClosingBody) {
96
- if (!committedHtmlClose) {
97
- enqueueHtml('</html>', controller);
98
- }
99
- } else if (!retainedClosingTags) {
100
- enqueueHtml('</body></html>', controller);
101
- }
102
- },
103
- });
104
- }
105
-
106
- async function writeRSCStream(
107
- rscStream: ReadableStream<Uint8Array>,
108
- controller: TransformStreamDefaultController<Uint8Array>,
109
- ): Promise<void> {
110
- const decoder = new TextDecoder('utf-8', { fatal: true });
111
- for await (const chunk of rscStream) {
112
- try {
113
- writeChunk(
114
- JSON.stringify(decoder.decode(chunk, { stream: true })),
115
- controller,
116
- );
117
- } catch {
118
- const base64 = JSON.stringify(btoa(String.fromCodePoint(...chunk)));
119
- writeChunk(
120
- `Uint8Array.from(atob(${base64}), m => m.codePointAt(0))`,
121
- controller,
122
- );
123
- }
124
- }
125
-
126
- const remaining = decoder.decode();
127
- if (remaining) {
128
- writeChunk(JSON.stringify(remaining), controller);
129
- }
130
- }
131
-
132
- function writeChunk(
133
- chunk: string,
134
- controller: TransformStreamDefaultController<Uint8Array>,
135
- ) {
136
- const script = `(self.__FLIGHT_DATA||=[]).push(${chunk})`;
137
- controller.enqueue(
138
- encoder.encode(
139
- `<script>${script.replace(/<!--/g, '<\\!--').replace(/<\/(script)/gi, '</\\$1')}</script>`,
140
- ),
141
- );
142
- }