@nexus_js/cli 0.6.0
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/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/add.d.ts +36 -0
- package/dist/add.d.ts.map +1 -0
- package/dist/add.js +342 -0
- package/dist/add.js.map +1 -0
- package/dist/analyzer.d.ts +70 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +247 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/audit.d.ts +35 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +383 -0
- package/dist/audit.js.map +1 -0
- package/dist/bin.d.ts +6 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +367 -0
- package/dist/bin.js.map +1 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -0
- package/dist/create.d.ts +7 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +256 -0
- package/dist/create.js.map +1 -0
- package/dist/fix.d.ts +22 -0
- package/dist/fix.d.ts.map +1 -0
- package/dist/fix.js +199 -0
- package/dist/fix.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/studio.d.ts +136 -0
- package/dist/studio.d.ts.map +1 -0
- package/dist/studio.js +721 -0
- package/dist/studio.js.map +1 -0
- package/package.json +63 -0
package/dist/create.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* create-nexus — scaffolding CLI for new Nexus projects.
|
|
4
|
+
* Usage: npx create-nexus my-app
|
|
5
|
+
*/
|
|
6
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
7
|
+
import { join, resolve } from 'node:path';
|
|
8
|
+
const CYAN = '\x1b[36m';
|
|
9
|
+
const GREEN = '\x1b[32m';
|
|
10
|
+
const YELLOW = '\x1b[33m';
|
|
11
|
+
const RESET = '\x1b[0m';
|
|
12
|
+
const BOLD = '\x1b[1m';
|
|
13
|
+
const DIM = '\x1b[2m';
|
|
14
|
+
const BANNER = `
|
|
15
|
+
${CYAN}◆ create-nexus${RESET}
|
|
16
|
+
|
|
17
|
+
The Definitive Full-Stack Framework
|
|
18
|
+
Islands × Runes × Server Actions
|
|
19
|
+
`;
|
|
20
|
+
async function main() {
|
|
21
|
+
console.log(BANNER);
|
|
22
|
+
const projectName = process.argv[2] ?? 'my-nexus-app';
|
|
23
|
+
const targetDir = resolve(process.cwd(), projectName);
|
|
24
|
+
console.log(` Creating ${BOLD}${projectName}${RESET}...\n`);
|
|
25
|
+
// Create directory structure
|
|
26
|
+
const dirs = [
|
|
27
|
+
'src/routes',
|
|
28
|
+
'src/routes/blog/[slug]',
|
|
29
|
+
'src/routes/api/users',
|
|
30
|
+
'src/components',
|
|
31
|
+
'src/islands',
|
|
32
|
+
'src/lib',
|
|
33
|
+
'public',
|
|
34
|
+
];
|
|
35
|
+
for (const dir of dirs) {
|
|
36
|
+
await mkdir(join(targetDir, dir), { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
// Write project files
|
|
39
|
+
await writeProjectFiles(targetDir, projectName);
|
|
40
|
+
console.log(` ${GREEN}✓${RESET} Project created at ${BOLD}${projectName}/${RESET}\n`);
|
|
41
|
+
console.log(` Next steps:\n`);
|
|
42
|
+
console.log(` ${DIM}cd${RESET} ${projectName}`);
|
|
43
|
+
console.log(` ${DIM}pnpm install${RESET}`);
|
|
44
|
+
console.log(` ${DIM}pnpm dev${RESET}\n`);
|
|
45
|
+
console.log(` ${CYAN}◆${RESET} Docs: ${BOLD}https://nexusjs.dev${RESET}\n`);
|
|
46
|
+
}
|
|
47
|
+
async function writeProjectFiles(dir, name) {
|
|
48
|
+
const files = {
|
|
49
|
+
'package.json': JSON.stringify({
|
|
50
|
+
name,
|
|
51
|
+
version: '0.1.0',
|
|
52
|
+
private: true,
|
|
53
|
+
type: 'module',
|
|
54
|
+
scripts: {
|
|
55
|
+
dev: 'nexus dev',
|
|
56
|
+
build: 'nexus build',
|
|
57
|
+
start: 'nexus start',
|
|
58
|
+
check: 'nexus check',
|
|
59
|
+
},
|
|
60
|
+
dependencies: {
|
|
61
|
+
'@nexus_js/runtime': 'workspace:*',
|
|
62
|
+
},
|
|
63
|
+
devDependencies: {
|
|
64
|
+
'@nexus_js/cli': 'workspace:*',
|
|
65
|
+
'@nexus_js/compiler': 'workspace:*',
|
|
66
|
+
typescript: '^5.5.0',
|
|
67
|
+
},
|
|
68
|
+
}, null, 2),
|
|
69
|
+
'tsconfig.json': JSON.stringify({
|
|
70
|
+
extends: '../../tsconfig.base.json',
|
|
71
|
+
compilerOptions: { paths: { '$lib/*': ['./src/lib/*'] } },
|
|
72
|
+
include: ['src/**/*'],
|
|
73
|
+
}, null, 2),
|
|
74
|
+
'nexus.config.ts': `import type { NexusConfig } from '@nexus_js/cli';
|
|
75
|
+
|
|
76
|
+
export default {
|
|
77
|
+
// Islands hydration strategy defaults
|
|
78
|
+
defaultHydration: 'client:visible',
|
|
79
|
+
|
|
80
|
+
// Image optimization
|
|
81
|
+
images: {
|
|
82
|
+
formats: ['avif', 'webp'],
|
|
83
|
+
sizes: [640, 1280, 1920],
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// Server options
|
|
87
|
+
server: {
|
|
88
|
+
port: 3000,
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// Build output
|
|
92
|
+
build: {
|
|
93
|
+
outDir: '.nexus/output',
|
|
94
|
+
sourcemap: false,
|
|
95
|
+
},
|
|
96
|
+
} satisfies NexusConfig;
|
|
97
|
+
`,
|
|
98
|
+
'src/routes/+layout.nx': `---
|
|
99
|
+
// Root layout — server-only
|
|
100
|
+
const appName = "My Nexus App";
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
<html lang="en">
|
|
104
|
+
<head>
|
|
105
|
+
<meta charset="UTF-8">
|
|
106
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
107
|
+
<title>{appName}</title>
|
|
108
|
+
</head>
|
|
109
|
+
<body>
|
|
110
|
+
<nav>
|
|
111
|
+
<a href="/">Home</a>
|
|
112
|
+
<a href="/blog">Blog</a>
|
|
113
|
+
</nav>
|
|
114
|
+
<main>
|
|
115
|
+
<!--nexus:slot-->
|
|
116
|
+
</main>
|
|
117
|
+
</body>
|
|
118
|
+
</html>
|
|
119
|
+
`,
|
|
120
|
+
'src/routes/+page.nx': `---
|
|
121
|
+
// Index page — runs on server only
|
|
122
|
+
const greeting = "Welcome to Nexus";
|
|
123
|
+
const features = [
|
|
124
|
+
{ icon: "🏝️", title: "Islands Architecture", desc: "Zero JS by default" },
|
|
125
|
+
{ icon: "⚡", title: "Svelte 5 Runes", desc: "Fine-grained reactivity" },
|
|
126
|
+
{ icon: "🔧", title: "Server Actions", desc: "Type-safe mutations" },
|
|
127
|
+
];
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
<script>
|
|
131
|
+
// Client island — only this code reaches the browser
|
|
132
|
+
let count = $state(0);
|
|
133
|
+
let doubled = $derived(count * 2);
|
|
134
|
+
</script>
|
|
135
|
+
|
|
136
|
+
<section class="hero">
|
|
137
|
+
<h1>{greeting}</h1>
|
|
138
|
+
<p>Islands × Runes × Server Actions</p>
|
|
139
|
+
</section>
|
|
140
|
+
|
|
141
|
+
<section class="features">
|
|
142
|
+
{#each features as f}
|
|
143
|
+
<div class="card">
|
|
144
|
+
<span>{f.icon}</span>
|
|
145
|
+
<h3>{f.title}</h3>
|
|
146
|
+
<p>{f.desc}</p>
|
|
147
|
+
</div>
|
|
148
|
+
{/each}
|
|
149
|
+
</section>
|
|
150
|
+
|
|
151
|
+
<div class="counter" client:visible>
|
|
152
|
+
<button onclick={() => count++}>
|
|
153
|
+
Clicked {count} times (×2 = {doubled})
|
|
154
|
+
</button>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<style>
|
|
158
|
+
.hero { text-align: center; padding: 4rem 2rem; }
|
|
159
|
+
.features { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
|
|
160
|
+
.card { padding: 1.5rem; border: 1px solid #eee; border-radius: 8px; }
|
|
161
|
+
.counter { text-align: center; margin-top: 2rem; }
|
|
162
|
+
button { padding: 0.75rem 2rem; font-size: 1rem; cursor: pointer; }
|
|
163
|
+
</style>
|
|
164
|
+
`,
|
|
165
|
+
'src/routes/blog/+page.nx': `---
|
|
166
|
+
// Blog listing page
|
|
167
|
+
const posts = [
|
|
168
|
+
{ slug: "hello-nexus", title: "Hello Nexus", date: "2026-04-03" },
|
|
169
|
+
{ slug: "islands-arch", title: "Islands Architecture Deep Dive", date: "2026-04-01" },
|
|
170
|
+
];
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
<h1>Blog</h1>
|
|
174
|
+
<ul>
|
|
175
|
+
{#each posts as post}
|
|
176
|
+
<li>
|
|
177
|
+
<a href="/blog/{post.slug}">{post.title}</a>
|
|
178
|
+
<time>{post.date}</time>
|
|
179
|
+
</li>
|
|
180
|
+
{/each}
|
|
181
|
+
</ul>
|
|
182
|
+
`,
|
|
183
|
+
'src/routes/blog/[slug]/+page.nx': `---
|
|
184
|
+
// Dynamic blog post page
|
|
185
|
+
// params.slug is injected by the router
|
|
186
|
+
const { slug } = ctx.params;
|
|
187
|
+
const post = { title: \`Post: \${slug}\`, content: "Post content here..." };
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
<article>
|
|
191
|
+
<h1>{post.title}</h1>
|
|
192
|
+
<p>{post.content}</p>
|
|
193
|
+
<a href="/blog">← Back to Blog</a>
|
|
194
|
+
</article>
|
|
195
|
+
`,
|
|
196
|
+
'src/routes/api/users/+server.nx': `---
|
|
197
|
+
// API route — returns JSON
|
|
198
|
+
// GET /api/users
|
|
199
|
+
const users = [
|
|
200
|
+
{ id: 1, name: "Alice" },
|
|
201
|
+
{ id: 2, name: "Bob" },
|
|
202
|
+
];
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
// Export GET handler
|
|
206
|
+
export async function GET(ctx) {
|
|
207
|
+
return Response.json({ users });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// POST /api/users
|
|
211
|
+
export async function POST(ctx) {
|
|
212
|
+
const body = await ctx.request.json();
|
|
213
|
+
// Create user logic...
|
|
214
|
+
return Response.json({ created: true }, { status: 201 });
|
|
215
|
+
}
|
|
216
|
+
`,
|
|
217
|
+
'src/lib/db.ts': `// Database client placeholder
|
|
218
|
+
// Replace with your preferred ORM (Prisma, Drizzle, etc.)
|
|
219
|
+
|
|
220
|
+
export const db = {
|
|
221
|
+
user: {
|
|
222
|
+
async findFirst() {
|
|
223
|
+
return { id: 1, name: 'Demo User', email: 'demo@nexusjs.dev' };
|
|
224
|
+
},
|
|
225
|
+
async findMany() {
|
|
226
|
+
return [{ id: 1, name: 'Demo User', email: 'demo@nexusjs.dev' }];
|
|
227
|
+
},
|
|
228
|
+
async update(args: { where?: unknown; data: unknown }) {
|
|
229
|
+
return { ...args.data };
|
|
230
|
+
},
|
|
231
|
+
async create(args: { data: unknown }) {
|
|
232
|
+
return args.data;
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
`,
|
|
237
|
+
'public/favicon.svg': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
238
|
+
<text y="28" font-size="28">◆</text>
|
|
239
|
+
</svg>`,
|
|
240
|
+
'.gitignore': `node_modules/
|
|
241
|
+
.nexus/
|
|
242
|
+
dist/
|
|
243
|
+
*.js.map
|
|
244
|
+
`,
|
|
245
|
+
};
|
|
246
|
+
for (const [filepath, content] of Object.entries(files)) {
|
|
247
|
+
const fullPath = join(dir, filepath);
|
|
248
|
+
await writeFile(fullPath, content, 'utf-8');
|
|
249
|
+
console.log(` ${GREEN}+${RESET} ${filepath}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
main().catch((err) => {
|
|
253
|
+
console.error('\x1b[31m[create-nexus Error]\x1b[0m', err);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
});
|
|
256
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAM,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,IAAI,GAAG,UAAU,CAAC;AACxB,MAAM,KAAK,GAAG,UAAU,CAAC;AACzB,MAAM,MAAM,GAAG,UAAU,CAAC;AAC1B,MAAM,KAAK,GAAG,SAAS,CAAC;AACxB,MAAM,IAAI,GAAG,SAAS,CAAC;AACvB,MAAM,GAAG,GAAG,SAAS,CAAC;AAEtB,MAAM,MAAM,GAAG;IACX,IAAI,iBAAiB,KAAK;;;;CAI7B,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAEpB,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC;IACtD,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;IAEtD,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,GAAG,WAAW,GAAG,KAAK,OAAO,CAAC,CAAC;IAE7D,6BAA6B;IAC7B,MAAM,IAAI,GAAG;QACX,YAAY;QACZ,wBAAwB;QACxB,sBAAsB;QACtB,gBAAgB;QAChB,aAAa;QACb,SAAS;QACT,QAAQ;KACT,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,sBAAsB;IACtB,MAAM,iBAAiB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAEhD,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,IAAI,KAAK,uBAAuB,IAAI,GAAG,WAAW,IAAI,KAAK,IAAI,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,KAAK,KAAK,IAAI,WAAW,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,eAAe,KAAK,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,WAAW,KAAK,IAAI,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,KAAK,UAAU,IAAI,sBAAsB,KAAK,IAAI,CAAC,CAAC;AAC/E,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,GAAW,EAAE,IAAY;IACxD,MAAM,KAAK,GAA2B;QACpC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC;YAC7B,IAAI;YACJ,OAAO,EAAE,OAAO;YAChB,OAAO,EAAE,IAAI;YACb,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE;gBACP,GAAG,EAAE,WAAW;gBAChB,KAAK,EAAE,aAAa;gBACpB,KAAK,EAAE,aAAa;gBACpB,KAAK,EAAE,aAAa;aACrB;YACD,YAAY,EAAE;gBACZ,mBAAmB,EAAE,aAAa;aACnC;YACD,eAAe,EAAE;gBACf,eAAe,EAAE,aAAa;gBAC9B,oBAAoB,EAAE,aAAa;gBACnC,UAAU,EAAE,QAAQ;aACrB;SACF,EAAE,IAAI,EAAE,CAAC,CAAC;QAEX,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC;YAC9B,OAAO,EAAE,0BAA0B;YACnC,eAAe,EAAE,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE;YACzD,OAAO,EAAE,CAAC,UAAU,CAAC;SACtB,EAAE,IAAI,EAAE,CAAC,CAAC;QAEX,iBAAiB,EAAE;;;;;;;;;;;;;;;;;;;;;;;CAuBtB;QAEG,uBAAuB,EAAE;;;;;;;;;;;;;;;;;;;;;CAqB5B;QAEG,qBAAqB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4C1B;QAEG,0BAA0B,EAAE;;;;;;;;;;;;;;;;;CAiB/B;QAEG,iCAAiC,EAAE;;;;;;;;;;;;CAYtC;QAEG,iCAAiC,EAAE;;;;;;;;;;;;;;;;;;;;CAoBtC;QAEG,eAAe,EAAE;;;;;;;;;;;;;;;;;;;CAmBpB;QAEG,oBAAoB,EAAE;;OAEnB;QAEH,YAAY,EAAE;;;;CAIjB;KACE,CAAC;IAEF,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACrC,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,GAAG,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/fix.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nexus fix — Automatic Vulnerability Remediation.
|
|
3
|
+
*
|
|
4
|
+
* Reads package.json, queries OSV for vulnerable packages,
|
|
5
|
+
* finds the patched version for each, updates package.json,
|
|
6
|
+
* runs the package manager install, and re-runs audit to verify.
|
|
7
|
+
*
|
|
8
|
+
* Unlike `npm audit fix` which is coarse-grained, `nexus fix`:
|
|
9
|
+
* - Only updates SPECIFICALLY the vulnerable package (not all transitive deps)
|
|
10
|
+
* - Targets the MINIMUM patched version (not always latest)
|
|
11
|
+
* - Respects your semver range preferences (^x.y.z → ^x.y.fix)
|
|
12
|
+
* - Shows a before/after diff
|
|
13
|
+
* - Offers a --dry-run mode (no changes written)
|
|
14
|
+
* - Re-audits after fixing to confirm 0 critical CVEs remain
|
|
15
|
+
*/
|
|
16
|
+
export interface FixOptions {
|
|
17
|
+
root: string;
|
|
18
|
+
dryRun?: boolean;
|
|
19
|
+
force?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function runFix(opts: FixOptions): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=fix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix.d.ts","sourceRoot":"","sources":["../src/fix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiDH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAK,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAG,OAAO,CAAC;CAClB;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAgK5D"}
|
package/dist/fix.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nexus fix — Automatic Vulnerability Remediation.
|
|
3
|
+
*
|
|
4
|
+
* Reads package.json, queries OSV for vulnerable packages,
|
|
5
|
+
* finds the patched version for each, updates package.json,
|
|
6
|
+
* runs the package manager install, and re-runs audit to verify.
|
|
7
|
+
*
|
|
8
|
+
* Unlike `npm audit fix` which is coarse-grained, `nexus fix`:
|
|
9
|
+
* - Only updates SPECIFICALLY the vulnerable package (not all transitive deps)
|
|
10
|
+
* - Targets the MINIMUM patched version (not always latest)
|
|
11
|
+
* - Respects your semver range preferences (^x.y.z → ^x.y.fix)
|
|
12
|
+
* - Shows a before/after diff
|
|
13
|
+
* - Offers a --dry-run mode (no changes written)
|
|
14
|
+
* - Re-audits after fixing to confirm 0 critical CVEs remain
|
|
15
|
+
*/
|
|
16
|
+
import { readFile } from 'node:fs/promises';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { execSync } from 'node:child_process';
|
|
19
|
+
// ── ANSI ──────────────────────────────────────────────────────────────────────
|
|
20
|
+
const c = {
|
|
21
|
+
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
|
|
22
|
+
red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m',
|
|
23
|
+
cyan: '\x1b[36m', mag: '\x1b[35m', gray: '\x1b[90m',
|
|
24
|
+
};
|
|
25
|
+
const log = {
|
|
26
|
+
ok: (...a) => console.log(` \x1b[32m✔\x1b[0m`, ...a),
|
|
27
|
+
warn: (...a) => console.log(` \x1b[33m⚠\x1b[0m`, ...a),
|
|
28
|
+
error: (...a) => console.error(` \x1b[31m✖\x1b[0m`, ...a),
|
|
29
|
+
info: (...a) => console.log(` \x1b[36mℹ\x1b[0m`, ...a),
|
|
30
|
+
step: (...a) => console.log(` \x1b[35m◆\x1b[0m`, ...a),
|
|
31
|
+
};
|
|
32
|
+
// ── Package manager detection ─────────────────────────────────────────────────
|
|
33
|
+
function detectPackageManager(root) {
|
|
34
|
+
const { existsSync } = require('node:fs');
|
|
35
|
+
if (existsSync(join(root, 'pnpm-lock.yaml')))
|
|
36
|
+
return 'pnpm';
|
|
37
|
+
if (existsSync(join(root, 'yarn.lock')))
|
|
38
|
+
return 'yarn';
|
|
39
|
+
return 'npm';
|
|
40
|
+
}
|
|
41
|
+
function installCmd(pm, pkg, version) {
|
|
42
|
+
const spec = `${pkg}@${version}`;
|
|
43
|
+
if (pm === 'pnpm')
|
|
44
|
+
return `pnpm add ${spec}`;
|
|
45
|
+
if (pm === 'yarn')
|
|
46
|
+
return `yarn add ${spec}`;
|
|
47
|
+
return `npm install ${spec}`;
|
|
48
|
+
}
|
|
49
|
+
// ── Semver helpers ────────────────────────────────────────────────────────────
|
|
50
|
+
function preserveRange(oldRange, newVersion) {
|
|
51
|
+
const prefix = oldRange.match(/^([~^>=<]+)/)?.[1] ?? '';
|
|
52
|
+
// If old range used ^, preserve it so minor updates still work
|
|
53
|
+
if (prefix === '^')
|
|
54
|
+
return `^${newVersion}`;
|
|
55
|
+
if (prefix === '~')
|
|
56
|
+
return `~${newVersion}`;
|
|
57
|
+
return newVersion;
|
|
58
|
+
}
|
|
59
|
+
export async function runFix(opts) {
|
|
60
|
+
const { root, dryRun = false } = opts;
|
|
61
|
+
console.log();
|
|
62
|
+
log.step(`${c.bold}Nexus Fix${c.reset} ${c.dim}—${c.reset} automatic vulnerability remediation`);
|
|
63
|
+
if (dryRun)
|
|
64
|
+
log.warn('Dry run mode — no changes will be written');
|
|
65
|
+
console.log();
|
|
66
|
+
// Load audit engine (dynamic — gracefully fails if not installed)
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
68
|
+
let audit;
|
|
69
|
+
try {
|
|
70
|
+
audit = await import('@nexus_js/audit');
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
log.error('@nexus_js/audit not installed. Run: pnpm add -D @nexus_js/audit');
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
+
const auditDependencies = audit.auditDependencies;
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
+
const filterVulnerable = audit.filterVulnerable;
|
|
81
|
+
// Read package.json
|
|
82
|
+
const pkgPath = join(root, 'package.json');
|
|
83
|
+
let pkgJson;
|
|
84
|
+
try {
|
|
85
|
+
pkgJson = JSON.parse(await readFile(pkgPath, 'utf-8'));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
log.error(`Cannot read ${pkgPath}`);
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const deps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };
|
|
93
|
+
const isDev = new Set(Object.keys(pkgJson.devDependencies ?? {}));
|
|
94
|
+
log.info(`Scanning ${Object.keys(deps).length} packages via OSV...`);
|
|
95
|
+
const results = await auditDependencies(deps);
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
97
|
+
const vulnerable = filterVulnerable(results).filter((r) => !opts.force ? (r.vulns[0]?.severity === 'critical' || r.vulns[0]?.severity === 'high') : true);
|
|
98
|
+
if (vulnerable.length === 0) {
|
|
99
|
+
log.ok('No critical/high vulnerabilities found — nothing to fix!');
|
|
100
|
+
console.log();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
console.log(` Found ${c.red}${vulnerable.length}${c.reset} vulnerable package${vulnerable.length > 1 ? 's' : ''}:\n`);
|
|
104
|
+
// Collect fix actions
|
|
105
|
+
const fixes = [];
|
|
106
|
+
const noFix = [];
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
108
|
+
for (const result of vulnerable) {
|
|
109
|
+
const topVuln = result.vulns[0];
|
|
110
|
+
if (!topVuln)
|
|
111
|
+
continue;
|
|
112
|
+
const currentRange = deps[result.package] ?? '*';
|
|
113
|
+
const fixedIn = topVuln.fixedIn;
|
|
114
|
+
console.log(` ${c.bold}${result.package}${c.reset} ${c.dim}(current: ${currentRange})${c.reset}\n` +
|
|
115
|
+
` ${topVuln.id} ${topVuln.severity.toUpperCase()} ${topVuln.summary}`);
|
|
116
|
+
if (fixedIn) {
|
|
117
|
+
const newRange = preserveRange(currentRange, fixedIn);
|
|
118
|
+
console.log(` ${c.green}→ Update to v${fixedIn}${c.reset} (range: ${newRange})\n`);
|
|
119
|
+
fixes.push({
|
|
120
|
+
pkg: result.package,
|
|
121
|
+
oldVersion: currentRange,
|
|
122
|
+
newVersion: newRange,
|
|
123
|
+
cve: topVuln.id,
|
|
124
|
+
isDev: isDev.has(result.package),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.log(` ${c.yellow}→ No patched version available in OSV data${c.reset}`);
|
|
129
|
+
console.log(` Consider: replacing, removing, or adding an allowVulnerable override\n`);
|
|
130
|
+
noFix.push({
|
|
131
|
+
pkg: result.package,
|
|
132
|
+
cve: topVuln.id,
|
|
133
|
+
reason: 'No patched version in OSV database',
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (fixes.length === 0) {
|
|
138
|
+
log.warn('No automatic fixes available. Review the packages above manually.');
|
|
139
|
+
console.log();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (dryRun) {
|
|
143
|
+
console.log(` ${c.dim}─────────────────────────────────────${c.reset}`);
|
|
144
|
+
console.log(` ${c.cyan}Dry run — would apply ${fixes.length} fix${fixes.length > 1 ? 'es' : ''}:${c.reset}`);
|
|
145
|
+
for (const f of fixes) {
|
|
146
|
+
console.log(` ${f.pkg}: ${f.oldVersion} → ${f.newVersion}`);
|
|
147
|
+
}
|
|
148
|
+
console.log();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// Apply fixes to package.json
|
|
152
|
+
const pm = detectPackageManager(root);
|
|
153
|
+
let applied = 0;
|
|
154
|
+
for (const fix of fixes) {
|
|
155
|
+
log.step(`Updating ${c.bold}${fix.pkg}${c.reset} ${fix.oldVersion} → ${c.green}${fix.newVersion}${c.reset}`);
|
|
156
|
+
try {
|
|
157
|
+
const cmd = installCmd(pm, fix.pkg, fix.newVersion.replace(/^[\^~]/, ''));
|
|
158
|
+
execSync(cmd, { cwd: root, stdio: 'inherit' });
|
|
159
|
+
applied++;
|
|
160
|
+
log.ok(`${fix.pkg} updated`);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
log.error(`Failed to update ${fix.pkg}. Try manually: ${pm} add ${fix.pkg}@${fix.newVersion}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
console.log();
|
|
167
|
+
// Re-audit to verify
|
|
168
|
+
if (applied > 0) {
|
|
169
|
+
log.step('Re-auditing after fixes...');
|
|
170
|
+
const updatedPkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
|
|
171
|
+
const updatedDeps = { ...updatedPkg.dependencies, ...updatedPkg.devDependencies };
|
|
172
|
+
const reCheck = await auditDependencies(updatedDeps);
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
174
|
+
const stillVulnerable = filterVulnerable(reCheck).filter((r) => r.vulns[0]?.severity === 'critical' || r.vulns[0]?.severity === 'high');
|
|
175
|
+
if (stillVulnerable.length === 0) {
|
|
176
|
+
log.ok(`${c.green}${c.bold}All critical/high vulnerabilities resolved!${c.reset}`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
log.warn(`${stillVulnerable.length} vulnerability/vulnerabilities remain:`);
|
|
180
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
181
|
+
for (const r of stillVulnerable) {
|
|
182
|
+
const v = r.vulns[0];
|
|
183
|
+
if (v)
|
|
184
|
+
log.error(` ${r.package} — ${v.id} (${v.severity})`);
|
|
185
|
+
}
|
|
186
|
+
log.info('Run `nexus audit` for full details. Some may require manual intervention.');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (noFix.length > 0) {
|
|
190
|
+
console.log();
|
|
191
|
+
log.warn(`${noFix.length} package${noFix.length > 1 ? 's have' : ' has'} no automatic fix:`);
|
|
192
|
+
for (const n of noFix) {
|
|
193
|
+
console.log(` ${c.yellow}${n.pkg}${c.reset} ${n.cve} — ${n.reason}`);
|
|
194
|
+
}
|
|
195
|
+
log.info('Consider adding an allowVulnerable override in nexus.config.ts with an expiry date.');
|
|
196
|
+
}
|
|
197
|
+
console.log();
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=fix.js.map
|
package/dist/fix.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix.js","sourceRoot":"","sources":["../src/fix.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/studio.d.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nexus Studio — Real-time developer dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Launched via `nexus studio` or automatically as a panel within `nexus dev`.
|
|
5
|
+
* Runs a lightweight WebSocket server alongside the main dev server.
|
|
6
|
+
* The browser UI connects to ws://localhost:${STUDIO_PORT}/_nexus/studio
|
|
7
|
+
*
|
|
8
|
+
* Panels:
|
|
9
|
+
* 1. Layout Tree — Visual nested layout hierarchy for the current route.
|
|
10
|
+
* 2. Island Map — All live islands on screen, their state, hydration strategy.
|
|
11
|
+
* 3. Action Log — Real-time stream of Server Action calls, payloads, timings.
|
|
12
|
+
* 4. JS Cost — Bundle analysis for the current route (mirrors nexus analyze).
|
|
13
|
+
* 5. Cache Inspector — Current cache entries, TTLs, hit/miss ratio.
|
|
14
|
+
* 6. Store Viewer — Live snapshot of the Global State Store.
|
|
15
|
+
*/
|
|
16
|
+
export declare const STUDIO_PORT = 4000;
|
|
17
|
+
export declare const STUDIO_WS_PATH = "/_nexus/studio";
|
|
18
|
+
export type StudioEvent = {
|
|
19
|
+
type: 'route:change';
|
|
20
|
+
payload: RouteInfo;
|
|
21
|
+
} | {
|
|
22
|
+
type: 'island:mounted';
|
|
23
|
+
payload: IslandInfo;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'island:destroyed';
|
|
26
|
+
payload: {
|
|
27
|
+
id: string;
|
|
28
|
+
};
|
|
29
|
+
} | {
|
|
30
|
+
type: 'island:state';
|
|
31
|
+
payload: {
|
|
32
|
+
id: string;
|
|
33
|
+
state: unknown;
|
|
34
|
+
};
|
|
35
|
+
} | {
|
|
36
|
+
type: 'action:call';
|
|
37
|
+
payload: ActionCall;
|
|
38
|
+
} | {
|
|
39
|
+
type: 'action:result';
|
|
40
|
+
payload: ActionResult;
|
|
41
|
+
} | {
|
|
42
|
+
type: 'action:error';
|
|
43
|
+
payload: ActionError;
|
|
44
|
+
} | {
|
|
45
|
+
type: 'cache:set';
|
|
46
|
+
payload: CacheEntry;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'cache:hit';
|
|
49
|
+
payload: {
|
|
50
|
+
key: string;
|
|
51
|
+
};
|
|
52
|
+
} | {
|
|
53
|
+
type: 'cache:miss';
|
|
54
|
+
payload: {
|
|
55
|
+
key: string;
|
|
56
|
+
};
|
|
57
|
+
} | {
|
|
58
|
+
type: 'store:update';
|
|
59
|
+
payload: {
|
|
60
|
+
key: string;
|
|
61
|
+
value: unknown;
|
|
62
|
+
};
|
|
63
|
+
} | {
|
|
64
|
+
type: 'hmr:update';
|
|
65
|
+
payload: {
|
|
66
|
+
file: string;
|
|
67
|
+
time: number;
|
|
68
|
+
};
|
|
69
|
+
} | {
|
|
70
|
+
type: 'build:complete';
|
|
71
|
+
payload: BuildInfo;
|
|
72
|
+
};
|
|
73
|
+
export interface RouteInfo {
|
|
74
|
+
path: string;
|
|
75
|
+
params: Record<string, string>;
|
|
76
|
+
layouts: string[];
|
|
77
|
+
page: string;
|
|
78
|
+
cacheTtl: number;
|
|
79
|
+
cacheStrategy: string;
|
|
80
|
+
jsCost: {
|
|
81
|
+
raw: number;
|
|
82
|
+
gzip: number;
|
|
83
|
+
budget: number;
|
|
84
|
+
overBudget: boolean;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export interface IslandInfo {
|
|
88
|
+
id: string;
|
|
89
|
+
component: string;
|
|
90
|
+
strategy: string;
|
|
91
|
+
state: unknown;
|
|
92
|
+
props: unknown;
|
|
93
|
+
el?: string;
|
|
94
|
+
}
|
|
95
|
+
export interface ActionCall {
|
|
96
|
+
id: string;
|
|
97
|
+
name: string;
|
|
98
|
+
islandId?: string;
|
|
99
|
+
input: unknown;
|
|
100
|
+
timestamp: number;
|
|
101
|
+
idempotencyKey?: string;
|
|
102
|
+
}
|
|
103
|
+
export interface ActionResult {
|
|
104
|
+
id: string;
|
|
105
|
+
name: string;
|
|
106
|
+
output: unknown;
|
|
107
|
+
duration: number;
|
|
108
|
+
cached: boolean;
|
|
109
|
+
}
|
|
110
|
+
export interface ActionError {
|
|
111
|
+
id: string;
|
|
112
|
+
name: string;
|
|
113
|
+
error: string;
|
|
114
|
+
code?: string;
|
|
115
|
+
duration: number;
|
|
116
|
+
}
|
|
117
|
+
export interface CacheEntry {
|
|
118
|
+
key: string;
|
|
119
|
+
tags: string[];
|
|
120
|
+
ttl: number;
|
|
121
|
+
size: number;
|
|
122
|
+
}
|
|
123
|
+
export interface BuildInfo {
|
|
124
|
+
routes: number;
|
|
125
|
+
islands: number;
|
|
126
|
+
totalSize: number;
|
|
127
|
+
duration: number;
|
|
128
|
+
}
|
|
129
|
+
export declare function broadcast(event: StudioEvent): void;
|
|
130
|
+
export interface StudioServer {
|
|
131
|
+
port: number;
|
|
132
|
+
close: () => void;
|
|
133
|
+
broadcast: typeof broadcast;
|
|
134
|
+
}
|
|
135
|
+
export declare function startStudio(preferredPort?: number): Promise<StudioServer>;
|
|
136
|
+
//# sourceMappingURL=studio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studio.d.ts","sourceRoot":"","sources":["../src/studio.ts"],"names":[],"mappings":"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"}
|