@bleedingdev/modern-js-runtime-extensions 3.9.0-ultramodern.6 → 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.
- 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
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { createRequire } from 'node:module';
|
|
4
|
-
import { tmpdir } from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { transformSync } from 'esbuild';
|
|
7
1
|
import * as contracts from '../src/workspaceEvents';
|
|
8
2
|
|
|
9
3
|
const cases = [
|
|
@@ -100,12 +94,9 @@ describe('workspace event contracts', () => {
|
|
|
100
94
|
);
|
|
101
95
|
expect(error).toMatchObject({
|
|
102
96
|
name: 'UltramodernWorkspaceEventValidationError',
|
|
103
|
-
message: `Invalid payload for UltraModern workspace event "${name}"`,
|
|
104
97
|
eventName: name,
|
|
105
98
|
payload,
|
|
106
99
|
});
|
|
107
|
-
// The existing public error is a data class, not an Error subclass.
|
|
108
|
-
expect(error).not.toBeInstanceOf(Error);
|
|
109
100
|
}
|
|
110
101
|
});
|
|
111
102
|
|
|
@@ -138,35 +129,24 @@ describe('workspace event contracts', () => {
|
|
|
138
129
|
});
|
|
139
130
|
}
|
|
140
131
|
|
|
141
|
-
it('
|
|
142
|
-
const payload =
|
|
143
|
-
to: '
|
|
144
|
-
state: new Date(0),
|
|
132
|
+
it('accepts consumer-owned fields, explicit undefined optionals and every signal id', () => {
|
|
133
|
+
const payload = {
|
|
134
|
+
to: '/raw',
|
|
145
135
|
replace: undefined,
|
|
136
|
+
state: undefined,
|
|
146
137
|
extension: 'consumer-owned',
|
|
147
|
-
}
|
|
138
|
+
};
|
|
148
139
|
expect(
|
|
149
140
|
contracts.assertUltramodernWorkspaceEventPayload(
|
|
150
141
|
'ultramodern:navigate',
|
|
151
142
|
payload,
|
|
152
143
|
),
|
|
153
144
|
).toBe(payload);
|
|
154
|
-
expect(payload.to).toBe(' /raw ');
|
|
155
|
-
for (const locale of ['en', 'cs', undefined]) {
|
|
156
|
-
expect(
|
|
157
|
-
contracts.isUltramodernRouteSettledPayload({
|
|
158
|
-
pathname: '/',
|
|
159
|
-
locale,
|
|
160
|
-
title: undefined,
|
|
161
|
-
}),
|
|
162
|
-
).toBe(true);
|
|
163
|
-
}
|
|
164
145
|
expect(
|
|
165
|
-
contracts.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
version: undefined,
|
|
146
|
+
contracts.isUltramodernRouteSettledPayload({
|
|
147
|
+
pathname: '/',
|
|
148
|
+
locale: undefined,
|
|
149
|
+
title: undefined,
|
|
170
150
|
}),
|
|
171
151
|
).toBe(true);
|
|
172
152
|
for (const signalId of [
|
|
@@ -177,19 +157,31 @@ describe('workspace event contracts', () => {
|
|
|
177
157
|
'save-data-behavior',
|
|
178
158
|
'cloudflare-ssr-cache-hints',
|
|
179
159
|
]) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
detail: undefined,
|
|
187
|
-
}),
|
|
188
|
-
).toBe(true);
|
|
189
|
-
}
|
|
160
|
+
expect(
|
|
161
|
+
contracts.isUltramodernPerformanceSignalPayload({
|
|
162
|
+
signalId,
|
|
163
|
+
status: 'warn',
|
|
164
|
+
}),
|
|
165
|
+
).toBe(true);
|
|
190
166
|
}
|
|
191
167
|
});
|
|
192
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
|
+
|
|
193
185
|
it('rejects invalid dispatch before notifying listeners', () => {
|
|
194
186
|
const target = new EventTarget();
|
|
195
187
|
const handler = rstest.fn();
|
|
@@ -226,166 +218,4 @@ describe('workspace event contracts', () => {
|
|
|
226
218
|
add.mockRestore();
|
|
227
219
|
remove.mockRestore();
|
|
228
220
|
});
|
|
229
|
-
|
|
230
|
-
it('keeps event-name payload inference and compile-time rejection', () => {
|
|
231
|
-
const temporaryRoot = mkdtempSync(
|
|
232
|
-
path.join(tmpdir(), 'workspace-events-types-'),
|
|
233
|
-
);
|
|
234
|
-
try {
|
|
235
|
-
const fixture = path.join(temporaryRoot, 'consumer.ts');
|
|
236
|
-
writeFileSync(
|
|
237
|
-
fixture,
|
|
238
|
-
`
|
|
239
|
-
import * as c from ${JSON.stringify(path.resolve(__dirname, '../src/workspaceEvents'))};
|
|
240
|
-
const target = new EventTarget();
|
|
241
|
-
const value: unknown = { to: '/' };
|
|
242
|
-
if (c.isUltramodernWorkspaceEventPayload(c.ultramodernWorkspaceEventNames.navigate, value)) {
|
|
243
|
-
const destination: string = value.to;
|
|
244
|
-
// @ts-expect-error Navigate payload has no pathname
|
|
245
|
-
value.pathname;
|
|
246
|
-
}
|
|
247
|
-
const event: CustomEvent<c.UltramodernRemoteReadyPayload> =
|
|
248
|
-
c.createUltramodernWorkspaceEvent('ultramodern:remote-ready', { appId: 'catalog' });
|
|
249
|
-
const cleanup: () => void = c.onUltramodernPerformanceSignal(target, (payload, event) => {
|
|
250
|
-
const status: c.UltramodernPerformanceReadinessSignalStatus = payload.status;
|
|
251
|
-
const signal: c.UltramodernPerformanceReadinessSignalId = event.detail.signalId;
|
|
252
|
-
});
|
|
253
|
-
const dispatched: boolean = c.dispatchUltramodernRouteSettled(target, { pathname: '/', locale: 'en' });
|
|
254
|
-
const asserted: c.UltramodernNavigatePayload = c.assertUltramodernWorkspaceEventPayload('ultramodern:navigate', value);
|
|
255
|
-
// @ts-expect-error Event names are closed
|
|
256
|
-
c.createUltramodernWorkspaceEvent('consumer:unknown', {});
|
|
257
|
-
// @ts-expect-error Wrong payload for the chosen event
|
|
258
|
-
c.dispatchUltramodernWorkspaceEvent(target, 'ultramodern:navigate', { appId: 'catalog' });
|
|
259
|
-
// @ts-expect-error Unsupported locale
|
|
260
|
-
c.dispatchUltramodernRouteSettled(target, { pathname: '/', locale: 'de' });
|
|
261
|
-
// @ts-expect-error Unsupported signal status
|
|
262
|
-
c.dispatchUltramodernPerformanceSignal(target, { signalId: 'bfcache', status: 'ok' });
|
|
263
|
-
`,
|
|
264
|
-
);
|
|
265
|
-
const requireFromPackage = createRequire(
|
|
266
|
-
path.resolve(__dirname, '../package.json'),
|
|
267
|
-
);
|
|
268
|
-
const compilerPackage = requireFromPackage.resolve(
|
|
269
|
-
'typescript/package.json',
|
|
270
|
-
);
|
|
271
|
-
const metadata = JSON.parse(readFileSync(compilerPackage, 'utf8'));
|
|
272
|
-
const compiler = path.resolve(
|
|
273
|
-
path.dirname(compilerPackage),
|
|
274
|
-
metadata.bin.tsc,
|
|
275
|
-
);
|
|
276
|
-
const config = path.join(temporaryRoot, 'tsconfig.json');
|
|
277
|
-
writeFileSync(
|
|
278
|
-
config,
|
|
279
|
-
JSON.stringify({
|
|
280
|
-
compilerOptions: {
|
|
281
|
-
noEmit: true,
|
|
282
|
-
strict: true,
|
|
283
|
-
skipLibCheck: true,
|
|
284
|
-
types: [],
|
|
285
|
-
target: 'ES2022',
|
|
286
|
-
module: 'NodeNext',
|
|
287
|
-
moduleResolution: 'NodeNext',
|
|
288
|
-
},
|
|
289
|
-
files: [fixture],
|
|
290
|
-
}),
|
|
291
|
-
);
|
|
292
|
-
const checked = spawnSync(
|
|
293
|
-
process.execPath,
|
|
294
|
-
[compiler, '--project', config, '--pretty', 'false'],
|
|
295
|
-
{
|
|
296
|
-
cwd: temporaryRoot,
|
|
297
|
-
encoding: 'utf8',
|
|
298
|
-
},
|
|
299
|
-
);
|
|
300
|
-
expect(checked.error).toBeUndefined();
|
|
301
|
-
expect({
|
|
302
|
-
status: checked.status,
|
|
303
|
-
output: checked.stdout + checked.stderr,
|
|
304
|
-
}).toEqual({ status: 0, output: '' });
|
|
305
|
-
} finally {
|
|
306
|
-
rmSync(temporaryRoot, { recursive: true, force: true });
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('supports native browser targets, shadow propagation and foreign realm events', async () => {
|
|
311
|
-
const requireFromBrowserFixture = createRequire(
|
|
312
|
-
path.resolve(
|
|
313
|
-
__dirname,
|
|
314
|
-
'../../../../tests/integration/rstest/basic-app-rstest-browser/package.json',
|
|
315
|
-
),
|
|
316
|
-
);
|
|
317
|
-
const { chromium } = requireFromBrowserFixture('playwright');
|
|
318
|
-
const source = readFileSync(
|
|
319
|
-
path.resolve(__dirname, '../src/workspaceEvents.ts'),
|
|
320
|
-
'utf8',
|
|
321
|
-
);
|
|
322
|
-
const compiled = transformSync(source, {
|
|
323
|
-
loader: 'ts',
|
|
324
|
-
format: 'cjs',
|
|
325
|
-
}).code;
|
|
326
|
-
const browser = await chromium.launch({ headless: true });
|
|
327
|
-
try {
|
|
328
|
-
const page = await browser.newPage();
|
|
329
|
-
const result = await page.evaluate((compiled: string) => {
|
|
330
|
-
const module = { exports: {} };
|
|
331
|
-
new Function('module', 'exports', compiled)(module, module.exports);
|
|
332
|
-
const api = module.exports as typeof contracts;
|
|
333
|
-
const host = document.createElement('div');
|
|
334
|
-
document.body.append(host);
|
|
335
|
-
const inner = document.createElement('button');
|
|
336
|
-
host.attachShadow({ mode: 'open' }).append(inner);
|
|
337
|
-
const payload = { to: '/shadow' };
|
|
338
|
-
const seen: unknown[] = [];
|
|
339
|
-
const off = api.onUltramodernNavigate(document, (detail, event) => {
|
|
340
|
-
seen.push({
|
|
341
|
-
same: detail === payload,
|
|
342
|
-
target: event.target === host,
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
api.dispatchUltramodernNavigate(inner, payload);
|
|
346
|
-
off();
|
|
347
|
-
api.dispatchUltramodernNavigate(inner, payload);
|
|
348
|
-
const iframe = document.createElement('iframe');
|
|
349
|
-
document.body.append(iframe);
|
|
350
|
-
const foreignWindow = iframe.contentWindow as Window &
|
|
351
|
-
typeof globalThis;
|
|
352
|
-
const foreignEvent = new foreignWindow.CustomEvent(
|
|
353
|
-
'ultramodern:navigate',
|
|
354
|
-
{
|
|
355
|
-
detail: payload,
|
|
356
|
-
},
|
|
357
|
-
);
|
|
358
|
-
let foreignReceived = false;
|
|
359
|
-
const offForeign = api.onUltramodernNavigate(
|
|
360
|
-
document,
|
|
361
|
-
(detail, event) => {
|
|
362
|
-
foreignReceived = detail === payload && event === foreignEvent;
|
|
363
|
-
},
|
|
364
|
-
);
|
|
365
|
-
document.dispatchEvent(foreignEvent);
|
|
366
|
-
offForeign();
|
|
367
|
-
const foreignTarget = new foreignWindow.EventTarget();
|
|
368
|
-
let foreignTargetReceived = false;
|
|
369
|
-
const offTarget = api.onUltramodernNavigate(foreignTarget, detail => {
|
|
370
|
-
foreignTargetReceived = detail === payload;
|
|
371
|
-
});
|
|
372
|
-
api.dispatchUltramodernNavigate(foreignTarget, payload);
|
|
373
|
-
offTarget();
|
|
374
|
-
return {
|
|
375
|
-
seen,
|
|
376
|
-
foreignReceived,
|
|
377
|
-
foreignTargetReceived,
|
|
378
|
-
distinctRealm: !(foreignEvent instanceof CustomEvent),
|
|
379
|
-
};
|
|
380
|
-
}, compiled);
|
|
381
|
-
expect(result).toEqual({
|
|
382
|
-
seen: [{ same: true, target: true }],
|
|
383
|
-
foreignReceived: true,
|
|
384
|
-
foreignTargetReceived: true,
|
|
385
|
-
distinctRealm: true,
|
|
386
|
-
});
|
|
387
|
-
} finally {
|
|
388
|
-
await browser.close();
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
221
|
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createSafeFailureHttpResult,
|
|
3
|
-
createSafeJsonFailureResponse,
|
|
4
|
-
} from '../src/safeFailure';
|
|
5
|
-
|
|
6
|
-
describe('native safe failure contract compatibility', () => {
|
|
7
|
-
it('should create safe JSON failure envelopes without leaking raw messages', async () => {
|
|
8
|
-
const result = createSafeFailureHttpResult(
|
|
9
|
-
new Error('database password leaked in stack'),
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
expect(result).toEqual({
|
|
13
|
-
status: 500,
|
|
14
|
-
body: {
|
|
15
|
-
success: false,
|
|
16
|
-
error: {
|
|
17
|
-
code: 'INTERNAL_SERVER_ERROR',
|
|
18
|
-
message: 'Internal Server Error',
|
|
19
|
-
status: 500,
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
headers: {
|
|
23
|
-
'content-type': 'application/json; charset=utf-8',
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const response = createSafeJsonFailureResponse(
|
|
28
|
-
new Error('database password leaked in stack'),
|
|
29
|
-
);
|
|
30
|
-
expect(response.status).toBe(500);
|
|
31
|
-
expect(response.headers.get('content-type')).toContain('application/json');
|
|
32
|
-
await expect(response.json()).resolves.toEqual(result.body);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should preserve service unavailable status and Retry-After metadata', () => {
|
|
36
|
-
const error = Object.assign(new Error('maintenance window details'), {
|
|
37
|
-
status: 503,
|
|
38
|
-
retryAfterSeconds: 120,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
expect(createSafeFailureHttpResult(error)).toEqual({
|
|
42
|
-
status: 503,
|
|
43
|
-
body: {
|
|
44
|
-
success: false,
|
|
45
|
-
error: {
|
|
46
|
-
code: 'SERVICE_UNAVAILABLE',
|
|
47
|
-
message: 'Service Unavailable',
|
|
48
|
-
status: 503,
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
headers: {
|
|
52
|
-
'content-type': 'application/json; charset=utf-8',
|
|
53
|
-
'Retry-After': '120',
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it.each([
|
|
59
|
-
['delay-seconds text', '120', '120'],
|
|
60
|
-
[
|
|
61
|
-
'the largest safely serializable delay',
|
|
62
|
-
Number.MAX_SAFE_INTEGER,
|
|
63
|
-
String(Number.MAX_SAFE_INTEGER),
|
|
64
|
-
],
|
|
65
|
-
[
|
|
66
|
-
'an HTTP-date string',
|
|
67
|
-
'Sun, 30 Aug 2026 20:00:00 GMT',
|
|
68
|
-
'Sun, 30 Aug 2026 20:00:00 GMT',
|
|
69
|
-
],
|
|
70
|
-
[
|
|
71
|
-
'a Date instance',
|
|
72
|
-
new Date(Date.UTC(2026, 7, 30, 20, 0, 0)),
|
|
73
|
-
'Sun, 30 Aug 2026 20:00:00 GMT',
|
|
74
|
-
],
|
|
75
|
-
])('should preserve %s in Retry-After', (_, retryAfter, expected) => {
|
|
76
|
-
const result = createSafeFailureHttpResult({
|
|
77
|
-
status: 503,
|
|
78
|
-
retryAfter,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
expect(result.headers['Retry-After']).toBe(expected);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it.each([
|
|
85
|
-
['a header-injection payload', '120\r\nX-Injected: true'],
|
|
86
|
-
['a non-numeric delay', 'after maintenance'],
|
|
87
|
-
['a delay too large for integer serialization', 1e21],
|
|
88
|
-
['an invalid HTTP date', 'Wed, 99 Jun 2026 25:61:61 GMT'],
|
|
89
|
-
['an invalid Date object', new Date(Number.NaN)],
|
|
90
|
-
])('should ignore %s in Retry-After without throwing', (_, retryAfter) => {
|
|
91
|
-
const error = Object.assign(new Error('maintenance window details'), {
|
|
92
|
-
status: 503,
|
|
93
|
-
retryAfter,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
expect(createSafeFailureHttpResult(error)).toEqual({
|
|
97
|
-
status: 503,
|
|
98
|
-
body: {
|
|
99
|
-
success: false,
|
|
100
|
-
error: {
|
|
101
|
-
code: 'SERVICE_UNAVAILABLE',
|
|
102
|
-
message: 'Service Unavailable',
|
|
103
|
-
status: 503,
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
headers: {
|
|
107
|
-
'content-type': 'application/json; charset=utf-8',
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
expect(() => createSafeJsonFailureResponse(error)).not.toThrow();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should clamp invalid thrown status values to 500', () => {
|
|
114
|
-
expect(createSafeFailureHttpResult({ status: 200 }).status).toBe(500);
|
|
115
|
-
expect(createSafeFailureHttpResult({ statusCode: 799 }).status).toBe(500);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
normalizePreloadBehavior,
|
|
3
|
-
resolvePreloadFromPrefetch,
|
|
4
|
-
} from '../src/prefetchPolicy';
|
|
5
|
-
|
|
6
|
-
const behaviors = ['intent', 'render', 'viewport', 'none'] as const;
|
|
7
|
-
|
|
8
|
-
describe('router prefetch precedence', () => {
|
|
9
|
-
describe.each(behaviors)('React Router prefetch=%s', prefetch => {
|
|
10
|
-
it('inherits prefetch when preload is omitted', () => {
|
|
11
|
-
expect(normalizePreloadBehavior(undefined, prefetch)).toBe(prefetch);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it.each([
|
|
15
|
-
...behaviors,
|
|
16
|
-
false,
|
|
17
|
-
] as const)('honors explicit preload=%s using none for disabled', preload => {
|
|
18
|
-
expect(normalizePreloadBehavior(preload, prefetch)).toBe(
|
|
19
|
-
preload === false ? 'none' : preload,
|
|
20
|
-
);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe.each([undefined, ...behaviors])('TanStack prefetch=%s', prefetch => {
|
|
25
|
-
it('inherits supported prefetch with viewport as the default', () => {
|
|
26
|
-
expect(resolvePreloadFromPrefetch(prefetch, undefined)).toBe(
|
|
27
|
-
prefetch === 'none' ? false : (prefetch ?? 'viewport'),
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it.each([
|
|
32
|
-
...behaviors,
|
|
33
|
-
false,
|
|
34
|
-
null,
|
|
35
|
-
'custom',
|
|
36
|
-
0,
|
|
37
|
-
])('passes explicit preload=%s through unchanged', preload => {
|
|
38
|
-
expect(resolvePreloadFromPrefetch(prefetch, preload)).toBe(preload);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('preserves the routers different defaults', () => {
|
|
43
|
-
expect(normalizePreloadBehavior(undefined, 'render')).toBe('render');
|
|
44
|
-
expect(resolvePreloadFromPrefetch(undefined, undefined)).toBe('viewport');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('preserves object identity for TanStack preload pass-through', () => {
|
|
48
|
-
const preload = {};
|
|
49
|
-
expect(resolvePreloadFromPrefetch('none', preload)).toBe(preload);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
configure,
|
|
3
|
-
createRequest,
|
|
4
|
-
} from '@modern-js/runtime-extensions/request-policy/server';
|
|
5
|
-
import { storage } from '@modern-js/runtime-utils/node';
|
|
6
|
-
import nock from 'nock';
|
|
7
|
-
|
|
8
|
-
describe('identity binding headers', () => {
|
|
9
|
-
const path = '/api';
|
|
10
|
-
const method = 'GET';
|
|
11
|
-
const response = {
|
|
12
|
-
code: 200,
|
|
13
|
-
data: {
|
|
14
|
-
message: 'hello Modernjs',
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const run = (
|
|
19
|
-
headers: Record<string, string>,
|
|
20
|
-
callback: () => Promise<void> | void,
|
|
21
|
-
) =>
|
|
22
|
-
storage.run(
|
|
23
|
-
{
|
|
24
|
-
headers,
|
|
25
|
-
monitors: {} as any,
|
|
26
|
-
},
|
|
27
|
-
callback,
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
test('should not forward case-variant protected identity headers beside server-derived binding', async () => {
|
|
31
|
-
const producer = 'producer-identity-case-collision';
|
|
32
|
-
const producerUrl = 'http://127.0.0.1:9088';
|
|
33
|
-
|
|
34
|
-
await run({}, async () => {
|
|
35
|
-
nock(producerUrl).get(path).reply(200, response);
|
|
36
|
-
const customRequest = rs.fn((requestPath: any, init: any) =>
|
|
37
|
-
fetch(requestPath, init),
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
configure({
|
|
41
|
-
request: customRequest as unknown as typeof fetch,
|
|
42
|
-
requestId: producer,
|
|
43
|
-
allowedHeaders: ['X-Tenant-Id'],
|
|
44
|
-
resolveHeaders: () => ({
|
|
45
|
-
'X-Tenant-Id': 'tenant-resolved',
|
|
46
|
-
}),
|
|
47
|
-
operationContract: {
|
|
48
|
-
enabled: false,
|
|
49
|
-
},
|
|
50
|
-
identityBinding: {
|
|
51
|
-
deriveHeaders: () => ({
|
|
52
|
-
'x-tenant-id': 'tenant-bound',
|
|
53
|
-
}),
|
|
54
|
-
strict: false,
|
|
55
|
-
},
|
|
56
|
-
setDomain: () => producerUrl,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const request = createRequest(
|
|
60
|
-
path,
|
|
61
|
-
method,
|
|
62
|
-
8080,
|
|
63
|
-
undefined,
|
|
64
|
-
undefined,
|
|
65
|
-
producer,
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
await request();
|
|
69
|
-
const headers = customRequest.mock.calls[0]?.[1]?.headers;
|
|
70
|
-
|
|
71
|
-
expect(headers['X-Tenant-Id']).toBeUndefined();
|
|
72
|
-
expect(headers['x-tenant-id']).toBe('tenant-resolved');
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
});
|