@bleedingdev/modern-js-plugin-tanstack 3.5.0-ultramodern.31 → 3.5.0-ultramodern.33

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.
@@ -45,6 +45,7 @@ function sanitizeSlotArg(value, seen) {
45
45
  if (null == value) return value;
46
46
  if ('string' == typeof value || 'number' == typeof value || 'boolean' == typeof value || 'bigint' == typeof value) return value;
47
47
  if ('object' != typeof value && 'function' != typeof value) return String(value);
48
+ if ('function' == typeof value) return '[Function]';
48
49
  if (seen.has(value)) return '[Circular]';
49
50
  seen.add(value);
50
51
  if (Array.isArray(value)) return value.map((item)=>sanitizeSlotArg(item, seen));
@@ -33,7 +33,7 @@ __webpack_require__.d(__webpack_exports__, {
33
33
  const context_namespaceObject = require("@modern-js/runtime/context");
34
34
  function getTanstackRouterState(context) {
35
35
  const state = (0, context_namespaceObject.getRouterRuntimeState)(context);
36
- if (void 0 === state || 'tanstack' !== state.framework) return;
36
+ if (void 0 === state || 'tanstack' !== state.framework || void 0 === state.instance) return;
37
37
  return state;
38
38
  }
39
39
  exports.getTanstackRouterState = __webpack_exports__.getTanstackRouterState;
@@ -67,9 +67,9 @@ function toTanstackRouteObject(route, globalApp) {
67
67
  shouldRevalidate: route.shouldRevalidate,
68
68
  handle: getRouteHandle(route),
69
69
  index: route.index,
70
- hasLoader: Boolean(route.loader),
71
- hasClientLoader: Boolean(route.clientData),
72
- hasAction: Boolean(route.action),
70
+ hasLoader: route.hasLoader || Boolean(route.loader),
71
+ hasClientLoader: route.hasClientLoader || Boolean(route.clientData),
72
+ hasAction: route.hasAction || Boolean(route.action),
73
73
  ...route.isClientComponent ? {
74
74
  isClientComponent: true
75
75
  } : {},
@@ -13,6 +13,7 @@ function sanitizeSlotArg(value, seen) {
13
13
  if (null == value) return value;
14
14
  if ('string' == typeof value || 'number' == typeof value || 'boolean' == typeof value || 'bigint' == typeof value) return value;
15
15
  if ('object' != typeof value && 'function' != typeof value) return String(value);
16
+ if ('function' == typeof value) return '[Function]';
16
17
  if (seen.has(value)) return '[Circular]';
17
18
  seen.add(value);
18
19
  if (Array.isArray(value)) return value.map((item)=>sanitizeSlotArg(item, seen));
@@ -1,7 +1,7 @@
1
1
  import { getRouterRuntimeState } from "@modern-js/runtime/context";
2
2
  function getTanstackRouterState(context) {
3
3
  const state = getRouterRuntimeState(context);
4
- if (void 0 === state || 'tanstack' !== state.framework) return;
4
+ if (void 0 === state || 'tanstack' !== state.framework || void 0 === state.instance) return;
5
5
  return state;
6
6
  }
7
7
  export { getTanstackRouterState };
@@ -25,9 +25,9 @@ function toTanstackRouteObject(route, globalApp) {
25
25
  shouldRevalidate: route.shouldRevalidate,
26
26
  handle: getRouteHandle(route),
27
27
  index: route.index,
28
- hasLoader: Boolean(route.loader),
29
- hasClientLoader: Boolean(route.clientData),
30
- hasAction: Boolean(route.action),
28
+ hasLoader: route.hasLoader || Boolean(route.loader),
29
+ hasClientLoader: route.hasClientLoader || Boolean(route.clientData),
30
+ hasAction: route.hasAction || Boolean(route.action),
31
31
  ...route.isClientComponent ? {
32
32
  isClientComponent: true
33
33
  } : {},
@@ -14,6 +14,7 @@ function sanitizeSlotArg(value, seen) {
14
14
  if (null == value) return value;
15
15
  if ('string' == typeof value || 'number' == typeof value || 'boolean' == typeof value || 'bigint' == typeof value) return value;
16
16
  if ('object' != typeof value && 'function' != typeof value) return String(value);
17
+ if ('function' == typeof value) return '[Function]';
17
18
  if (seen.has(value)) return '[Circular]';
18
19
  seen.add(value);
19
20
  if (Array.isArray(value)) return value.map((item)=>sanitizeSlotArg(item, seen));
@@ -2,7 +2,7 @@ import "node:module";
2
2
  import { getRouterRuntimeState } from "@modern-js/runtime/context";
3
3
  function getTanstackRouterState(context) {
4
4
  const state = getRouterRuntimeState(context);
5
- if (void 0 === state || 'tanstack' !== state.framework) return;
5
+ if (void 0 === state || 'tanstack' !== state.framework || void 0 === state.instance) return;
6
6
  return state;
7
7
  }
8
8
  export { getTanstackRouterState };
@@ -26,9 +26,9 @@ function toTanstackRouteObject(route, globalApp) {
26
26
  shouldRevalidate: route.shouldRevalidate,
27
27
  handle: getRouteHandle(route),
28
28
  index: route.index,
29
- hasLoader: Boolean(route.loader),
30
- hasClientLoader: Boolean(route.clientData),
31
- hasAction: Boolean(route.action),
29
+ hasLoader: route.hasLoader || Boolean(route.loader),
30
+ hasClientLoader: route.hasClientLoader || Boolean(route.clientData),
31
+ hasAction: route.hasAction || Boolean(route.action),
32
32
  ...route.isClientComponent ? {
33
33
  isClientComponent: true
34
34
  } : {},
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "tanstack-router"
20
20
  ],
21
- "version": "3.5.0-ultramodern.31",
21
+ "version": "3.5.0-ultramodern.33",
22
22
  "engines": {
23
23
  "node": ">=20"
24
24
  },
@@ -98,14 +98,14 @@
98
98
  "@tanstack/react-router": "1.170.16",
99
99
  "@tanstack/router-core": "1.171.13",
100
100
  "react-server-dom-rspack": "0.0.2",
101
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.31",
102
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.31",
103
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.31",
104
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.31"
101
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.33",
102
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.33",
103
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.33",
104
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.33"
105
105
  },
106
106
  "peerDependencies": {
107
- "@modern-js/app-tools": "3.5.0-ultramodern.31",
108
- "@modern-js/runtime": "3.5.0-ultramodern.31",
107
+ "@modern-js/app-tools": "3.5.0-ultramodern.33",
108
+ "@modern-js/runtime": "3.5.0-ultramodern.33",
109
109
  "react": "^19.2.7",
110
110
  "react-dom": "^19.2.7"
111
111
  },
@@ -120,9 +120,9 @@
120
120
  "@typescript/native-preview": "7.0.0-dev.20260628.1",
121
121
  "react": "^19.2.7",
122
122
  "react-dom": "^19.2.7",
123
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.5.0-ultramodern.31",
124
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.31",
125
- "@scripts/rstest-config": "2.66.0"
123
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.5.0-ultramodern.33",
124
+ "@scripts/rstest-config": "2.66.0",
125
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.33"
126
126
  },
127
127
  "sideEffects": [
128
128
  "./src/runtime/register.ts",
package/rstest.config.mts CHANGED
@@ -25,6 +25,7 @@ export default {
25
25
  include: [
26
26
  'tests/router/cli.test.ts',
27
27
  'tests/router/fastDefaults.test.ts',
28
+ 'tests/router/flightSerialization.roundtrip.test.tsx',
28
29
  'tests/router/generateRouteArtifacts.test.ts',
29
30
  'tests/router/hooks.test.ts',
30
31
  'tests/router/loaderBridge.test.ts',
@@ -33,6 +34,7 @@ export default {
33
34
  'tests/router/register.test.ts',
34
35
  'tests/router/routeHooks.test.ts',
35
36
  'tests/router/rsc.test.tsx',
37
+ 'tests/router/slotUsageSanitizer.test.ts',
36
38
  'tests/router/ssrPreload.test.ts',
37
39
  'tests/router/tanstackTypes.test.ts',
38
40
  'tests/router/routeTree.test.ts',
@@ -50,6 +50,10 @@ function sanitizeSlotArg(
50
50
  return String(value);
51
51
  }
52
52
 
53
+ if (typeof value === 'function') {
54
+ return '[Function]';
55
+ }
56
+
53
57
  if (seen.has(value)) {
54
58
  return '[Circular]';
55
59
  }
@@ -23,7 +23,11 @@ export function getTanstackRouterState(
23
23
  context: object,
24
24
  ): TanstackRouterState | undefined {
25
25
  const state = getRouterRuntimeState(context);
26
- if (state === undefined || state.framework !== 'tanstack') {
26
+ if (
27
+ state === undefined ||
28
+ state.framework !== 'tanstack' ||
29
+ state.instance === undefined
30
+ ) {
27
31
  return undefined;
28
32
  }
29
33
 
@@ -69,9 +69,9 @@ function toTanstackRouteObject(
69
69
  shouldRevalidate: route.shouldRevalidate,
70
70
  handle: getRouteHandle(route),
71
71
  index: route.index,
72
- hasLoader: Boolean(route.loader),
73
- hasClientLoader: Boolean(route.clientData),
74
- hasAction: Boolean(route.action),
72
+ hasLoader: route.hasLoader || Boolean(route.loader),
73
+ hasClientLoader: route.hasClientLoader || Boolean(route.clientData),
74
+ hasAction: route.hasAction || Boolean(route.action),
75
75
  ...(route.isClientComponent ? { isClientComponent: true } : {}),
76
76
  ...(route.inValidSSRRoute ? { inValidSSRRoute: true } : {}),
77
77
  lazyImport: route.lazyImport,
@@ -0,0 +1,137 @@
1
+ import type React from 'react';
2
+ import { isValidElement } from 'react';
3
+ import { createRscProxy } from '../../src/runtime/rsc/createRscProxy';
4
+ import {
5
+ reviveTanstackRscFlightValues,
6
+ serializeTanstackRscFlightValues,
7
+ } from '../../src/runtime/rsc/flightSerialization';
8
+ import {
9
+ RENDERABLE_RSC,
10
+ SERVER_COMPONENT_STREAM,
11
+ type ServerComponentStream,
12
+ } from '../../src/runtime/rsc/symbols';
13
+
14
+ type RoundTripFixture = {
15
+ bigintValue: bigint;
16
+ booleanValue: boolean;
17
+ circular: Record<string, unknown>;
18
+ dateValue: Date;
19
+ falseValue: boolean;
20
+ mapValue: Map<unknown, unknown>;
21
+ nested: {
22
+ array: unknown[];
23
+ object: {
24
+ child: unknown;
25
+ };
26
+ };
27
+ nullValue: null;
28
+ numberValue: number;
29
+ rscProxy: React.ReactElement & Record<PropertyKey, unknown>;
30
+ setValue: Set<unknown>;
31
+ sharedA: Record<string, unknown>;
32
+ sharedB: Record<string, unknown>;
33
+ stringValue: string;
34
+ undefinedValue: undefined;
35
+ };
36
+
37
+ function createStream(): ServerComponentStream {
38
+ return {
39
+ createReplayStream: () => new ReadableStream<Uint8Array>(),
40
+ };
41
+ }
42
+
43
+ test('round-trips TanStack RSC flight values across supported value types', async () => {
44
+ const shared = { marker: 'shared' };
45
+ const circular: Record<string, unknown> = { marker: 'circular' };
46
+ circular.self = circular;
47
+
48
+ const mapValue = new Map<unknown, unknown>([
49
+ ['primitive', 1],
50
+ ['shared', shared],
51
+ [shared, circular],
52
+ ]);
53
+ mapValue.set('self', mapValue);
54
+
55
+ const setValue = new Set<unknown>(['set-item', shared, circular]);
56
+ setValue.add(setValue);
57
+
58
+ const rscProxy = createRscProxy(() => ({ slot: 'server' }), {
59
+ renderable: true,
60
+ stream: createStream(),
61
+ }) as React.ReactElement & Record<PropertyKey, unknown>;
62
+
63
+ const fixture: RoundTripFixture = {
64
+ bigintValue: 9007199254740993n,
65
+ booleanValue: true,
66
+ circular,
67
+ dateValue: new Date('2026-07-08T12:34:56.789Z'),
68
+ falseValue: false,
69
+ mapValue,
70
+ nested: {
71
+ array: [shared, [null, undefined, 7n]],
72
+ object: {
73
+ child: shared,
74
+ },
75
+ },
76
+ nullValue: null,
77
+ numberValue: 42,
78
+ rscProxy,
79
+ setValue,
80
+ sharedA: shared,
81
+ sharedB: shared,
82
+ stringValue: 'flight',
83
+ undefinedValue: undefined,
84
+ };
85
+
86
+ const serialized = serializeTanstackRscFlightValues(
87
+ fixture,
88
+ ) as RoundTripFixture;
89
+ const revived = (await reviveTanstackRscFlightValues(
90
+ serialized,
91
+ )) as RoundTripFixture;
92
+
93
+ expect(revived).not.toBe(fixture);
94
+ expect(revived.stringValue).toBe('flight');
95
+ expect(revived.numberValue).toBe(42);
96
+ expect(revived.booleanValue).toBe(true);
97
+ expect(revived.falseValue).toBe(false);
98
+ expect(revived.nullValue).toBeNull();
99
+ expect(Object.hasOwn(revived, 'undefinedValue')).toBe(true);
100
+ expect(revived.undefinedValue).toBeUndefined();
101
+ expect(revived.bigintValue).toBe(9007199254740993n);
102
+ expect(revived.dateValue).toBeInstanceOf(Date);
103
+ expect(revived.dateValue.toISOString()).toBe('2026-07-08T12:34:56.789Z');
104
+
105
+ expect(revived.nested.array[0]).toBe(revived.sharedA);
106
+ expect(revived.nested.object.child).toBe(revived.sharedA);
107
+ expect(revived.sharedB).toBe(revived.sharedA);
108
+
109
+ expect(revived.circular).not.toBe(circular);
110
+ expect(revived.circular.self).toBe(revived.circular);
111
+
112
+ expect(revived.mapValue).toBeInstanceOf(Map);
113
+ expect(revived.mapValue).not.toBe(mapValue);
114
+ expect(revived.mapValue.get('primitive')).toBe(1);
115
+ expect(revived.mapValue.get('shared')).toBe(revived.sharedA);
116
+ expect(revived.mapValue.get(revived.sharedA)).toBe(revived.circular);
117
+ expect(revived.mapValue.get('self')).toBe(revived.mapValue);
118
+
119
+ expect(revived.setValue).toBeInstanceOf(Set);
120
+ expect(revived.setValue).not.toBe(setValue);
121
+ expect(revived.setValue.has('set-item')).toBe(true);
122
+ expect(revived.setValue.has(revived.sharedA)).toBe(true);
123
+ expect(revived.setValue.has(revived.circular)).toBe(true);
124
+ expect(revived.setValue.has(revived.setValue)).toBe(true);
125
+
126
+ expect(serialized.rscProxy).not.toBe(rscProxy);
127
+ expect(serialized.rscProxy).toMatchObject({
128
+ __modernTanstackRsc: true,
129
+ kind: 'renderable',
130
+ });
131
+ expect(serialized.rscProxy.stream).toBeInstanceOf(ReadableStream);
132
+
133
+ expect(revived.rscProxy).not.toBe(rscProxy);
134
+ expect(isValidElement(revived.rscProxy)).toBe(true);
135
+ expect(revived.rscProxy[RENDERABLE_RSC]).toBe(true);
136
+ expect(revived.rscProxy[SERVER_COMPONENT_STREAM]).toBeDefined();
137
+ });
@@ -3,8 +3,10 @@ import {
3
3
  routerProviderRegistryHooks,
4
4
  } from '@modern-js/runtime/context';
5
5
  import * as tanstackHooks from '../../src/runtime/hooks';
6
+ import { applyRouterRuntimeState } from '../../src/runtime/lifecycle';
6
7
  import { tanstackRouterPlugin as browserPlugin } from '../../src/runtime/plugin';
7
8
  import { tanstackRouterPlugin as nodePlugin } from '../../src/runtime/plugin.node';
9
+ import { getTanstackRouterState } from '../../src/runtime/state';
8
10
 
9
11
  describe('tanstack router hooks single declaration source', () => {
10
12
  test('re-exports the canonical hook instances owned by @modern-js/runtime', () => {
@@ -24,3 +26,15 @@ describe('tanstack router hooks single declaration source', () => {
24
26
  expect(nodePlugin().registryHooks).toBe(routerProviderRegistryHooks);
25
27
  });
26
28
  });
29
+
30
+ describe('tanstack router runtime state accessor', () => {
31
+ test('returns undefined before a tanstack router instance is created', () => {
32
+ const context = {};
33
+
34
+ applyRouterRuntimeState(context as never, {
35
+ framework: 'tanstack',
36
+ });
37
+
38
+ expect(getTanstackRouterState(context)).toBeUndefined();
39
+ });
40
+ });
@@ -809,6 +809,9 @@ describe('tanstack route tree from RouteObject[]', () => {
809
809
  path: 'client',
810
810
  clientData: './client.data',
811
811
  data: './data',
812
+ hasAction: true,
813
+ hasClientLoader: true,
814
+ hasLoader: true,
812
815
  inValidSSRRoute: true,
813
816
  isClientComponent: true,
814
817
  },
@@ -830,7 +833,9 @@ describe('tanstack route tree from RouteObject[]', () => {
830
833
 
831
834
  expect(clientRoute.options.ssr).toBe(false);
832
835
  expect(clientRoute.options.staticData).toMatchObject({
836
+ modernRouteHasAction: true,
833
837
  modernRouteHasClientLoader: true,
838
+ modernRouteHasLoader: true,
834
839
  modernRouteIsClientComponent: true,
835
840
  });
836
841
  });
@@ -0,0 +1,23 @@
1
+ import { sanitizeSlotArgs } from '../../src/runtime/rsc/slotUsageSanitizer';
2
+
3
+ describe('RSC slot usage sanitizer', () => {
4
+ test('redacts callable slot args without serializing function source', () => {
5
+ function tokenFactory() {
6
+ return 'token-secret';
7
+ }
8
+
9
+ const args = sanitizeSlotArgs([
10
+ tokenFactory,
11
+ { nested: tokenFactory },
12
+ [tokenFactory],
13
+ ]);
14
+
15
+ expect(args).toEqual([
16
+ '[Function]',
17
+ { nested: '[Function]' },
18
+ ['[Function]'],
19
+ ]);
20
+ expect(JSON.stringify(args)).not.toContain('token-secret');
21
+ expect(JSON.stringify(args)).not.toContain('tokenFactory');
22
+ });
23
+ });