@bleedingdev/modern-js-plugin-tanstack 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.
@@ -1,104 +0,0 @@
1
- import type { RouterProviderFactory } from '@modern-js/runtime-extensions/router-provider';
2
- import { rstest } from '@rstest/core';
3
- import type { RouterExtendsHooks } from '../src/runtime/hooks';
4
-
5
- type RuntimeContextModule = typeof import('@modern-js/runtime/context');
6
- type RouterModule = typeof import('../src/runtime/router');
7
-
8
- type Vertical = {
9
- context: RuntimeContextModule;
10
- providers: typeof import('@modern-js/runtime-extensions/router-provider');
11
- factory: RouterProviderFactory;
12
- hooks: RouterExtendsHooks;
13
- Link: unknown;
14
- routerPlugin: RouterModule['routerPlugin'];
15
- useMatches: unknown;
16
- };
17
-
18
- async function loadVertical(): Promise<Vertical> {
19
- rstest.resetModules();
20
- const routerModule = (await import('../src/runtime/router')) as RouterModule;
21
- const context = (await import(
22
- '@modern-js/runtime/context'
23
- )) as RuntimeContextModule;
24
- const { tanstackRouterProviderFactory } = await import(
25
- '../src/runtime/register'
26
- );
27
- const { routerProviderRegistryHooks } = await import('../src/runtime/hooks');
28
- const { Link } = await import('../src/runtime/prefetchLink');
29
- const { useMatches } = await import('../src/runtime/routeHooks');
30
-
31
- const providers = await import(
32
- '@modern-js/runtime-extensions/router-provider'
33
- );
34
- return {
35
- context,
36
- providers,
37
- factory: tanstackRouterProviderFactory,
38
- hooks: routerProviderRegistryHooks,
39
- Link,
40
- routerPlugin: routerModule.routerPlugin,
41
- useMatches,
42
- };
43
- }
44
-
45
- function setupRouterWrapper(vertical: Vertical) {
46
- let onBeforeRender:
47
- | ((context: any, interrupt: (value?: unknown) => unknown) => unknown)
48
- | undefined;
49
-
50
- const plugin = vertical.routerPlugin({ framework: 'tanstack' });
51
- plugin.setup?.({
52
- getHooks: () => vertical.hooks,
53
- onAfterCreateRouter: () => undefined,
54
- getRuntimeConfig: () => ({ router: { framework: 'tanstack' } }),
55
- onBeforeRender: listener => {
56
- onBeforeRender = listener;
57
- },
58
- wrapRoot: () => undefined,
59
- });
60
-
61
- expect(typeof onBeforeRender).toBe('function');
62
- const runtimeContext: any = {};
63
- onBeforeRender?.(runtimeContext, () => undefined);
64
- return runtimeContext.router as { Link: unknown; useMatches: unknown };
65
- }
66
-
67
- describe('TanStack provider runtime-realm isolation', () => {
68
- beforeEach(() => {
69
- rstest.resetModules();
70
- (
71
- globalThis as typeof globalThis & {
72
- __webpack_require__?: { u: (chunkId: unknown) => string };
73
- }
74
- ).__webpack_require__ = { u: chunkId => String(chunkId) };
75
- });
76
-
77
- afterEach(() => {
78
- rstest.resetModules();
79
- });
80
-
81
- it('binds each production router wrapper to its own real provider graph', async () => {
82
- const warnSpy = rstest.spyOn(console, 'warn').mockImplementation(() => {});
83
- try {
84
- const verticalA = await loadVertical();
85
- const routerA = setupRouterWrapper(verticalA);
86
- expect(routerA.Link).toBe(verticalA.Link);
87
- expect(routerA.useMatches).toBe(verticalA.useMatches);
88
-
89
- const verticalB = await loadVertical();
90
- expect(verticalB.context).not.toBe(verticalA.context);
91
- expect(verticalB.factory).not.toBe(verticalA.factory);
92
- expect(verticalB.Link).not.toBe(verticalA.Link);
93
- expect(verticalB.useMatches).not.toBe(verticalA.useMatches);
94
-
95
- const routerB = setupRouterWrapper(verticalB);
96
- expect(routerB.Link).toBe(verticalB.Link);
97
- expect(routerB.useMatches).toBe(verticalB.useMatches);
98
- expect(routerB.Link).not.toBe(verticalA.Link);
99
- expect(routerB.useMatches).not.toBe(verticalA.useMatches);
100
- } finally {
101
- warnSpy.mockRestore();
102
- }
103
- });
104
- });
@@ -1,162 +0,0 @@
1
- const path = require('node:path');
2
-
3
- const scenario = process.argv[2];
4
- const serverPath = process.argv[3];
5
- const clientPath = process.argv[4];
6
- let moduleLoads = 0;
7
-
8
- global.__webpack_chunk_load__ = async () => {};
9
- global.__webpack_require__ = () => {
10
- moduleLoads += 1;
11
- return {
12
- action(formData) {
13
- return formData;
14
- },
15
- };
16
- };
17
- global.__rspack_rsc_hot_reloader__ = { on: () => () => {} };
18
- global.__rspack_rsc_manifest__ = {
19
- clientManifest: {},
20
- entryCssFiles: {},
21
- entryJsFiles: [],
22
- moduleLoading: { prefix: '', crossOrigin: '' },
23
- serverConsumerModuleMap: {},
24
- serverManifest: Object.fromEntries(
25
- Array.from({ length: 8 }, (_, index) => [
26
- `action-${index}`,
27
- { id: 'server-module', chunks: [], name: 'action' },
28
- ]),
29
- ),
30
- };
31
-
32
- let mapArrayConstructions = 0;
33
- if (scenario === 'cycle') {
34
- const NativeMap = Map;
35
- global.Map = class CountedMap extends NativeMap {
36
- constructor(iterable) {
37
- if (Array.isArray(iterable)) {
38
- mapArrayConstructions += 1;
39
- }
40
- super(iterable);
41
- }
42
- };
43
- }
44
-
45
- const server = require(path.resolve(serverPath));
46
-
47
- function output(value) {
48
- process.stdout.write(JSON.stringify(value), () => process.exit(0));
49
- }
50
-
51
- async function main() {
52
- switch (scenario) {
53
- case 'cycle': {
54
- const body = new FormData();
55
- body.set(
56
- '0',
57
- JSON.stringify([
58
- ...Array.from({ length: 8 }, (_, index) => [`key-${index}`, index]),
59
- ...Array.from({ length: 8 }, () => '$Q0'),
60
- ]),
61
- );
62
- let error;
63
- try {
64
- await server.decodeReply(body);
65
- } catch (caught) {
66
- error = caught;
67
- }
68
- output({ mapArrayConstructions, rejected: error instanceof Error });
69
- return;
70
- }
71
- case 'referenced-form-data': {
72
- const body = new FormData();
73
- body.append('_1_item', 'first');
74
- body.append('_2_item', 'second');
75
- body.append('0', JSON.stringify(['$K1', '$K2']));
76
- let keyIterations = 0;
77
- const originalKeys = body.keys.bind(body);
78
- body.keys = () => {
79
- keyIterations += 1;
80
- return originalKeys();
81
- };
82
- const decoded = await server.decodeReply(body);
83
- output({
84
- keyIterations,
85
- values: decoded.map(value => value.get('item')),
86
- });
87
- return;
88
- }
89
- case 'blob-type': {
90
- const body = new FormData();
91
- body.set('1', 'not-a-blob');
92
- body.set('0', JSON.stringify('$B1'));
93
- let error;
94
- try {
95
- await server.decodeReply(body);
96
- } catch (caught) {
97
- error = caught;
98
- }
99
- output({
100
- rejected: error instanceof Error,
101
- returnedString: error === undefined,
102
- });
103
- return;
104
- }
105
- case 'async-iterator': {
106
- let throwCalls = 0;
107
- const reason = new Error('iterator failed');
108
- const iterator = {
109
- next: () => Promise.reject(reason),
110
- throw: () => {
111
- throwCalls += 1;
112
- return throwCalls < 6
113
- ? Promise.reject(reason)
114
- : {
115
- // biome-ignore lint/suspicious/noThenProperty: The regression requires a deliberately pending thenable to bound the vulnerable recursion.
116
- then: () => {},
117
- };
118
- },
119
- };
120
- const iterable = { [Symbol.asyncIterator]: () => iterator };
121
- try {
122
- await server.decodeReplyFromAsyncIterable(iterable);
123
- } catch {}
124
- await new Promise(resolve => setImmediate(resolve));
125
- output({ throwCalls });
126
- return;
127
- }
128
- case 'action-load': {
129
- const body = new FormData();
130
- for (let index = 0; index < 8; index += 1) {
131
- body.append(`$ACTION_ID_action-${index}`, '');
132
- }
133
- const action = await server.decodeAction(body);
134
- if (action === null) {
135
- throw new Error('Expected an action');
136
- }
137
- const submitted = await action();
138
- output({
139
- moduleLoads,
140
- submittedEntries: Array.from(submitted.entries()),
141
- });
142
- return;
143
- }
144
- case 'form-data-roundtrip': {
145
- const client = require(path.resolve(clientPath));
146
- const nested = new FormData();
147
- nested.append('item', 'first');
148
- nested.append('item', 'second');
149
- const encoded = await client.encodeReply({ nested });
150
- const decoded = await server.decodeReply(encoded);
151
- output({ values: decoded.nested.getAll('item') });
152
- return;
153
- }
154
- default:
155
- throw new Error(`Unknown scenario: ${scenario}`);
156
- }
157
- }
158
-
159
- main().catch(error => {
160
- console.error(error);
161
- process.exitCode = 1;
162
- });
@@ -1,23 +0,0 @@
1
- import {
2
- modifyRoutes as canonicalModifyRoutes,
3
- routerProviderRegistryHooks,
4
- } from '@modern-js/runtime/context';
5
- import * as tanstackHooks from '../../src/runtime/hooks';
6
- import { tanstackRouterPlugin as browserPlugin } from '../../src/runtime/plugin';
7
- import { tanstackRouterPlugin as nodePlugin } from '../../src/runtime/plugin.node';
8
-
9
- describe('tanstack router hooks single declaration source', () => {
10
- test('re-exports and registers the canonical hook instances', () => {
11
- // Identity matters: separate instances would split the hook registry
12
- // between the built-in router wrapper and this provider.
13
- expect(tanstackHooks.modifyRoutes).toBe(canonicalModifyRoutes);
14
- expect(tanstackHooks.routerProviderRegistryHooks).toBe(
15
- routerProviderRegistryHooks,
16
- );
17
- expect(tanstackHooks.modifyRoutes).toBe(
18
- routerProviderRegistryHooks.modifyRoutes,
19
- );
20
- expect(browserPlugin().registryHooks).toBe(routerProviderRegistryHooks);
21
- expect(nodePlugin().registryHooks).toBe(routerProviderRegistryHooks);
22
- });
23
- });
@@ -1,84 +0,0 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { createRequire } from 'node:module';
3
- import path from 'node:path';
4
-
5
- type DecoderScenario =
6
- | 'async-iterator'
7
- | 'blob-type'
8
- | 'cycle'
9
- | 'form-data-roundtrip';
10
-
11
- const resolvePackage = createRequire(
12
- path.join(__dirname, 'rsc-decoder-security-resolver.cjs'),
13
- ).resolve;
14
- const childPath = path.join(
15
- __dirname,
16
- 'fixtures',
17
- 'rscDecoderSecurityChild.cjs',
18
- );
19
-
20
- function runDecoderScenario(options: {
21
- entry: 'edge' | 'node';
22
- scenario: DecoderScenario;
23
- }): Record<string, unknown> {
24
- const result = spawnSync(
25
- process.execPath,
26
- [
27
- '--conditions=react-server',
28
- childPath,
29
- options.scenario,
30
- resolvePackage(`react-server-dom-rspack/server.${options.entry}`),
31
- resolvePackage(`react-server-dom-rspack/client.${options.entry}`),
32
- ],
33
- {
34
- encoding: 'utf8',
35
- env: { ...process.env, NODE_ENV: 'production' },
36
- timeout: 5_000,
37
- },
38
- );
39
-
40
- if (result.error || result.status !== 0) {
41
- throw new Error(
42
- [
43
- `RSC decoder child failed for ${options.entry}/production/${options.scenario}`,
44
- result.stdout,
45
- result.stderr,
46
- ]
47
- .filter(Boolean)
48
- .join('\n'),
49
- { cause: result.error },
50
- );
51
- }
52
-
53
- return JSON.parse(result.stdout) as Record<string, unknown>;
54
- }
55
-
56
- for (const entry of ['node', 'edge'] as const) {
57
- describe(`${entry} production Flight decoder security`, () => {
58
- test('bounds cyclic Map reconstruction before invoking constructors', () => {
59
- const result = runDecoderScenario({ entry, scenario: 'cycle' });
60
-
61
- expect(result.rejected).toBe(true);
62
- expect(result.mapArrayConstructions).toBeLessThanOrEqual(2);
63
- });
64
-
65
- test('round-trips referenced FormData through the public client and server APIs', () => {
66
- expect(
67
- runDecoderScenario({ entry, scenario: 'form-data-roundtrip' }),
68
- ).toEqual({ values: ['first', 'second'] });
69
- });
70
-
71
- test('rejects a Blob reference backed by a string', () => {
72
- expect(runDecoderScenario({ entry, scenario: 'blob-type' })).toEqual({
73
- rejected: true,
74
- returnedString: false,
75
- });
76
- });
77
-
78
- test('does not recursively throw into a rejecting async iterator', () => {
79
- expect(runDecoderScenario({ entry, scenario: 'async-iterator' })).toEqual(
80
- { throwCalls: 1 },
81
- );
82
- });
83
- });
84
- }