@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.6 → 3.9.0-ultramodern.8
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.
- package/package.json +4 -4
- package/tests/buildIdentity.test.ts +0 -19
- package/tests/contextExtensions.test.ts +0 -44
- package/tests/headRuntime.test.tsx +0 -140
- package/tests/rendererHead.test.ts +0 -41
- package/tests/request-policy/browser-policy.test.ts +0 -500
- package/tests/request-policy/client-isolation.test.ts +0 -40
- package/tests/request-policy/node-policy.test.ts +0 -294
- package/tests/request-policy/policyCore.test.ts +0 -121
- package/tests/request-policy/requestContext.test.ts +8 -22
- package/tests/request-policy/requestFactory.outbound-contract.test.ts +109 -510
- package/tests/request-policy/traceparent.test.ts +2 -5
- package/tests/request-policy/transport-retry.test.ts +44 -47
- package/tests/routerState.test.ts +0 -79
- package/tests/routerStatePlugin.test.ts +1 -46
- package/tests/runtimeContextProjection.test.ts +0 -52
- package/tests/safeFailure.test.ts +10 -28
- package/tests/workspaceEvents.test.ts +31 -201
- package/tests/nativeSafeFailureCompatibility.test.ts +0 -117
- package/tests/prefetchPolicy.test.ts +0 -51
- package/tests/request-policy/identityBinding.test.ts +0 -75
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"directory": "packages/runtime/runtime-extensions"
|
|
12
12
|
},
|
|
13
13
|
"license": "MIT",
|
|
14
|
-
"version": "3.9.0-ultramodern.
|
|
14
|
+
"version": "3.9.0-ultramodern.8",
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": ">=26.7.0"
|
|
17
17
|
},
|
|
@@ -274,7 +274,7 @@
|
|
|
274
274
|
},
|
|
275
275
|
"devDependencies": {
|
|
276
276
|
"@loadable/component": "5.16.7",
|
|
277
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.
|
|
277
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.8",
|
|
278
278
|
"@rsbuild/core": "2.2.3",
|
|
279
279
|
"@rslib/core": "1.0.0",
|
|
280
280
|
"@scripts/rstest-config": "2.66.0",
|
|
@@ -303,8 +303,8 @@
|
|
|
303
303
|
}
|
|
304
304
|
},
|
|
305
305
|
"dependencies": {
|
|
306
|
-
"@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.9.0-ultramodern.
|
|
307
|
-
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.
|
|
306
|
+
"@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.9.0-ultramodern.8",
|
|
307
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.8",
|
|
308
308
|
"react-helmet-async": "3.0.0"
|
|
309
309
|
},
|
|
310
310
|
"scripts": {
|
|
@@ -28,25 +28,6 @@ const readers = {
|
|
|
28
28
|
sourceRevision: () => 'compiled-revision',
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
it('applies injected identity to every surface without mutating the artifact', () => {
|
|
32
|
-
const resolved = resolveUltramodernBuildArtifact(artifact, readers);
|
|
33
|
-
for (const record of [
|
|
34
|
-
resolved.deliveryUnit,
|
|
35
|
-
resolved.surfaces.api,
|
|
36
|
-
resolved.surfaces.ui,
|
|
37
|
-
]) {
|
|
38
|
-
expect(record).toMatchObject({
|
|
39
|
-
build: 'compiled-build',
|
|
40
|
-
buildMarker: 'compiled-build',
|
|
41
|
-
sourceRevision: 'compiled-revision',
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
expect(resolved.deliveryUnit.unitId).toBe('shop/catalog');
|
|
45
|
-
expect(resolved.surfaces.api.surface).toBe('api');
|
|
46
|
-
expect(resolved.surfaces.ui.surface).toBe('ui');
|
|
47
|
-
expect(artifact.deliveryUnit.buildMarker).toBe('original');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
31
|
it('falls back only for missing compiler references and preserves each supplied value', () => {
|
|
51
32
|
expect(resolveUltramodernBuildArtifact(artifact)).toEqual(artifact);
|
|
52
33
|
|
|
@@ -1,50 +1,6 @@
|
|
|
1
1
|
import { createRuntimeContextExtension } from '../src/contextExtensions';
|
|
2
2
|
|
|
3
3
|
describe('runtime context extensions', () => {
|
|
4
|
-
it('stores and retrieves typed values per context object', () => {
|
|
5
|
-
const extension = createRuntimeContextExtension<{ value: number }>(
|
|
6
|
-
'test:isolation-a',
|
|
7
|
-
);
|
|
8
|
-
const contextA = {};
|
|
9
|
-
const contextB = {};
|
|
10
|
-
|
|
11
|
-
extension.set(contextA, { value: 1 });
|
|
12
|
-
extension.set(contextB, { value: 2 });
|
|
13
|
-
|
|
14
|
-
expect(extension.get(contextA)).toEqual({ value: 1 });
|
|
15
|
-
expect(extension.get(contextB)).toEqual({ value: 2 });
|
|
16
|
-
|
|
17
|
-
extension.remove(contextA);
|
|
18
|
-
expect(extension.get(contextA)).toBeUndefined();
|
|
19
|
-
expect(extension.get(contextB)).toEqual({ value: 2 });
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('isolates extensions with different ids on the same context', () => {
|
|
23
|
-
const first = createRuntimeContextExtension<string>('test:first');
|
|
24
|
-
const second = createRuntimeContextExtension<string>('test:second');
|
|
25
|
-
const context = {};
|
|
26
|
-
|
|
27
|
-
first.set(context, 'one');
|
|
28
|
-
second.set(context, 'two');
|
|
29
|
-
|
|
30
|
-
expect(first.get(context)).toBe('one');
|
|
31
|
-
expect(second.get(context)).toBe('two');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('shares state between extensions created with the same id', () => {
|
|
35
|
-
const a = createRuntimeContextExtension<string>('test:shared');
|
|
36
|
-
const b = createRuntimeContextExtension<string>('test:shared');
|
|
37
|
-
const context = {};
|
|
38
|
-
|
|
39
|
-
a.set(context, 'value');
|
|
40
|
-
expect(b.get(context)).toBe('value');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('returns undefined for contexts without any extensions', () => {
|
|
44
|
-
const extension = createRuntimeContextExtension<string>('test:empty');
|
|
45
|
-
expect(extension.get({})).toBeUndefined();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
4
|
it('does not leak into string-key enumeration of the context', () => {
|
|
49
5
|
const extension = createRuntimeContextExtension<string>('test:hidden');
|
|
50
6
|
const context = {};
|
|
@@ -146,41 +146,6 @@ describe('server Helmet collection', () => {
|
|
|
146
146
|
expect(meta).toContain('content="inner"');
|
|
147
147
|
expect(meta).not.toContain('content="outer"');
|
|
148
148
|
expect(meta).toContain('content="modernjs"');
|
|
149
|
-
// outer instance tags come first, like react-helmet-async
|
|
150
|
-
expect(meta.indexOf('keywords')).toBeLessThan(meta.indexOf('description'));
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('keeps duplicate metas within a single Helmet instance', () => {
|
|
154
|
-
const context = createServerContext();
|
|
155
|
-
renderWithContext(
|
|
156
|
-
context,
|
|
157
|
-
<Helmet>
|
|
158
|
-
<meta name="author" content="a" />
|
|
159
|
-
<meta name="author" content="b" />
|
|
160
|
-
</Helmet>,
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
const meta = getHelmetData(context)!.meta.toString();
|
|
164
|
-
expect(meta).toContain('content="a"');
|
|
165
|
-
expect(meta).toContain('content="b"');
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('normalizes charSet when deduping and drops meta without primary attribute', () => {
|
|
169
|
-
const context = createServerContext();
|
|
170
|
-
renderWithContext(
|
|
171
|
-
context,
|
|
172
|
-
<>
|
|
173
|
-
<Helmet>
|
|
174
|
-
<meta charSet="utf-8" />
|
|
175
|
-
<meta content="orphan" />
|
|
176
|
-
</Helmet>
|
|
177
|
-
<Helmet meta={[{ charSet: 'utf-8' } as any]} />
|
|
178
|
-
</>,
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
const meta = getHelmetData(context)!.meta.toString();
|
|
182
|
-
expect(meta.match(/charset/g)).toHaveLength(1);
|
|
183
|
-
expect(meta).not.toContain('orphan');
|
|
184
149
|
});
|
|
185
150
|
|
|
186
151
|
it('applies the innermost title and the innermost titleTemplate', () => {
|
|
@@ -202,71 +167,6 @@ describe('server Helmet collection', () => {
|
|
|
202
167
|
);
|
|
203
168
|
});
|
|
204
169
|
|
|
205
|
-
it('falls back to defaultTitle without applying the template', () => {
|
|
206
|
-
const context = createServerContext();
|
|
207
|
-
renderWithContext(
|
|
208
|
-
context,
|
|
209
|
-
<Helmet defaultTitle="Default" titleTemplate="%s | Site" />,
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
expect(getHelmetData(context)!.title.toString()).toBe(
|
|
213
|
-
'<title data-rh="true">Default</title>',
|
|
214
|
-
);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('maps <html>/<body> children to html/body attributes', () => {
|
|
218
|
-
const context = createServerContext();
|
|
219
|
-
renderWithContext(
|
|
220
|
-
context,
|
|
221
|
-
<Helmet>
|
|
222
|
-
<html lang="en" />
|
|
223
|
-
<body className="dark" />
|
|
224
|
-
</Helmet>,
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
const helmet = getHelmetData(context)!;
|
|
228
|
-
expect(helmet.htmlAttributes.toString()).toBe('lang="en"');
|
|
229
|
-
expect(helmet.bodyAttributes.toString()).toBe('class="dark"');
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('keeps one canonical link but all distinct stylesheets', () => {
|
|
233
|
-
const context = createServerContext();
|
|
234
|
-
renderWithContext(
|
|
235
|
-
context,
|
|
236
|
-
<>
|
|
237
|
-
<Helmet>
|
|
238
|
-
<link rel="canonical" href="https://outer.example/" />
|
|
239
|
-
<link rel="stylesheet" href="/a.css" />
|
|
240
|
-
<link rel="stylesheet" href="/b.css" />
|
|
241
|
-
</Helmet>
|
|
242
|
-
<Helmet>
|
|
243
|
-
<link rel="canonical" href="https://inner.example/" />
|
|
244
|
-
</Helmet>
|
|
245
|
-
</>,
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
const link = getHelmetData(context)!.link.toString();
|
|
249
|
-
expect(link).toContain('https://inner.example/');
|
|
250
|
-
expect(link).not.toContain('https://outer.example/');
|
|
251
|
-
expect(link).toContain('/a.css');
|
|
252
|
-
expect(link).toContain('/b.css');
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it('does not serialize script innerHTML as an attribute', () => {
|
|
256
|
-
const context = createServerContext();
|
|
257
|
-
renderWithContext(
|
|
258
|
-
context,
|
|
259
|
-
<Helmet>
|
|
260
|
-
<script type="application/ld+json">{'{"@context":"a"}'}</script>
|
|
261
|
-
</Helmet>,
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
const script = getHelmetData(context)!.script.toString();
|
|
265
|
-
expect(script).toBe(
|
|
266
|
-
'<script data-rh="true" type="application/ld+json">{"@context":"a"}</script>',
|
|
267
|
-
);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
170
|
it('publishes prioritized SEO tags separately with component parity', () => {
|
|
271
171
|
const context = createServerContext();
|
|
272
172
|
renderWithContext(
|
|
@@ -292,29 +192,6 @@ describe('server Helmet collection', () => {
|
|
|
292
192
|
expect(helmet.link.toString()).not.toContain('https://example.com/page');
|
|
293
193
|
expect(helmet.script.toString()).toContain('/ordinary.js');
|
|
294
194
|
expect(helmet.script.toString()).not.toContain('application/ld+json');
|
|
295
|
-
|
|
296
|
-
const priority = helmet.priority.toComponent();
|
|
297
|
-
expect(priority.map(element => element.type)).toEqual([
|
|
298
|
-
'meta',
|
|
299
|
-
'link',
|
|
300
|
-
'script',
|
|
301
|
-
]);
|
|
302
|
-
expect(priority[0].props).toMatchObject({
|
|
303
|
-
'data-rh': true,
|
|
304
|
-
content: 'priority description',
|
|
305
|
-
name: 'description',
|
|
306
|
-
});
|
|
307
|
-
expect(priority[2].props).toMatchObject({
|
|
308
|
-
'data-rh': true,
|
|
309
|
-
dangerouslySetInnerHTML: { __html: '{"name":"page"}' },
|
|
310
|
-
type: 'application/ld+json',
|
|
311
|
-
});
|
|
312
|
-
expect(helmet.meta.toComponent()[0].props).toMatchObject({
|
|
313
|
-
content: 'ordinary keywords',
|
|
314
|
-
name: 'keywords',
|
|
315
|
-
});
|
|
316
|
-
expect(helmet.title.toComponent()[0].props.children).toBe('Priority Page');
|
|
317
|
-
expect(helmet.htmlAttributes.toComponent()).toEqual({ className: 'app' });
|
|
318
195
|
});
|
|
319
196
|
|
|
320
197
|
it('is idempotent when React replays the tree (streaming SSR retry)', () => {
|
|
@@ -349,21 +226,4 @@ describe('server Helmet collection', () => {
|
|
|
349
226
|
expect(second.htmlAttributes.toString()).toBe(firstSnapshot.htmlAttributes);
|
|
350
227
|
expect(second.meta.toString().match(/description/g)).toHaveLength(1);
|
|
351
228
|
});
|
|
352
|
-
|
|
353
|
-
it('keeps the innermost base tag only', () => {
|
|
354
|
-
const context = createServerContext();
|
|
355
|
-
renderWithContext(
|
|
356
|
-
context,
|
|
357
|
-
<>
|
|
358
|
-
<Helmet base={{ href: 'https://outer.example/' } as any} />
|
|
359
|
-
<Helmet>
|
|
360
|
-
<base href="https://inner.example/" />
|
|
361
|
-
</Helmet>
|
|
362
|
-
</>,
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
const base = getHelmetData(context)!.base.toString();
|
|
366
|
-
expect(base).toContain('https://inner.example/');
|
|
367
|
-
expect(base).not.toContain('https://outer.example/');
|
|
368
|
-
});
|
|
369
229
|
});
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
collectHeadRecord,
|
|
6
6
|
completeHeadRender,
|
|
7
7
|
createHeadChunkProcessor,
|
|
8
|
-
createWebHeadMarkerStripper,
|
|
9
8
|
} from '../src';
|
|
10
9
|
import { createNodeHeadMarkerStripper, pipeNodeHeadStream } from '../src/node';
|
|
11
10
|
|
|
@@ -45,16 +44,6 @@ describe('renderer head transactions', () => {
|
|
|
45
44
|
expect(completeHeadRender(context, userTemplate)).toBe(userTemplate);
|
|
46
45
|
});
|
|
47
46
|
|
|
48
|
-
it('does not retain ordinary streaming content', () => {
|
|
49
|
-
const context = {};
|
|
50
|
-
beginHeadRender(context);
|
|
51
|
-
const processor = createHeadChunkProcessor(context);
|
|
52
|
-
const content = 'x'.repeat(128 * 1024);
|
|
53
|
-
|
|
54
|
-
expect(processor.push(content)).toBe(content);
|
|
55
|
-
expect(processor.finish()).toBe('');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
47
|
it('strips markers split across chunks without corrupting unicode', () => {
|
|
59
48
|
const sampleContext = {};
|
|
60
49
|
beginHeadRender(sampleContext);
|
|
@@ -101,36 +90,6 @@ describe('renderer head transactions', () => {
|
|
|
101
90
|
expect(published).toEqual(['previous']);
|
|
102
91
|
});
|
|
103
92
|
|
|
104
|
-
it('strips split markers from a web byte stream', async () => {
|
|
105
|
-
const context = {};
|
|
106
|
-
let published: string[] = [];
|
|
107
|
-
beginHeadRender(context);
|
|
108
|
-
const props = collectHeadRecord(
|
|
109
|
-
context,
|
|
110
|
-
() => 'web',
|
|
111
|
-
records => {
|
|
112
|
-
published = records;
|
|
113
|
-
},
|
|
114
|
-
)!;
|
|
115
|
-
const html = `č${marker(props)}尾`;
|
|
116
|
-
const encoder = new TextEncoder();
|
|
117
|
-
const encoded = encoder.encode(html);
|
|
118
|
-
const source = new ReadableStream<Uint8Array>({
|
|
119
|
-
start(controller) {
|
|
120
|
-
controller.enqueue(encoded.slice(0, 1));
|
|
121
|
-
controller.enqueue(encoded.slice(1, 17));
|
|
122
|
-
controller.enqueue(encoded.slice(17));
|
|
123
|
-
controller.close();
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const output = await new Response(
|
|
128
|
-
source.pipeThrough(createWebHeadMarkerStripper(context)),
|
|
129
|
-
).text();
|
|
130
|
-
expect(output).toBe('č尾');
|
|
131
|
-
expect(published).toEqual(['web']);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
93
|
it('strips split markers from a Node stream', async () => {
|
|
135
94
|
const context = {};
|
|
136
95
|
let published: string[] = [];
|