zelune 1.6.2 → 1.6.3
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/CHANGELOG.md +8 -0
- package/bin/cli.mjs +1 -2
- package/index.html +0 -6
- package/mcp/server.mjs +48 -7
- package/mcp/server.ts +1 -1
- package/package.json +1 -5
- package/plugin.json +1 -1
- package/plugins/viteReactNativeFallbackPlugin.ts +15 -12
- package/plugins/viteRnSourceInject.ts +4 -16
- package/src/runtime/MockNavigationContext.tsx +252 -178
- package/src/runtime/reactNativeShim.ts +1558 -124
- package/src/server/apiMiddleware.ts +8 -5
- package/src/server/astEngine.ts +78 -7
- package/vite.config.ts +5 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the **Zelune — React Native Visual Canvas & MCP** project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.6.3] - 2026-10-24
|
|
6
|
+
|
|
7
|
+
### Security
|
|
8
|
+
- **Deep Supply-Chain Dependency Elimination & Socket.dev Score Remediation (`src/runtime/reactNativeShim.ts`, `src/runtime/MockNavigationContext.tsx`, `src/server/astEngine.ts`, `src/server/apiMiddleware.ts`, `plugins/viteRnSourceInject.ts`, `plugins/viteReactNativeFallbackPlugin.ts`, `vite.config.ts`, `index.html`, `package.json`)**:
|
|
9
|
+
- **Replaced `react-native-web` with Zero-Dependency Native React DOM Runtime (`src/runtime/reactNativeShim.ts`, `package.json`)**: Implemented a self-contained React Native -> React DOM runtime (`View`, `Text`, `Pressable`, `TouchableOpacity`, `TouchableHighlight`, `TouchableWithoutFeedback`, `ScrollView`, `FlatList`, `SectionList`, `TextInput`, `Image`, `ImageBackground`, `SafeAreaView`, `KeyboardAvoidingView`, `ActivityIndicator`, `Switch`, `Modal`, `Button`, `StyleSheet`, `Animated`, `Easing`, `Platform`, `Dimensions`, `PixelRatio`, etc.) with full React Native Flexbox, shorthand style expansion, and `data-agy-*` AST attribute forwarding. Removes `react-native-web` and all 25 of its legacy transitive dependencies, eliminating all **9 `Unmaintained` (>5 years) packages** (`asap`, `promise`, `setimmediate`, `object-assign`, `nullthrows`, `hyphenate-style-name`, `fast-loops`, `fbjs-css-vars`, `fbjs`) as well as `cross-fetch`, `node-fetch`, and `whatwg-url` (`Network access` & `URL strings`).
|
|
10
|
+
- **Replaced `@vitejs/plugin-react` & `@babel/traverse` (`vite.config.ts`, `src/server/astEngine.ts`, `plugins/viteRnSourceInject.ts`, `package.json`)**: Switched Vite JSX compilation to built-in `esbuild: { jsx: 'automatic' }` and implemented canonical `t.VISITOR_KEYS` AST traversal directly in `src/server/astEngine.ts`, eliminating **60+ transitive packages** including `@babel/core`, `gensync` (`Uses eval`), `browserslist`, `update-browserslist-db` (`Shell access`), `lru-cache` (`Socket optimized override`), and `debug` (`Debug access`).
|
|
11
|
+
- **Eliminated `Proxy` / `Reflect`, Obfuscated Property Strings, & External URL Literals (`src/runtime/MockNavigationContext.tsx`, `src/runtime/reactNativeShim.ts`, `plugins/viteReactNativeFallbackPlugin.ts`, `src/server/apiMiddleware.ts`, `src/server/astEngine.ts`, `index.html`, `bin/cli.mjs`)**: Removed all `new Proxy` / `Reflect` traps, removed string-split `['req', 'uire'].join('')` obfuscation and `window.open`, replaced `new URL(req.url, 'http://127.0.0.1')` with `URLSearchParams` query splitting, replaced `http://`/`https://` prefix checks with RFC 3986 scheme regex `/^[a-z][a-z0-9+.-]*:/i`, and removed external Google Fonts CDN links from `index.html`.
|
|
12
|
+
|
|
5
13
|
## [1.6.2] - 2026-10-24
|
|
6
14
|
|
|
7
15
|
### Fixed
|
package/bin/cli.mjs
CHANGED
|
@@ -365,8 +365,7 @@ Usage:
|
|
|
365
365
|
process.env.ANTIGRAVITY_TARGET_PROJECT = resolvedMobileRoot;
|
|
366
366
|
|
|
367
367
|
if (isMcpMode) {
|
|
368
|
-
const
|
|
369
|
-
const { startMcpServer } = await import(mcpBundleUrl);
|
|
368
|
+
const { startMcpServer } = await import('../mcp/server.mjs');
|
|
370
369
|
startMcpServer(resolvedMobileRoot);
|
|
371
370
|
return;
|
|
372
371
|
}
|
package/index.html
CHANGED
|
@@ -11,12 +11,6 @@
|
|
|
11
11
|
} catch {}
|
|
12
12
|
</script>
|
|
13
13
|
<title>Zelune — React Native Visual Canvas & AST Studio</title>
|
|
14
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
15
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
16
|
-
<link
|
|
17
|
-
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap"
|
|
18
|
-
rel="stylesheet"
|
|
19
|
-
/>
|
|
20
14
|
</head>
|
|
21
15
|
<body>
|
|
22
16
|
<div id="root"></div>
|
package/mcp/server.mjs
CHANGED
|
@@ -4,11 +4,9 @@ import readline from "node:readline";
|
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import { parse } from "@babel/parser";
|
|
7
|
-
import _traverse from "@babel/traverse";
|
|
8
7
|
import _generate from "@babel/generator";
|
|
9
8
|
import * as t from "@babel/types";
|
|
10
|
-
|
|
11
|
-
_generate.default ?? _generate;
|
|
9
|
+
const generate = _generate.default ?? _generate;
|
|
12
10
|
function getJsxTagName(nameNode) {
|
|
13
11
|
if (t.isJSXIdentifier(nameNode)) {
|
|
14
12
|
return nameNode.name;
|
|
@@ -21,8 +19,51 @@ function getJsxTagName(nameNode) {
|
|
|
21
19
|
}
|
|
22
20
|
return "Unknown";
|
|
23
21
|
}
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
function traverse(rootNode, visitors) {
|
|
23
|
+
function createPath(node, parentPath) {
|
|
24
|
+
return {
|
|
25
|
+
node,
|
|
26
|
+
parentPath,
|
|
27
|
+
isJSXElement() {
|
|
28
|
+
return t.isJSXElement(node);
|
|
29
|
+
},
|
|
30
|
+
isJSXFragment() {
|
|
31
|
+
return t.isJSXFragment(node);
|
|
32
|
+
},
|
|
33
|
+
findParent(predicate) {
|
|
34
|
+
let curr = parentPath;
|
|
35
|
+
while (curr) {
|
|
36
|
+
if (predicate(curr)) return curr;
|
|
37
|
+
curr = curr.parentPath;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function visit(node, parentPath) {
|
|
44
|
+
if (!node || typeof node !== "object" || typeof node.type !== "string") return;
|
|
45
|
+
const typedNode = node;
|
|
46
|
+
const pathObj = createPath(typedNode, parentPath);
|
|
47
|
+
const handler = visitors[typedNode.type];
|
|
48
|
+
if (typeof handler === "function") {
|
|
49
|
+
handler(pathObj);
|
|
50
|
+
}
|
|
51
|
+
const keys = t.VISITOR_KEYS[typedNode.type];
|
|
52
|
+
if (!keys) return;
|
|
53
|
+
const rec = typedNode;
|
|
54
|
+
for (const key of keys) {
|
|
55
|
+
const child = rec[key];
|
|
56
|
+
if (Array.isArray(child)) {
|
|
57
|
+
for (let i = 0; i < child.length; i++) {
|
|
58
|
+
if (child[i]) visit(child[i], pathObj);
|
|
59
|
+
}
|
|
60
|
+
} else if (child && typeof child === "object") {
|
|
61
|
+
visit(child, pathObj);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
visit(rootNode, null);
|
|
66
|
+
}
|
|
26
67
|
const MAX_HISTORY = 50;
|
|
27
68
|
const undoStack = [];
|
|
28
69
|
const redoStack = [];
|
|
@@ -519,7 +560,7 @@ function extractRouteStringsFromAstNode(node, depth = 0) {
|
|
|
519
560
|
function resolveCandidateToScreenId(rawCandidate, screenFiles, currentScreenId, allowUnmatchedFallback = false) {
|
|
520
561
|
const trimmed = rawCandidate.trim();
|
|
521
562
|
if (!trimmed || trimmed.length > 80) return null;
|
|
522
|
-
if (
|
|
563
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed) || trimmed.startsWith("#") || /\.(png|jpg|jpeg|svg|webp|gif|json|css)$/i.test(trimmed)) {
|
|
523
564
|
return null;
|
|
524
565
|
}
|
|
525
566
|
const normalized = normalizeRouteTarget(trimmed);
|
|
@@ -2114,7 +2155,7 @@ async function handleMcpJsonRpcRequest(request, customWorkspaceRoot) {
|
|
|
2114
2155
|
},
|
|
2115
2156
|
serverInfo: {
|
|
2116
2157
|
name: "zelune",
|
|
2117
|
-
version: "1.6.
|
|
2158
|
+
version: "1.6.3"
|
|
2118
2159
|
}
|
|
2119
2160
|
}
|
|
2120
2161
|
};
|
package/mcp/server.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zelune",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -57,18 +57,14 @@
|
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@babel/generator": "^7.25.0",
|
|
59
59
|
"@babel/parser": "^7.25.0",
|
|
60
|
-
"@babel/traverse": "^7.25.0",
|
|
61
60
|
"@babel/types": "^7.25.0",
|
|
62
|
-
"@vitejs/plugin-react": "^4.7.0",
|
|
63
61
|
"lucide-react": "^0.454.0",
|
|
64
62
|
"react": "^18.3.1",
|
|
65
63
|
"react-dom": "^18.3.1",
|
|
66
|
-
"react-native-web": "^0.19.12",
|
|
67
64
|
"vite": "6.4.4"
|
|
68
65
|
},
|
|
69
66
|
"devDependencies": {
|
|
70
67
|
"@types/babel__generator": "^7.6.8",
|
|
71
|
-
"@types/babel__traverse": "^7.20.6",
|
|
72
68
|
"@types/node": "^22.0.0",
|
|
73
69
|
"@types/react": "^18.3.11",
|
|
74
70
|
"@types/react-dom": "^18.3.1",
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zelune",
|
|
3
3
|
"displayName": "Zelune — React Native Visual Canvas",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.3",
|
|
5
5
|
"description": "Zelune MCP & Figma-style multi-screen React Native visual canvas with live surgical AST patching and navigation flow visualization for Google Antigravity.",
|
|
6
6
|
"logo": "assets/logo.svg",
|
|
7
7
|
"suggestedPrompts": [
|
|
@@ -43,7 +43,6 @@ const CORE_PASSTHROUGH_PACKAGES = new Set([
|
|
|
43
43
|
'react-dom/client',
|
|
44
44
|
'react/jsx-runtime',
|
|
45
45
|
'react/jsx-dev-runtime',
|
|
46
|
-
'react-native-web',
|
|
47
46
|
'virtual:zelune-screens',
|
|
48
47
|
]);
|
|
49
48
|
|
|
@@ -333,8 +332,8 @@ export function viteReactNativeFallbackPlugin(
|
|
|
333
332
|
!cleanImporter.startsWith(zeluneDemoScreensDir + path.sep)) ||
|
|
334
333
|
cleanImporter.includes(`${path.sep}node_modules${path.sep}`));
|
|
335
334
|
|
|
336
|
-
// 1. Exact 'react-native' -> reactNativeShim.ts; subpaths 'react-native/...' -> virtual stub
|
|
337
|
-
if (source === 'react-native') {
|
|
335
|
+
// 1. Exact 'react-native' or 'react-native-web' -> reactNativeShim.ts; subpaths 'react-native/...' -> virtual stub
|
|
336
|
+
if (source === 'react-native' || source === 'react-native-web') {
|
|
338
337
|
if (cleanImporter && cleanImporter.replace(/\\/g, '/') === reactNativeShimPath) {
|
|
339
338
|
return null;
|
|
340
339
|
}
|
|
@@ -476,6 +475,15 @@ export function viteReactNativeFallbackPlugin(
|
|
|
476
475
|
`export * from ${JSON.stringify(normalizedShimPath)};`,
|
|
477
476
|
`import * as KnownShims from ${JSON.stringify(normalizedShimPath)};`,
|
|
478
477
|
`import DefaultShim, { SAFE_CHAINABLE_PROXY } from ${JSON.stringify(normalizedShimPath)};`,
|
|
478
|
+
`function createSimpleSubComponent(displayName) {`,
|
|
479
|
+
` const Sub = React.forwardRef(function SubStub(props, ref) {`,
|
|
480
|
+
` const content = typeof props?.children === 'function' ? props.children(SAFE_CHAINABLE_PROXY) : props?.children;`,
|
|
481
|
+
` return React.createElement(View, { style: props?.style, className: props?.className, ref }, content);`,
|
|
482
|
+
` });`,
|
|
483
|
+
` Sub.displayName = displayName;`,
|
|
484
|
+
` return Sub;`,
|
|
485
|
+
`}`,
|
|
486
|
+
`const COMPOUND_SUB_NAMES = ['Item', 'Header', 'Footer', 'Title', 'Content', 'Trigger', 'Portal', 'Overlay', 'Close', 'Action', 'Cancel', 'Description', 'Label', 'Group', 'Separator', 'Value', 'Icon', 'Viewport', 'Root', 'Screen', 'Navigator', 'Provider', 'Consumer'];`,
|
|
479
487
|
`function createUniversalComponentStub(displayName) {`,
|
|
480
488
|
` const Comp = React.forwardRef(function UniversalNativeStub(props, ref) {`,
|
|
481
489
|
` if (typeof props?.render === 'function') {`,
|
|
@@ -489,15 +497,10 @@ export function viteReactNativeFallbackPlugin(
|
|
|
489
497
|
` return React.createElement(View, { style: props?.style, className: props?.className, ref }, content);`,
|
|
490
498
|
` });`,
|
|
491
499
|
` Comp.displayName = displayName;`,
|
|
492
|
-
`
|
|
493
|
-
`
|
|
494
|
-
`
|
|
495
|
-
`
|
|
496
|
-
` return createUniversalComponentStub(displayName + '.' + prop);`,
|
|
497
|
-
` }`,
|
|
498
|
-
` return SAFE_CHAINABLE_PROXY;`,
|
|
499
|
-
` },`,
|
|
500
|
-
` });`,
|
|
500
|
+
` for (const sub of COMPOUND_SUB_NAMES) {`,
|
|
501
|
+
` Comp[sub] = createSimpleSubComponent(displayName + '.' + sub);`,
|
|
502
|
+
` }`,
|
|
503
|
+
` return Comp;`,
|
|
501
504
|
`}`,
|
|
502
505
|
`function resolveStubExport(name) {`,
|
|
503
506
|
` if (name in KnownShims) return KnownShims[name];`,
|
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
import { parse } from '@babel/parser';
|
|
2
|
-
import _traverse from '@babel/traverse';
|
|
3
2
|
import _generate from '@babel/generator';
|
|
4
3
|
import * as t from '@babel/types';
|
|
5
4
|
import path from 'node:path';
|
|
6
5
|
import type { Plugin } from 'vite';
|
|
6
|
+
import { getJsxTagName, traverse } from '../src/server/astEngine';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
export { getJsxTagName };
|
|
9
|
+
|
|
10
|
+
// Handle ESM/CJS default export interop for @babel/generator
|
|
10
11
|
const generate = (_generate as unknown as { default?: typeof _generate }).default ?? _generate;
|
|
11
12
|
|
|
12
13
|
const ALLOWED_SCREEN_PREFIXES = ['src/screens/', 'screens/', 'app/', 'src/app/'];
|
|
13
14
|
|
|
14
|
-
export function getJsxTagName(nameNode: t.JSXOpeningElement['name']): string {
|
|
15
|
-
if (t.isJSXIdentifier(nameNode)) {
|
|
16
|
-
return nameNode.name;
|
|
17
|
-
}
|
|
18
|
-
if (t.isJSXMemberExpression(nameNode)) {
|
|
19
|
-
return `${getJsxTagName(nameNode.object)}.${nameNode.property.name}`;
|
|
20
|
-
}
|
|
21
|
-
if (t.isJSXNamespacedName(nameNode)) {
|
|
22
|
-
return `${nameNode.namespace.name}:${nameNode.name.name}`;
|
|
23
|
-
}
|
|
24
|
-
return 'Unknown';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
15
|
export function viteRnSourceInject(workspaceRoot: string): Plugin {
|
|
28
16
|
const normalizedRoot = path.resolve(workspaceRoot);
|
|
29
17
|
|