@octanejs/mcp-server 0.2.22 → 0.2.24
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/README.md +4 -2
- package/package.json +1 -1
- package/src/bridge.js +46 -6
- package/src/bridge.test.js +26 -0
- package/src/index.js +19 -0
- package/src/index.test.js +4 -0
package/README.md
CHANGED
|
@@ -163,9 +163,11 @@ one manifest suite by name (`js-framework`, `todomvc`, `weather-app`,
|
|
|
163
163
|
`controlled-form`, `external-store-fanout`, `external-store-integrations`,
|
|
164
164
|
`scheduler-responsiveness`, `suspense-recovery`, `event-delegation`,
|
|
165
165
|
`application-composition`, `scaling-curves`, `dev-form-diagnostics`,
|
|
166
|
-
`behavior-root-events`, `
|
|
166
|
+
`behavior-root-events`, `radix-collection-order`, `router-dispatch`,
|
|
167
|
+
`floating-tree-navigation`, `manifest-cache-invalidation`, `vite-client-assets`, `activity`,
|
|
168
|
+
`streaming-ssr`, `streaming-backpressure`,
|
|
167
169
|
`compiler-throughput`, `tsrx-component-graph`, `codegen-size`, `hook-memo`,
|
|
168
|
-
`template-call-memo`, `bundle-size`, `bundle-reachability`, `three-renderer`,
|
|
170
|
+
`transition-hooks`, `template-call-memo`, `tsrx-renderer-selection`, `bundle-size`, `bundle-reachability`, `three-renderer`,
|
|
169
171
|
`three-bundle-size`, …)
|
|
170
172
|
or every suite with `all`; `quick` selects the reduced-iteration smoke pass. The
|
|
171
173
|
suite list mirrors the runner manifest and `node benchmarks/bench.mjs --list`.
|
package/package.json
CHANGED
package/src/bridge.js
CHANGED
|
@@ -6,6 +6,7 @@ import { join, resolve } from 'node:path';
|
|
|
6
6
|
// expected union from the workspace manifests, so publishing a new binding
|
|
7
7
|
// without registering it in either catalog fails the mcp-server tests.
|
|
8
8
|
export const KNOWN_BINDINGS = {
|
|
9
|
+
'react-is': '@octanejs/octane-is',
|
|
9
10
|
'@gsap/react': '@octanejs/gsap',
|
|
10
11
|
animejs: '@octanejs/animejs',
|
|
11
12
|
'usehooks-ts': '@octanejs/usehooks-ts',
|
|
@@ -30,6 +31,7 @@ export const KNOWN_BINDINGS = {
|
|
|
30
31
|
'@tanstack/react-store': '@octanejs/tanstack-store',
|
|
31
32
|
'@xstate/react': '@octanejs/xstate',
|
|
32
33
|
'@xstate/store-react': '@octanejs/xstate-store',
|
|
34
|
+
'@xyflow/react': '@octanejs/xyflow',
|
|
33
35
|
'@tanstack/react-router-ssr-query': '@octanejs/tanstack-router-ssr-query',
|
|
34
36
|
'@tanstack/react-hotkeys': '@octanejs/tanstack-hotkeys',
|
|
35
37
|
'@tanstack/react-pacer': '@octanejs/tanstack-pacer',
|
|
@@ -50,6 +52,10 @@ export const KNOWN_BINDINGS = {
|
|
|
50
52
|
'@lexical/react': '@octanejs/lexical',
|
|
51
53
|
'@tiptap/react': '@octanejs/tiptap',
|
|
52
54
|
'react-pdf': '@octanejs/pdf',
|
|
55
|
+
'@portabletext/react': '@octanejs/portabletext',
|
|
56
|
+
'@sanity/icons': '@octanejs/sanity-icons',
|
|
57
|
+
'@sanity/react-loader': '@octanejs/sanity-loader',
|
|
58
|
+
'@sanity/logos': '@octanejs/sanity-logos',
|
|
53
59
|
'@monaco-editor/react': '@octanejs/monaco-editor',
|
|
54
60
|
'lucide-react': '@octanejs/lucide',
|
|
55
61
|
'@phosphor-icons/react': '@octanejs/phosphor-icons',
|
|
@@ -60,13 +66,17 @@ export const KNOWN_BINDINGS = {
|
|
|
60
66
|
'react-stately': '@octanejs/aria',
|
|
61
67
|
'radix-ui': '@octanejs/radix',
|
|
62
68
|
'react-hook-form': '@octanejs/hook-form',
|
|
69
|
+
'better-auth/react': '@octanejs/better-auth',
|
|
63
70
|
'@base-ui-components/react': '@octanejs/base-ui',
|
|
64
71
|
'@dnd-kit/react': '@octanejs/dnd-kit',
|
|
65
72
|
'embla-carousel-react': '@octanejs/embla-carousel',
|
|
66
73
|
'react-dropzone': '@octanejs/dropzone',
|
|
67
74
|
sonner: '@octanejs/sonner',
|
|
68
75
|
'react-error-boundary': '@octanejs/react-error-boundary',
|
|
76
|
+
'react-email': '@octanejs/email',
|
|
77
|
+
'@react-email/components': '@octanejs/email',
|
|
69
78
|
'react-resizable-panels': '@octanejs/resizable-panels',
|
|
79
|
+
'react-select': '@octanejs/select',
|
|
70
80
|
'react-transition-group': '@octanejs/transition-group',
|
|
71
81
|
'react-day-picker': '@octanejs/day-picker',
|
|
72
82
|
'input-otp': '@octanejs/input-otp',
|
|
@@ -81,6 +91,7 @@ export const KNOWN_BINDINGS = {
|
|
|
81
91
|
'react-content-loader': '@octanejs/content-loader',
|
|
82
92
|
'react-to-print': '@octanejs/to-print',
|
|
83
93
|
'react-calendar': '@octanejs/calendar',
|
|
94
|
+
'thinking-orbs': '@octanejs/thinking-orbs',
|
|
84
95
|
'@formkit/auto-animate': '@octanejs/auto-animate',
|
|
85
96
|
streamdown: '@octanejs/streamdown',
|
|
86
97
|
'react-syntax-highlighter': '@octanejs/syntax-highlighter',
|
|
@@ -172,6 +183,7 @@ export const KNOWN_BINDINGS = {
|
|
|
172
183
|
export const KNOWN_NATIVE_BINDINGS = new Set([
|
|
173
184
|
'@octanejs/devtools',
|
|
174
185
|
'@octanejs/electron',
|
|
186
|
+
'@octanejs/email-cli',
|
|
175
187
|
'@octanejs/tauri',
|
|
176
188
|
]);
|
|
177
189
|
|
|
@@ -199,6 +211,7 @@ export const KNOWN_VANILLA_CORES = {
|
|
|
199
211
|
'embla-carousel-react': 'embla-carousel',
|
|
200
212
|
'@xstate/react': 'xstate',
|
|
201
213
|
'@xstate/store-react': '@xstate/store',
|
|
214
|
+
'@xyflow/react': '@xyflow/system',
|
|
202
215
|
'react-redux': 'redux',
|
|
203
216
|
'@reduxjs/toolkit': 'redux',
|
|
204
217
|
'react-i18next': 'i18next',
|
|
@@ -462,6 +475,20 @@ export async function collectSourceFiles(root, out = [], depth = 0) {
|
|
|
462
475
|
|
|
463
476
|
export function scanSource(source) {
|
|
464
477
|
const apis = new Map();
|
|
478
|
+
const symbolExports = new Map();
|
|
479
|
+
// Introspection libraries export element-kind symbols, not components.
|
|
480
|
+
// Require the right-hand identifier to resolve to an actual Symbol.for
|
|
481
|
+
// declaration; ordinary component exports/render calls remain API uses.
|
|
482
|
+
const symbols = new Set(
|
|
483
|
+
[...source.matchAll(/\b([A-Za-z_$][\w$]*)\s*=\s*Symbol\.for\(\s*['"][^'"]+['"]\s*\)/g)].map(
|
|
484
|
+
(match) => match[1],
|
|
485
|
+
),
|
|
486
|
+
);
|
|
487
|
+
for (const match of source.matchAll(
|
|
488
|
+
/\bexports\.([A-Za-z_$][\w$]*)\s*=\s*([A-Za-z_$][\w$]*)\s*;/g,
|
|
489
|
+
)) {
|
|
490
|
+
if (symbols.has(match[2])) symbolExports.set(match[1], (symbolExports.get(match[1]) ?? 0) + 1);
|
|
491
|
+
}
|
|
465
492
|
for (const name of Object.keys(REACT_API_MAP)) {
|
|
466
493
|
if (name === 'onChange') continue;
|
|
467
494
|
const matches = source.match(new RegExp(`\\b${name}\\b`, 'g'));
|
|
@@ -481,13 +508,14 @@ export function scanSource(source) {
|
|
|
481
508
|
}
|
|
482
509
|
}
|
|
483
510
|
const classComponent = /\bextends\s+(React\.)?(Pure)?Component\b/.test(source);
|
|
484
|
-
return { apis, imports, classComponent };
|
|
511
|
+
return { apis, imports, classComponent, symbolExports };
|
|
485
512
|
}
|
|
486
513
|
|
|
487
514
|
export async function scanPath(root) {
|
|
488
515
|
const files = await collectSourceFiles(resolve(root));
|
|
489
516
|
const totals = new Map();
|
|
490
517
|
const imports = new Set();
|
|
518
|
+
const symbolExports = new Map();
|
|
491
519
|
let classComponents = false;
|
|
492
520
|
for (const file of files) {
|
|
493
521
|
let source;
|
|
@@ -501,14 +529,26 @@ export async function scanPath(root) {
|
|
|
501
529
|
totals.set(name, (totals.get(name) ?? 0) + count);
|
|
502
530
|
}
|
|
503
531
|
for (const spec of result.imports) imports.add(spec);
|
|
532
|
+
for (const [name, count] of result.symbolExports) {
|
|
533
|
+
symbolExports.set(name, (symbolExports.get(name) ?? 0) + count);
|
|
534
|
+
}
|
|
504
535
|
classComponents ||= result.classComponent;
|
|
505
536
|
}
|
|
506
|
-
return { filesScanned: files.length, totals, imports, classComponents };
|
|
537
|
+
return { filesScanned: files.length, totals, imports, classComponents, symbolExports };
|
|
507
538
|
}
|
|
508
539
|
|
|
509
|
-
function apiRows(totals) {
|
|
540
|
+
function apiRows(totals, symbolExports) {
|
|
510
541
|
return [...totals.entries()]
|
|
511
|
-
.map(([name, count]) =>
|
|
542
|
+
.map(([name, count]) =>
|
|
543
|
+
symbolExports.get(name) === count
|
|
544
|
+
? {
|
|
545
|
+
name,
|
|
546
|
+
count,
|
|
547
|
+
status: 'rewrite',
|
|
548
|
+
note: 'Exported element-kind marker: map to the Octane kind; predicates for unsupported kinds remain false. This does not require rendering that component.',
|
|
549
|
+
}
|
|
550
|
+
: { name, count, ...REACT_API_MAP[name] },
|
|
551
|
+
)
|
|
512
552
|
.sort((a, b) => b.count - a.count);
|
|
513
553
|
}
|
|
514
554
|
|
|
@@ -569,7 +609,7 @@ export async function bridgeReport({ packageName, path, projectRoot }) {
|
|
|
569
609
|
}
|
|
570
610
|
|
|
571
611
|
const scan = await scanPath(scanRoot);
|
|
572
|
-
const rows = apiRows(scan.totals);
|
|
612
|
+
const rows = apiRows(scan.totals, scan.symbolExports);
|
|
573
613
|
report.filesScanned = scan.filesScanned;
|
|
574
614
|
report.reactImports = [...scan.imports];
|
|
575
615
|
report.classComponents = scan.classComponents;
|
|
@@ -592,7 +632,7 @@ export function bridgeReportFromSource(source, { packageName } = {}) {
|
|
|
592
632
|
report.vanillaCore = detectVanillaCore(packageName, null);
|
|
593
633
|
}
|
|
594
634
|
const scan = scanSource(source);
|
|
595
|
-
const rows = apiRows(scan.apis);
|
|
635
|
+
const rows = apiRows(scan.apis, scan.symbolExports);
|
|
596
636
|
report.reactImports = [...scan.imports];
|
|
597
637
|
report.classComponents = scan.classComponent;
|
|
598
638
|
report.apis = rows;
|
package/src/bridge.test.js
CHANGED
|
@@ -41,6 +41,23 @@ describe('scanSource', () => {
|
|
|
41
41
|
expect(scanSource('class Memoish extends PureComponent {}').classComponent).toBe(true);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
it('classifies symbol-kind exports without requiring the corresponding renderer', () => {
|
|
45
|
+
const report = bridgeReportFromSource(`
|
|
46
|
+
var REACT_PROFILER_TYPE = Symbol.for('react.profiler');
|
|
47
|
+
exports.Profiler = REACT_PROFILER_TYPE;
|
|
48
|
+
exports.isProfiler = value => value.type === REACT_PROFILER_TYPE;
|
|
49
|
+
`);
|
|
50
|
+
expect(report.apis.find((row) => row.name === 'Profiler').status).toBe('rewrite');
|
|
51
|
+
expect(report.verdict).toBe('bridgeable-with-rewrites');
|
|
52
|
+
const renderer = bridgeReportFromSource(`
|
|
53
|
+
import { Profiler } from 'react';
|
|
54
|
+
var REACT_PROFILER_TYPE = Symbol.for('react.profiler');
|
|
55
|
+
exports.Profiler = REACT_PROFILER_TYPE;
|
|
56
|
+
export const View = () => <Profiler />;
|
|
57
|
+
`);
|
|
58
|
+
expect(renderer.verdict).toBe('needs-rework');
|
|
59
|
+
});
|
|
60
|
+
|
|
44
61
|
it('targets only React-style text-host onChange wiring', () => {
|
|
45
62
|
const source = `
|
|
46
63
|
function Demo(props) {
|
|
@@ -298,6 +315,10 @@ describe('KNOWN_BINDINGS', () => {
|
|
|
298
315
|
expect(KNOWN_BINDINGS['react-textarea-autosize']).toBe('@octanejs/textarea-autosize');
|
|
299
316
|
});
|
|
300
317
|
|
|
318
|
+
it('maps react-select to the exact Octane binding', () => {
|
|
319
|
+
expect(KNOWN_BINDINGS['react-select']).toBe('@octanejs/select');
|
|
320
|
+
});
|
|
321
|
+
|
|
301
322
|
it('maps react-window to its exact official Octane binding', () => {
|
|
302
323
|
expect(KNOWN_BINDINGS['react-window']).toBe('@octanejs/window');
|
|
303
324
|
});
|
|
@@ -307,6 +328,11 @@ describe('KNOWN_BINDINGS', () => {
|
|
|
307
328
|
expect(KNOWN_VANILLA_CORES['@tanstack/react-db']).toBe('@tanstack/db');
|
|
308
329
|
});
|
|
309
330
|
|
|
331
|
+
it('maps XYFlow React to the Octane binding and framework-neutral system core', () => {
|
|
332
|
+
expect(KNOWN_BINDINGS['@xyflow/react']).toBe('@octanejs/xyflow');
|
|
333
|
+
expect(KNOWN_VANILLA_CORES['@xyflow/react']).toBe('@xyflow/system');
|
|
334
|
+
});
|
|
335
|
+
|
|
310
336
|
it('maps Streamdown and every official plugin package to the consolidated binding', () => {
|
|
311
337
|
const upstreamPackages = [
|
|
312
338
|
'streamdown',
|
package/src/index.js
CHANGED
|
@@ -55,6 +55,7 @@ export const BENCHMARK_SUITES = [
|
|
|
55
55
|
'recursive-context',
|
|
56
56
|
'spa-navigation',
|
|
57
57
|
'scoped-signals',
|
|
58
|
+
'scoped-signals-trace',
|
|
58
59
|
'scoped-native-reads',
|
|
59
60
|
'signal-favoring',
|
|
60
61
|
'news',
|
|
@@ -71,9 +72,16 @@ export const BENCHMARK_SUITES = [
|
|
|
71
72
|
'scaling-curves',
|
|
72
73
|
'dev-form-diagnostics',
|
|
73
74
|
'scheduler-depth',
|
|
75
|
+
'hydration-range-compaction',
|
|
76
|
+
'deferred-hydration-boundaries',
|
|
77
|
+
'hydration-render-phase-queue',
|
|
74
78
|
'behavior-root-events',
|
|
79
|
+
'radix-collection-order',
|
|
75
80
|
'router-dispatch',
|
|
81
|
+
'visx-categorical-scale',
|
|
82
|
+
'rspack-css-graph',
|
|
76
83
|
'floating-tree-navigation',
|
|
84
|
+
'ink-cursor-update',
|
|
77
85
|
'manifest-cache-invalidation',
|
|
78
86
|
'vite-client-assets',
|
|
79
87
|
'store-selector-fanout',
|
|
@@ -95,7 +103,10 @@ export const BENCHMARK_SUITES = [
|
|
|
95
103
|
'async-waterfall',
|
|
96
104
|
'async-composition',
|
|
97
105
|
'lynx-list',
|
|
106
|
+
'lynx-handle-retirement',
|
|
98
107
|
'universal-leaf-update',
|
|
108
|
+
'universal-object-teardown',
|
|
109
|
+
'universal-template-events',
|
|
99
110
|
'universal-external-store',
|
|
100
111
|
'lynx-render',
|
|
101
112
|
'lynx-table',
|
|
@@ -103,13 +114,21 @@ export const BENCHMARK_SUITES = [
|
|
|
103
114
|
'lynx-bundle-size',
|
|
104
115
|
'codegen-size',
|
|
105
116
|
'hook-memo',
|
|
117
|
+
'transition-hooks',
|
|
106
118
|
'template-call-memo',
|
|
107
119
|
'compiler-throughput',
|
|
108
120
|
'tsrx-component-graph',
|
|
121
|
+
'tsrx-void-memo-aliases',
|
|
122
|
+
'tsrx-hydrate-module-slicing',
|
|
123
|
+
'tsrx-stable-hookful-propagation',
|
|
124
|
+
'tsrx-renderer-validation-ranges',
|
|
125
|
+
'tsrx-local-component-name-catalog',
|
|
109
126
|
'tsrx-jsx-return-branches',
|
|
110
127
|
'text-type-roots',
|
|
111
128
|
'tsrx-nesting-diagnostics',
|
|
129
|
+
'tsrx-renderer-selection',
|
|
112
130
|
'tsrx-native-change-analysis',
|
|
131
|
+
'tsrx-vite-preflight-parsing',
|
|
113
132
|
'bundle-size',
|
|
114
133
|
'bundle-reachability',
|
|
115
134
|
'three-renderer',
|
package/src/index.test.js
CHANGED
|
@@ -76,6 +76,10 @@ describe('@octanejs/mcp-server helpers', () => {
|
|
|
76
76
|
.split('\n')
|
|
77
77
|
.map((line) => line.trim())
|
|
78
78
|
.filter((line) => line && line !== 'Available suites:');
|
|
79
|
+
expect(suites).toContain('tsrx-hydrate-module-slicing');
|
|
80
|
+
expect(suites).toContain('tsrx-stable-hookful-propagation');
|
|
81
|
+
expect(suites).toContain('tsrx-renderer-validation-ranges');
|
|
82
|
+
expect(suites).toContain('tsrx-local-component-name-catalog');
|
|
79
83
|
expect(suites).toEqual(BENCHMARK_SUITES);
|
|
80
84
|
|
|
81
85
|
// The public MCP schema must accept every runner suite, not just keep
|