@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.5 → 3.9.0-ultramodern.7

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 (31) hide show
  1. package/dist/cjs/routerProvider.js +9 -74
  2. package/dist/esm/routerProvider.mjs +8 -67
  3. package/dist/esm-node/routerProvider.mjs +8 -67
  4. package/dist/types/routerProvider.d.ts +2 -24
  5. package/dist/types/server-config/bffRuntime.d.ts +2 -2
  6. package/dist/types/server-config/serverTelemetry.d.ts +27 -27
  7. package/package.json +4 -4
  8. package/src/routerProvider.ts +14 -194
  9. package/src/server-config/bffRuntime.ts +2 -2
  10. package/src/server-config/serverTelemetry.ts +27 -27
  11. package/tests/buildIdentity.test.ts +0 -19
  12. package/tests/contextExtensions.test.ts +0 -44
  13. package/tests/headRuntime.test.tsx +0 -140
  14. package/tests/rendererHead.test.ts +0 -41
  15. package/tests/request-policy/browser-policy.test.ts +0 -500
  16. package/tests/request-policy/client-isolation.test.ts +0 -40
  17. package/tests/request-policy/node-policy.test.ts +0 -294
  18. package/tests/request-policy/policyCore.test.ts +0 -121
  19. package/tests/request-policy/requestContext.test.ts +8 -22
  20. package/tests/request-policy/requestFactory.outbound-contract.test.ts +109 -510
  21. package/tests/request-policy/traceparent.test.ts +2 -5
  22. package/tests/request-policy/transport-retry.test.ts +44 -47
  23. package/tests/routerProvider.test.ts +2 -11
  24. package/tests/routerState.test.ts +0 -79
  25. package/tests/routerStatePlugin.test.ts +1 -46
  26. package/tests/runtimeContextProjection.test.ts +0 -52
  27. package/tests/safeFailure.test.ts +10 -28
  28. package/tests/workspaceEvents.test.ts +31 -201
  29. package/tests/nativeSafeFailureCompatibility.test.ts +0 -117
  30. package/tests/prefetchPolicy.test.ts +0 -51
  31. package/tests/request-policy/identityBinding.test.ts +0 -75
@@ -97,209 +97,32 @@ export function createRouterProviderRealm(
97
97
  });
98
98
  }
99
99
 
100
- type RouterProviderRegistry = {
101
- providers: Map<string, RouterProviderFactory>;
102
- defaultProvider?: string;
103
- /** Names that were registered by more than one module instance. */
104
- duplicateProviders: Set<string>;
105
- /** Duplicate names already reported when the compatibility fallback won. */
106
- warnedDuplicates: Set<string>;
107
- };
108
-
109
- /**
110
- * Versioned compatibility-registry key. The unsuffixed key
111
- * ('@modern-js/runtime:router-providers') is owned by older published copies
112
- * of this module whose `registerRouterProvider` *throws* on a same-name
113
- * re-registration. `:v2` added keep-first registration but also enforced one
114
- * non-default provider across the whole JavaScript realm. `:v3` removes that
115
- * page-global app invariant: explicit runtime realms own provider selection,
116
- * while this registry remains only a mixed-version fallback. New copies read
117
- * but never mutate the v2 slot so older published copies remain compatible.
118
- */
119
- const REGISTRY_SLOT: unique symbol = Symbol.for(
120
- '@modern-js/runtime:router-providers:v3',
121
- );
122
- const LEGACY_V2_REGISTRY_SLOT: unique symbol = Symbol.for(
123
- '@modern-js/runtime:router-providers:v2',
124
- );
125
-
126
- function getRegistry(): RouterProviderRegistry {
127
- const host = globalThis as { [REGISTRY_SLOT]?: RouterProviderRegistry };
128
- host[REGISTRY_SLOT] ??= {
129
- providers: new Map(),
130
- duplicateProviders: new Set(),
131
- warnedDuplicates: new Set(),
132
- };
133
- // Defense in depth within the v3 key: if a future v3-keyed copy of this
134
- // module ever predates a later-added field (mixed v3 minors in a Module
135
- // Federation setup), heal the shape instead of crashing on `undefined`.
136
- host[REGISTRY_SLOT].duplicateProviders ??= new Set();
137
- host[REGISTRY_SLOT].warnedDuplicates ??= new Set();
138
- return host[REGISTRY_SLOT];
139
- }
140
-
141
- function getLegacyV2Registry():
142
- | Pick<RouterProviderRegistry, 'providers' | 'defaultProvider'>
143
- | undefined {
144
- const host = globalThis as {
145
- [LEGACY_V2_REGISTRY_SLOT]?: Pick<
146
- RouterProviderRegistry,
147
- 'providers' | 'defaultProvider'
148
- >;
149
- };
150
- return host[LEGACY_V2_REGISTRY_SLOT];
151
- }
152
-
153
- export function registerRouterProvider(
154
- name: string,
155
- factory: RouterProviderFactory,
156
- options: { isDefault?: boolean } = {},
157
- ): void {
158
- const registry = getRegistry();
159
- const existing = registry.providers.get(name);
160
-
161
- if (existing !== undefined) {
162
- // Keep-first semantics. A same-name re-registration with a *different*
163
- // factory is almost always two bundled copies of the same provider
164
- // module — e.g. a Module Federation remote that does not share
165
- // '@modern-js/plugin-tanstack/runtime' evaluates its own copy, which
166
- // creates a fresh factory function per evaluation. That must not crash
167
- // the app; the first registration remains available for compatibility.
168
- // Merely loading independent app-owned realms is valid, so defer the
169
- // warning until a caller actually consumes this ambiguous fallback.
170
- if (existing !== factory) {
171
- registry.duplicateProviders.add(name);
172
- }
173
- return;
174
- }
175
-
176
- if (options.isDefault === true) {
177
- registry.defaultProvider = name;
178
- }
179
-
180
- registry.providers.set(name, factory);
181
- }
182
-
183
100
  export function resolveRouterProvider(
184
- framework?: string,
185
- options: {
186
- /**
187
- * Provider factories owned by the resolving app/runtime wrapper. A realm
188
- * is the exclusive provider source when supplied because its factories
189
- * close over the correct app module graph.
190
- */
191
- realm?: RouterProviderRealm;
192
- /**
193
- * Legacy single-provider override retained for published wrappers. New
194
- * integrations should pass an app-owned `realm`, which supports every
195
- * provider rather than only the default.
196
- */
197
- localDefault?: { name: string; factory: RouterProviderFactory };
198
- } = {},
101
+ framework: string | undefined,
102
+ options: { realm: RouterProviderRealm },
199
103
  ): RouterProviderFactory {
200
- if (options.realm !== undefined) {
201
- const name = framework || options.realm.defaultProvider;
202
- const realmProviderNames = options.realm.names();
203
-
204
- if (name === undefined) {
205
- throw new Error(
206
- `[@modern-js/runtime] The app-owned router provider realm does not declare a default provider. Available realm providers: ${
207
- realmProviderNames.join(', ') || '(none)'
208
- }.`,
209
- );
210
- }
211
-
212
- const factory = options.realm.get(name);
213
- if (factory === undefined) {
214
- throw new Error(
215
- `[@modern-js/runtime] Router provider "${name}" is not registered in the app-owned router provider realm. ` +
216
- `Available realm providers: ${
217
- realmProviderNames.join(', ') || '(none)'
218
- }. Compatibility registry fallback is disabled for app-owned realms.`,
219
- );
220
- }
221
-
222
- return factory;
223
- }
224
-
225
- const registry = getRegistry();
226
- const legacyV2Registry = getLegacyV2Registry();
227
- // `||` on purpose: a falsy framework value (empty string from env
228
- // templating, `false`, `undefined`) falls back to the default provider
229
- // instead of erroring on an unknown framework "". Prefer the older
230
- // localDefault contract before compatibility slots.
231
- const name =
232
- framework ||
233
- options.localDefault?.name ||
234
- registry.defaultProvider ||
235
- legacyV2Registry?.defaultProvider;
104
+ const name = framework || options.realm.defaultProvider;
105
+ const realmProviderNames = options.realm.names();
236
106
 
237
107
  if (name === undefined) {
238
108
  throw new Error(
239
- '[@modern-js/runtime] No default router provider is registered. This is a bug in the runtime setup.',
109
+ `[@modern-js/runtime] The app-owned router provider realm does not declare a default provider. Available realm providers: ${
110
+ realmProviderNames.join(', ') || '(none)'
111
+ }.`,
240
112
  );
241
113
  }
242
114
 
243
- if (
244
- options.localDefault !== undefined &&
245
- name === options.localDefault.name
246
- ) {
247
- return options.localDefault.factory;
248
- }
249
-
250
- const factory = registry.providers.get(name);
251
- if (factory !== undefined) {
252
- if (
253
- registry.duplicateProviders.has(name) &&
254
- !registry.warnedDuplicates.has(name)
255
- ) {
256
- registry.warnedDuplicates.add(name);
257
- console.warn(
258
- `[@modern-js/runtime] The router provider "${name}" was registered more than once with different module instances, and a router wrapper without an app-owned provider realm is resolving the mixed-version compatibility fallback. The fallback is keeping the first registration. ` +
259
- 'Modern router wrappers isolate app-owned realms automatically. ' +
260
- 'For an older wrapper, share the provider runtime between Module Federation host and remotes or upgrade every app to a realm-aware runtime.',
261
- );
262
- }
263
- return factory;
264
- }
265
-
266
- const legacyV2Factory = legacyV2Registry?.providers.get(name);
267
- if (legacyV2Factory !== undefined) {
268
- return legacyV2Factory;
269
- }
270
-
271
- if (name === 'tanstack') {
115
+ const factory = options.realm.get(name);
116
+ if (factory === undefined) {
272
117
  throw new Error(
273
- '[@modern-js/runtime] `runtime.router.framework` is set to "tanstack", but no TanStack router provider is registered. ' +
274
- 'Install @modern-js/plugin-tanstack, add `tanstackRouterPlugin()` to the `plugins` array in modern.config.ts, ' +
275
- "and make sure '@modern-js/plugin-tanstack/runtime' is imported (e.g. in modern.runtime.ts).",
118
+ `[@modern-js/runtime] Router provider "${name}" is not registered in the app-owned router provider realm. ` +
119
+ `Available realm providers: ${
120
+ realmProviderNames.join(', ') || '(none)'
121
+ }.`,
276
122
  );
277
123
  }
278
124
 
279
- throw new Error(
280
- `[@modern-js/runtime] Unknown router framework "${name}". ` +
281
- `Registered providers: ${
282
- [
283
- ...new Set([
284
- ...registry.providers.keys(),
285
- ...(legacyV2Registry?.providers.keys() ?? []),
286
- ]),
287
- ].join(', ') || '(none)'
288
- }. Install and register the plugin that provides this router framework.`,
289
- );
290
- }
291
-
292
- /**
293
- * Test-only escape hatch: compatibility registries live on `globalThis`, so
294
- * unit tests need a way to restore a pristine state between cases.
295
- */
296
- export function unsafe_resetRouterProvidersForTesting(): void {
297
- const host = globalThis as {
298
- [REGISTRY_SLOT]?: RouterProviderRegistry;
299
- [LEGACY_V2_REGISTRY_SLOT]?: unknown;
300
- };
301
- delete host[REGISTRY_SLOT];
302
- delete host[LEGACY_V2_REGISTRY_SLOT];
125
+ return factory;
303
126
  }
304
127
 
305
128
  /** Compose app-owned providers without importing the native runtime graph. */
@@ -312,9 +135,6 @@ export function createRouterPlugin<Hooks extends Record<string, unknown>>({
312
135
  registryHooks: Hooks;
313
136
  localProviders?: readonly Omit<RouterProviderRegistration, 'isDefault'>[];
314
137
  }) {
315
- registerRouterProvider(defaultProvider.name, defaultProvider.factory, {
316
- isDefault: true,
317
- });
318
138
  const realm = createRouterProviderRealm([
319
139
  { ...defaultProvider, isDefault: true },
320
140
  ...localProviders,
@@ -123,10 +123,10 @@ export interface BffEffectUserConfig {
123
123
  /**
124
124
  * Enforce Effect-native API/runtime modules instead of raw request handlers.
125
125
  *
126
- * When enabled, Effect API entries must export a `defineEffectBff(...)`
126
+ * Effect API entries must export a `defineEffectBff(...)`
127
127
  * definition or a `{ api, layer }` HttpApi module. Raw `handler` exports,
128
128
  * default request handlers, and unbranded custom `createHandler` factories
129
- * are treated as legacy escape hatches.
129
+ * are rejected.
130
130
  *
131
131
  * @default true
132
132
  */
@@ -31,18 +31,18 @@ export interface ServerTelemetrySloUserConfig {
31
31
  alertCooldownMs?: number;
32
32
  }
33
33
 
34
- export interface ServerTelemetryCanaryContractGateUserConfig {
34
+ export interface ServerTelemetryHealthContractGateUserConfig {
35
35
  /**
36
36
  * Whether this contract gate currently passes.
37
37
  */
38
38
  passed: boolean;
39
39
  /**
40
- * Optional failure reason used for rollback diagnostics.
40
+ * Optional failure reason used for health diagnostics.
41
41
  */
42
42
  reason?: string;
43
43
  }
44
44
 
45
- export interface ServerTelemetryCanaryAutopilotUserConfig {
45
+ export interface ServerTelemetrySnapshotObservationUserConfig {
46
46
  /**
47
47
  * Enable automatic contract gate synchronization from a gate snapshot file.
48
48
  *
@@ -70,15 +70,15 @@ export interface ServerTelemetryCanaryAutopilotUserConfig {
70
70
  /**
71
71
  * Runtime MF fallback signal ingestion.
72
72
  */
73
- runtimeFallbackSignal?: ServerTelemetryCanaryRuntimeFallbackSignalUserConfig;
73
+ runtimeFallbackSignal?: ServerTelemetryHealthRuntimeFallbackSignalUserConfig;
74
74
  /**
75
75
  * Optional pluggable state store backend for contract gate snapshots.
76
76
  * When omitted, snapshots are read/written from gateSnapshotPath on local disk.
77
77
  */
78
- stateStore?: ServerTelemetryCanaryAutopilotStateStoreUserConfig;
78
+ stateStore?: ServerTelemetrySnapshotObservationStateStoreUserConfig;
79
79
  }
80
80
 
81
- export interface ServerTelemetryCanaryAutopilotStateStoreUserConfig {
81
+ export interface ServerTelemetrySnapshotObservationStateStoreUserConfig {
82
82
  /**
83
83
  * Path or package name of a module that exports
84
84
  * `createContractGateSnapshotStore(context)`.
@@ -90,7 +90,7 @@ export interface ServerTelemetryCanaryAutopilotStateStoreUserConfig {
90
90
  options?: Record<string, unknown>;
91
91
  }
92
92
 
93
- export interface ServerTelemetryCanaryRuntimeFallbackSignalUserConfig {
93
+ export interface ServerTelemetryHealthRuntimeFallbackSignalUserConfig {
94
94
  /**
95
95
  * Enable runtime MF fallback signal ingestion endpoint.
96
96
  *
@@ -126,16 +126,16 @@ export interface ServerTelemetryCanaryRuntimeFallbackSignalUserConfig {
126
126
  maxBodyBytes?: number;
127
127
  /**
128
128
  * Optional runtime trust policy for fallback signal ingestion.
129
- * Use this to restrict who can mutate canary contract gates.
129
+ * Use this to restrict who can mutate health contract gates.
130
130
  */
131
- trustPolicy?: ServerTelemetryCanaryRuntimeFallbackSignalTrustPolicyUserConfig;
131
+ trustPolicy?: ServerTelemetryHealthRuntimeFallbackSignalTrustPolicyUserConfig;
132
132
  /**
133
133
  * Optional request authentication for runtime fallback signal endpoint.
134
134
  */
135
- auth?: ServerTelemetryCanaryRuntimeFallbackSignalAuthUserConfig;
135
+ auth?: ServerTelemetryHealthRuntimeFallbackSignalAuthUserConfig;
136
136
  }
137
137
 
138
- export interface ServerTelemetryCanaryRuntimeFallbackSignalAuthUserConfig {
138
+ export interface ServerTelemetryHealthRuntimeFallbackSignalAuthUserConfig {
139
139
  /**
140
140
  * Enable auth guard for runtime fallback signal endpoint.
141
141
  *
@@ -158,7 +158,7 @@ export interface ServerTelemetryCanaryRuntimeFallbackSignalAuthUserConfig {
158
158
  expectedValueEnv?: string;
159
159
  }
160
160
 
161
- export interface ServerTelemetryCanaryRuntimeFallbackSignalTrustPolicyUserConfig {
161
+ export interface ServerTelemetryHealthRuntimeFallbackSignalTrustPolicyUserConfig {
162
162
  /**
163
163
  * Allowlist of app names accepted by runtime fallback signal endpoint.
164
164
  * Empty means no app-name allowlist check.
@@ -200,61 +200,61 @@ export interface ServerTelemetryCanaryRuntimeFallbackSignalTrustPolicyUserConfig
200
200
  dedupeWindowMs?: number;
201
201
  }
202
202
 
203
- export interface ServerTelemetryCanaryUserConfig {
203
+ export interface ServerTelemetryHealthUserConfig {
204
204
  /**
205
- * Enable canary rollout/rollback orchestration.
205
+ * Enable telemetry health observation.
206
206
  *
207
207
  * @default false
208
208
  */
209
209
  enabled?: boolean;
210
210
  /**
211
- * Periodic canary evaluation interval in milliseconds.
211
+ * Periodic health evaluation interval in milliseconds.
212
212
  *
213
213
  * @default 15000
214
214
  */
215
215
  evaluationIntervalMs?: number;
216
216
  /**
217
- * Required consecutive healthy evaluations before promotion.
217
+ * Required consecutive healthy evaluations before reporting healthy.
218
218
  *
219
219
  * @default 3
220
220
  */
221
221
  minConsecutiveHealthyEvaluations?: number;
222
222
  /**
223
- * Consecutive failing evaluations before automated rollback.
223
+ * Consecutive failing evaluations before reporting unhealthy.
224
224
  *
225
225
  * @default 2
226
226
  */
227
- rollbackConsecutiveFailures?: number;
227
+ minConsecutiveFailedEvaluations?: number;
228
228
  /**
229
- * Maximum queue utilization ratio allowed during canary.
229
+ * Maximum queue utilization ratio allowed for healthy telemetry.
230
230
  *
231
231
  * @default 0.8
232
232
  */
233
233
  maxQueueUtilization?: number;
234
234
  /**
235
- * Maximum allowed total dropped envelopes during canary.
235
+ * Maximum allowed total dropped envelopes for healthy telemetry.
236
236
  *
237
237
  * @default 0
238
238
  */
239
239
  maxTotalDropped?: number;
240
240
  /**
241
- * Maximum allowed unhealthy exporters during canary.
241
+ * Maximum allowed unhealthy exporters for healthy telemetry.
242
242
  *
243
243
  * @default 0
244
244
  */
245
245
  maxUnhealthyExporters?: number;
246
246
  /**
247
- * Contract gate map used in rollout decisions.
247
+ * Contract gate map used in health evaluations.
248
248
  * `true` means passing, `false` means failing.
249
249
  */
250
250
  contractGates?: Record<
251
251
  string,
252
- boolean | ServerTelemetryCanaryContractGateUserConfig
252
+ boolean | ServerTelemetryHealthContractGateUserConfig
253
253
  >;
254
254
  /**
255
- * Contract-gate autopilot settings.
255
+ * Contract-gate snapshot observation settings.
256
256
  */
257
- autopilot?: ServerTelemetryCanaryAutopilotUserConfig;
257
+ snapshotObservation?: ServerTelemetrySnapshotObservationUserConfig;
258
258
  }
259
259
 
260
260
  export interface ServerTelemetryConfigExtension {
@@ -321,9 +321,9 @@ export interface ServerTelemetryUserConfig {
321
321
  */
322
322
  slo?: ServerTelemetrySloUserConfig;
323
323
  /**
324
- * Canary rollout and automated rollback orchestration policy.
324
+ * Telemetry health observation policy.
325
325
  */
326
- canary?: ServerTelemetryCanaryUserConfig;
326
+ health?: ServerTelemetryHealthUserConfig;
327
327
  exporters?: {
328
328
  /**
329
329
  * OpenTelemetry HTTP exporter.
@@ -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
  });