@nexus_js/compiler 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 +17 -0
- package/dist/codegen.d.ts +4 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +308 -0
- package/dist/codegen.js.map +1 -0
- package/dist/css-scope.d.ts +76 -0
- package/dist/css-scope.d.ts.map +1 -0
- package/dist/css-scope.js +327 -0
- package/dist/css-scope.js.map +1 -0
- package/dist/guard.d.ts +59 -0
- package/dist/guard.d.ts.map +1 -0
- package/dist/guard.js +212 -0
- package/dist/guard.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/island-ssr-stubs.d.ts +25 -0
- package/dist/island-ssr-stubs.d.ts.map +1 -0
- package/dist/island-ssr-stubs.js +107 -0
- package/dist/island-ssr-stubs.js.map +1 -0
- package/dist/island-wrap.d.ts +19 -0
- package/dist/island-wrap.d.ts.map +1 -0
- package/dist/island-wrap.js +108 -0
- package/dist/island-wrap.js.map +1 -0
- package/dist/parser.d.ts +28 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +128 -0
- package/dist/parser.js.map +1 -0
- package/dist/preload-scanner.d.ts +60 -0
- package/dist/preload-scanner.d.ts.map +1 -0
- package/dist/preload-scanner.js +156 -0
- package/dist/preload-scanner.js.map +1 -0
- package/dist/server-actions-extract.d.ts +9 -0
- package/dist/server-actions-extract.d.ts.map +1 -0
- package/dist/server-actions-extract.js +89 -0
- package/dist/server-actions-extract.js.map +1 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nexus CSS Scoping — Compile-time class hash injection with @layer.
|
|
3
|
+
*
|
|
4
|
+
* Strategy: Pure AOT, zero runtime overhead.
|
|
5
|
+
*
|
|
6
|
+
* Specificity fix (the insight from the user):
|
|
7
|
+
* Plain [data-nx="hash"] .card has higher specificity than .card,
|
|
8
|
+
* which breaks overrides from parent components or third-party libraries.
|
|
9
|
+
*
|
|
10
|
+
* Solution: Wrap ALL generated scoped styles inside @layer nexus.scoped.
|
|
11
|
+
* CSS Cascade Layers (Level 5 spec, baseline 2022) have LOWER specificity
|
|
12
|
+
* than unlayered styles by design, regardless of selector weight.
|
|
13
|
+
*
|
|
14
|
+
* Layer precedence (highest wins, last wins within same layer):
|
|
15
|
+
* unlayered styles > @layer nexus.global > @layer nexus.scoped
|
|
16
|
+
*
|
|
17
|
+
* This means:
|
|
18
|
+
* - Component styles (.card) are isolated by hash ✓
|
|
19
|
+
* - Parent overrides work without !important ✓
|
|
20
|
+
* - Third-party libraries can override without !important ✓
|
|
21
|
+
* - :global(selector) still works as an escape hatch ✓
|
|
22
|
+
*
|
|
23
|
+
* How it works:
|
|
24
|
+
* 1. Compute a stable 6-char hash from the component filepath (FNV-1a).
|
|
25
|
+
* 2. Rewrite every CSS selector to be scoped with [data-nx="<hash>"].
|
|
26
|
+
* 3. Wrap the entire output in @layer nexus.scoped { ... }.
|
|
27
|
+
* 4. Inject data-nx="<hash>" onto every root element in the template.
|
|
28
|
+
*
|
|
29
|
+
* Example — input:
|
|
30
|
+
* .card { color: red }
|
|
31
|
+
* .card:hover h2 { font-size: 2rem }
|
|
32
|
+
* @media (max-width: 768px) { .card { display: none } }
|
|
33
|
+
*
|
|
34
|
+
* Output:
|
|
35
|
+
* @layer nexus.scoped {
|
|
36
|
+
* [data-nx="a3f9c1"] .card { color: red }
|
|
37
|
+
* [data-nx="a3f9c1"] .card:hover h2 { font-size: 2rem }
|
|
38
|
+
* @media (max-width: 768px) { [data-nx="a3f9c1"] .card { display: none } }
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* Template rewrite:
|
|
42
|
+
* <div class="card"> → <div class="card" data-nx="a3f9c1">
|
|
43
|
+
*
|
|
44
|
+
* Layer declaration (injected once in root layout):
|
|
45
|
+
* @layer nexus.scoped, nexus.global;
|
|
46
|
+
*/
|
|
47
|
+
/** Murmurhash-inspired: fast, stable 32-bit hash → 6 hex chars */
|
|
48
|
+
export function componentHash(filepath) {
|
|
49
|
+
let h = 0x811c9dc5;
|
|
50
|
+
for (let i = 0; i < filepath.length; i++) {
|
|
51
|
+
h ^= filepath.charCodeAt(i);
|
|
52
|
+
h = Math.imul(h, 0x01000193);
|
|
53
|
+
}
|
|
54
|
+
return (h >>> 0).toString(16).padStart(8, '0').slice(0, 6);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Transforms raw CSS into scoped CSS using the component hash.
|
|
58
|
+
* Handles: selectors, @media, @keyframes (not scoped), @layer, :global() escape hatch.
|
|
59
|
+
*/
|
|
60
|
+
/** Layer declaration to emit once in the root <head> */
|
|
61
|
+
export const NEXUS_LAYER_DECLARATION = '@layer nexus.scoped, nexus.global;';
|
|
62
|
+
export function scopeCSS(rawCSS, filepath) {
|
|
63
|
+
const hash = componentHash(filepath);
|
|
64
|
+
const attr = `[data-nx="${hash}"]`;
|
|
65
|
+
const classes = new Set();
|
|
66
|
+
// Extract class names for template injection tracking
|
|
67
|
+
const classRe = /\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/g;
|
|
68
|
+
let m;
|
|
69
|
+
while ((m = classRe.exec(rawCSS)) !== null) {
|
|
70
|
+
if (m[1])
|
|
71
|
+
classes.add(m[1]);
|
|
72
|
+
}
|
|
73
|
+
const scoped = transformCSS(rawCSS, attr);
|
|
74
|
+
// Wrap in @layer nexus.scoped — fixes specificity wars.
|
|
75
|
+
// Cascade layers have lower priority than unlayered styles, so parent
|
|
76
|
+
// components and global CSS can always override without !important.
|
|
77
|
+
const layered = `@layer nexus.scoped {\n${scoped}\n}`;
|
|
78
|
+
return { css: layered, hash, classes };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Adds scope attribute to every HTML root element in a template string.
|
|
82
|
+
* Skips elements that are: slot, nexus-island, html, head, body.
|
|
83
|
+
* Handles :global(selector) — removes scoping for that selector.
|
|
84
|
+
*/
|
|
85
|
+
/**
|
|
86
|
+
* `.nx` pages often use `<template>...</template>` as the root. In the live DOM,
|
|
87
|
+
* `<template>` contents are inert (not rendered). SSR must unwrap the outer
|
|
88
|
+
* wrapper so the shell is visible; nested `<template>` inside the tree is rare
|
|
89
|
+
* and still wrapped until unwrapped by the same pass on inner routes only when
|
|
90
|
+
* they are the file root.
|
|
91
|
+
*/
|
|
92
|
+
export function unwrapOuterTemplateElement(html) {
|
|
93
|
+
const t = html.trimStart();
|
|
94
|
+
if (!/^<template\b/i.test(t))
|
|
95
|
+
return html;
|
|
96
|
+
const lower = t.toLowerCase();
|
|
97
|
+
let depth = 0;
|
|
98
|
+
let i = 0;
|
|
99
|
+
let contentStart = -1;
|
|
100
|
+
while (i < t.length) {
|
|
101
|
+
const open = lower.indexOf('<template', i);
|
|
102
|
+
const close = lower.indexOf('</template>', i);
|
|
103
|
+
if (open !== -1 && (close === -1 || open < close)) {
|
|
104
|
+
if (depth === 0) {
|
|
105
|
+
const gt = t.indexOf('>', open);
|
|
106
|
+
if (gt === -1)
|
|
107
|
+
return html;
|
|
108
|
+
contentStart = gt + 1;
|
|
109
|
+
}
|
|
110
|
+
depth++;
|
|
111
|
+
i = open + '<template'.length;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (close !== -1) {
|
|
115
|
+
depth--;
|
|
116
|
+
if (depth === 0 && contentStart !== -1) {
|
|
117
|
+
return t.slice(contentStart, close).trim();
|
|
118
|
+
}
|
|
119
|
+
i = close + '</template>'.length;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
return html;
|
|
125
|
+
}
|
|
126
|
+
export function scopeTemplate(html, hash) {
|
|
127
|
+
const skip = new Set(['html', 'head', 'body', 'meta', 'link', 'script', 'style', 'nexus-island', 'slot']);
|
|
128
|
+
let out = '';
|
|
129
|
+
let i = 0;
|
|
130
|
+
while (i < html.length) {
|
|
131
|
+
const lt = html.indexOf('<', i);
|
|
132
|
+
if (lt === -1) {
|
|
133
|
+
out += html.slice(i);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
out += html.slice(i, lt);
|
|
137
|
+
const afterLt = html.slice(lt + 1);
|
|
138
|
+
if (afterLt[0] === '/' || afterLt[0] === '!') {
|
|
139
|
+
const gt = html.indexOf('>', lt);
|
|
140
|
+
if (gt === -1) {
|
|
141
|
+
out += html.slice(lt);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
out += html.slice(lt, gt + 1);
|
|
145
|
+
i = gt + 1;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const tagM = /^([a-zA-Z][\w-]*)/.exec(afterLt);
|
|
149
|
+
if (!tagM) {
|
|
150
|
+
out += '<';
|
|
151
|
+
i = lt + 1;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const tag = tagM[1] ?? '';
|
|
155
|
+
if (!tag) {
|
|
156
|
+
out += '<';
|
|
157
|
+
i = lt + 1;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const lower = tag.toLowerCase();
|
|
161
|
+
let j = lt + 1 + tagM[0].length;
|
|
162
|
+
let brace = 0;
|
|
163
|
+
let quote = null;
|
|
164
|
+
let closed = false;
|
|
165
|
+
while (j < html.length) {
|
|
166
|
+
const c = html[j];
|
|
167
|
+
if (quote !== null) {
|
|
168
|
+
if (c === '\\' && j + 1 < html.length) {
|
|
169
|
+
j += 2;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (c === quote)
|
|
173
|
+
quote = null;
|
|
174
|
+
j++;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (c === '"' || c === "'") {
|
|
178
|
+
quote = c;
|
|
179
|
+
j++;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (c === '{')
|
|
183
|
+
brace++;
|
|
184
|
+
else if (c === '}')
|
|
185
|
+
brace = Math.max(0, brace - 1);
|
|
186
|
+
else if (c === '/' && html[j + 1] === '>' && brace === 0) {
|
|
187
|
+
const full = html.slice(lt, j + 2);
|
|
188
|
+
if (skip.has(lower) || full.includes('data-nx=')) {
|
|
189
|
+
out += full;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
const attrPart = html.slice(lt + 1 + tagM[0].length, j);
|
|
193
|
+
out += `<${tag}${attrPart} data-nx="${hash}" />`;
|
|
194
|
+
}
|
|
195
|
+
j += 2;
|
|
196
|
+
closed = true;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
else if (c === '>' && brace === 0) {
|
|
200
|
+
const full = html.slice(lt, j + 1);
|
|
201
|
+
if (skip.has(lower) || full.includes('data-nx=')) {
|
|
202
|
+
out += full;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const attrPart = html.slice(lt + 1 + tagM[0].length, j);
|
|
206
|
+
out += `<${tag}${attrPart} data-nx="${hash}">`;
|
|
207
|
+
}
|
|
208
|
+
j++;
|
|
209
|
+
closed = true;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
j++;
|
|
213
|
+
}
|
|
214
|
+
if (!closed) {
|
|
215
|
+
out += html.slice(lt);
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
i = j;
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
223
|
+
// Internal CSS transformer
|
|
224
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
225
|
+
function transformCSS(css, attr) {
|
|
226
|
+
// Remove /* comments */
|
|
227
|
+
const stripped = css.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
228
|
+
// Tokenize into rules (naive but effective for our subset)
|
|
229
|
+
return processBlock(stripped, attr);
|
|
230
|
+
}
|
|
231
|
+
function processBlock(block, attr) {
|
|
232
|
+
const result = [];
|
|
233
|
+
let i = 0;
|
|
234
|
+
const len = block.length;
|
|
235
|
+
while (i < len) {
|
|
236
|
+
// Skip whitespace
|
|
237
|
+
const wsStart = i;
|
|
238
|
+
while (i < len && /\s/.test(block[i] ?? ''))
|
|
239
|
+
i++;
|
|
240
|
+
// @ rules
|
|
241
|
+
if (block[i] === '@') {
|
|
242
|
+
const atEnd = block.indexOf('{', i);
|
|
243
|
+
if (atEnd === -1) {
|
|
244
|
+
i = len;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const atRule = block.slice(i, atEnd).trim();
|
|
248
|
+
i = atEnd + 1;
|
|
249
|
+
// Find matching closing brace
|
|
250
|
+
let depth = 1;
|
|
251
|
+
const bodyStart = i;
|
|
252
|
+
while (i < len && depth > 0) {
|
|
253
|
+
if (block[i] === '{')
|
|
254
|
+
depth++;
|
|
255
|
+
else if (block[i] === '}')
|
|
256
|
+
depth--;
|
|
257
|
+
i++;
|
|
258
|
+
}
|
|
259
|
+
const body = block.slice(bodyStart, i - 1);
|
|
260
|
+
// @keyframes — don't scope
|
|
261
|
+
if (/^@keyframes/i.test(atRule)) {
|
|
262
|
+
result.push(`${atRule} {${body}}`);
|
|
263
|
+
}
|
|
264
|
+
// @layer, @supports, @media — recurse into body
|
|
265
|
+
else if (/^@(media|supports|layer|container)/i.test(atRule)) {
|
|
266
|
+
result.push(`${atRule} {${processBlock(body, attr)}}`);
|
|
267
|
+
}
|
|
268
|
+
// Other @ rules — pass through
|
|
269
|
+
else {
|
|
270
|
+
result.push(`${atRule} {${body}}`);
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
// Regular rule: find selector + body
|
|
275
|
+
const ruleStart = i;
|
|
276
|
+
const braceOpen = block.indexOf('{', i);
|
|
277
|
+
if (braceOpen === -1)
|
|
278
|
+
break;
|
|
279
|
+
const selector = block.slice(i, braceOpen).trim();
|
|
280
|
+
i = braceOpen + 1;
|
|
281
|
+
let depth = 1;
|
|
282
|
+
const bodyStart = i;
|
|
283
|
+
while (i < len && depth > 0) {
|
|
284
|
+
if (block[i] === '{')
|
|
285
|
+
depth++;
|
|
286
|
+
else if (block[i] === '}')
|
|
287
|
+
depth--;
|
|
288
|
+
i++;
|
|
289
|
+
}
|
|
290
|
+
const body = block.slice(bodyStart, i - 1);
|
|
291
|
+
if (!selector)
|
|
292
|
+
continue;
|
|
293
|
+
const scopedSelector = scopeSelector(selector, attr);
|
|
294
|
+
result.push(`${scopedSelector} {${body}}`);
|
|
295
|
+
}
|
|
296
|
+
return result.join('\n');
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Scopes a CSS selector string (may be comma-separated).
|
|
300
|
+
* Handles :global(selector) escape hatch — removes scope for that part.
|
|
301
|
+
*/
|
|
302
|
+
function scopeSelector(selector, attr) {
|
|
303
|
+
return selector
|
|
304
|
+
.split(',')
|
|
305
|
+
.map((s) => scopeSingleSelector(s.trim(), attr))
|
|
306
|
+
.join(', ');
|
|
307
|
+
}
|
|
308
|
+
function scopeSingleSelector(sel, attr) {
|
|
309
|
+
// :global(...) escape hatch — strip :global() wrapper, don't scope
|
|
310
|
+
if (/^:global\(/.test(sel)) {
|
|
311
|
+
return sel.replace(/:global\(([^)]+)\)/g, '$1');
|
|
312
|
+
}
|
|
313
|
+
// Handle :global inside a selector
|
|
314
|
+
if (sel.includes(':global(')) {
|
|
315
|
+
return sel.replace(/:global\(([^)]+)\)/g, '$1');
|
|
316
|
+
}
|
|
317
|
+
// Skip already-scoped or bare combinators
|
|
318
|
+
if (sel.startsWith(attr))
|
|
319
|
+
return sel;
|
|
320
|
+
// For :root, :host — insert attr before
|
|
321
|
+
if (/^:(root|host)/.test(sel)) {
|
|
322
|
+
return `${attr}${sel}`;
|
|
323
|
+
}
|
|
324
|
+
// Default: prepend the scope attribute
|
|
325
|
+
return `${attr} ${sel}`;
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=css-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css-scope.js","sourceRoot":"","sources":["../src/css-scope.ts"],"names":[],"mappings":"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"}
|
package/dist/guard.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nexus Guard — Compiler-level security leak detector.
|
|
3
|
+
*
|
|
4
|
+
* Analyzes .nx files to ensure server-only values (environment variables,
|
|
5
|
+
* DB connection strings, API secrets, private keys) never reach the client
|
|
6
|
+
* bundle. The guard runs at build time AND in dev mode on every file change.
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* .nx file
|
|
10
|
+
* ├── Server block (between --- delimiters) — safe to use secrets
|
|
11
|
+
* └── Template section
|
|
12
|
+
* ├── Static HTML — never executed by client
|
|
13
|
+
* └── <script> blocks — compiled into client bundle ← DANGER ZONE
|
|
14
|
+
*
|
|
15
|
+
* The guard checks if variables holding secret values are referenced inside
|
|
16
|
+
* <script> blocks that will be included in the client bundle.
|
|
17
|
+
*/
|
|
18
|
+
export type LeakType = 'env-secret' | 'env-var' | 'db-connection' | 'api-key' | 'private-key';
|
|
19
|
+
export type Severity = 'error' | 'warning';
|
|
20
|
+
export interface SecurityLeak {
|
|
21
|
+
type: LeakType;
|
|
22
|
+
variable: string;
|
|
23
|
+
line: number;
|
|
24
|
+
column: number;
|
|
25
|
+
severity: Severity;
|
|
26
|
+
message: string;
|
|
27
|
+
/** Actionable fix suggestion */
|
|
28
|
+
hint: string;
|
|
29
|
+
}
|
|
30
|
+
export interface GuardResult {
|
|
31
|
+
filepath: string;
|
|
32
|
+
passed: boolean;
|
|
33
|
+
leaks: SecurityLeak[];
|
|
34
|
+
/** Lines scanned */
|
|
35
|
+
scanned: number;
|
|
36
|
+
/** ms taken to run the guard */
|
|
37
|
+
duration: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Analyzes a .nx file for security leaks.
|
|
41
|
+
*
|
|
42
|
+
* @param source - Raw .nx file content
|
|
43
|
+
* @param filepath - Path to the file (for error messages)
|
|
44
|
+
* @returns GuardResult with all detected leaks
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* import { guard } from '@nexus_js/compiler/guard';
|
|
48
|
+
* const result = guard(source, 'src/routes/+page.nx');
|
|
49
|
+
* if (!result.passed) {
|
|
50
|
+
* for (const leak of result.leaks.filter(l => l.severity === 'error')) {
|
|
51
|
+
* console.error(`[Guard] ${leak.message} (line ${leak.line})`);
|
|
52
|
+
* }
|
|
53
|
+
* process.exit(1);
|
|
54
|
+
* }
|
|
55
|
+
*/
|
|
56
|
+
export declare function guard(source: string, filepath: string): GuardResult;
|
|
57
|
+
/** Format a GuardResult for terminal output with ANSI colors. */
|
|
58
|
+
export declare function formatGuardResult(result: GuardResult): string;
|
|
59
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,QAAQ,GAChB,YAAY,GACZ,SAAS,GACT,eAAe,GACf,SAAS,GACT,aAAa,CAAC;AAElB,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAM,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAM,MAAM,CAAC;IACjB,MAAM,EAAI,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAG,MAAM,CAAC;IACjB,gCAAgC;IAChC,IAAI,EAAM,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAG,MAAM,CAAC;IAClB,MAAM,EAAK,OAAO,CAAC;IACnB,KAAK,EAAM,YAAY,EAAE,CAAC;IAC1B,oBAAoB;IACpB,OAAO,EAAI,MAAM,CAAC;IAClB,gCAAgC;IAChC,QAAQ,EAAG,MAAM,CAAC;CACnB;AA0GD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAiFnE;AAED,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CA0B7D"}
|
package/dist/guard.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nexus Guard — Compiler-level security leak detector.
|
|
3
|
+
*
|
|
4
|
+
* Analyzes .nx files to ensure server-only values (environment variables,
|
|
5
|
+
* DB connection strings, API secrets, private keys) never reach the client
|
|
6
|
+
* bundle. The guard runs at build time AND in dev mode on every file change.
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* .nx file
|
|
10
|
+
* ├── Server block (between --- delimiters) — safe to use secrets
|
|
11
|
+
* └── Template section
|
|
12
|
+
* ├── Static HTML — never executed by client
|
|
13
|
+
* └── <script> blocks — compiled into client bundle ← DANGER ZONE
|
|
14
|
+
*
|
|
15
|
+
* The guard checks if variables holding secret values are referenced inside
|
|
16
|
+
* <script> blocks that will be included in the client bundle.
|
|
17
|
+
*/
|
|
18
|
+
const PATTERNS = [
|
|
19
|
+
{
|
|
20
|
+
// PEM private keys
|
|
21
|
+
regex: /-----BEGIN\s+[\w\s]+PRIVATE KEY-----/gi,
|
|
22
|
+
type: 'private-key',
|
|
23
|
+
severity: 'error',
|
|
24
|
+
hint: 'Private keys must never appear in source files. Use a secrets manager or environment variable.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
// Stripe, Clerk, Supabase secret keys
|
|
28
|
+
regex: /\b(sk_live_|sk_test_|supabase_secret_|clerk_secret_|AKID)[A-Za-z0-9_-]{10,}/g,
|
|
29
|
+
type: 'api-key',
|
|
30
|
+
severity: 'error',
|
|
31
|
+
hint: 'Hard-coded API keys are a critical security risk. Store in process.env and reference server-side only.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
// Database connection strings
|
|
35
|
+
regex: /(['"`])(postgresql|mysql|mongodb|redis|sqlite):\/\/[^'"`\s]+\1/gi,
|
|
36
|
+
type: 'db-connection',
|
|
37
|
+
severity: 'error',
|
|
38
|
+
hint: 'Database URLs contain credentials. Use process.env.DATABASE_URL in the server block only.',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
// High-risk env vars (secrets by name)
|
|
42
|
+
regex: /process\.env\.(\w*(?:PASSWORD|SECRET|PRIVATE|KEY|TOKEN|CERT|SEED|SALT|CREDENTIALS)\w*)/gi,
|
|
43
|
+
type: 'env-secret',
|
|
44
|
+
severity: 'error',
|
|
45
|
+
hint: 'This env var appears to contain a secret. Access it only in the server frontmatter (--- block).',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
// Any other process.env.* reference in client code
|
|
49
|
+
regex: /process\.env\.([A-Z_][A-Z0-9_]+)/g,
|
|
50
|
+
type: 'env-var',
|
|
51
|
+
severity: 'warning',
|
|
52
|
+
hint: 'process.env is not available in the browser. Move this to the server frontmatter or use $env() from @nexus_js/runtime.',
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
// ── Parser helpers ─────────────────────────────────────────────────────────────
|
|
56
|
+
/** Extract the server frontmatter block (between --- delimiters). */
|
|
57
|
+
function extractServerBlock(source) {
|
|
58
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source);
|
|
59
|
+
return match?.[1] ?? '';
|
|
60
|
+
}
|
|
61
|
+
/** Extract all client-facing <script> blocks from the template section. */
|
|
62
|
+
function extractClientScripts(source) {
|
|
63
|
+
// Remove frontmatter first
|
|
64
|
+
const template = source.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, '');
|
|
65
|
+
const frontmatterLines = source.length - template.length > 0
|
|
66
|
+
? source.slice(0, source.indexOf(template)).split('\n').length
|
|
67
|
+
: 0;
|
|
68
|
+
const results = [];
|
|
69
|
+
const scriptRe = /<script(?:\s[^>]*)?>(?!\s*\/\/)?([\s\S]*?)<\/script>/gi;
|
|
70
|
+
let match;
|
|
71
|
+
while ((match = scriptRe.exec(template)) !== null) {
|
|
72
|
+
const before = template.slice(0, match.index);
|
|
73
|
+
const startLine = frontmatterLines + before.split('\n').length;
|
|
74
|
+
results.push({ content: match[1] ?? '', startLine });
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
/** Scan a code block for a pattern, returning all match locations. */
|
|
79
|
+
function scanBlock(content, startLine, pattern) {
|
|
80
|
+
const results = [];
|
|
81
|
+
const lines = content.split('\n');
|
|
82
|
+
const regex = new RegExp(pattern.regex.source, pattern.regex.flags.includes('g') ? pattern.regex.flags : pattern.regex.flags + 'g');
|
|
83
|
+
for (let i = 0; i < lines.length; i++) {
|
|
84
|
+
regex.lastIndex = 0;
|
|
85
|
+
let m;
|
|
86
|
+
while ((m = regex.exec(lines[i] ?? '')) !== null) {
|
|
87
|
+
results.push({
|
|
88
|
+
variable: m[1] != null ? `${m[0].split('(')[0]}.${m[1]}` : m[0].slice(0, 60),
|
|
89
|
+
line: startLine + i,
|
|
90
|
+
column: m.index,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return results;
|
|
95
|
+
}
|
|
96
|
+
// ── Public API ─────────────────────────────────────────────────────────────────
|
|
97
|
+
/**
|
|
98
|
+
* Analyzes a .nx file for security leaks.
|
|
99
|
+
*
|
|
100
|
+
* @param source - Raw .nx file content
|
|
101
|
+
* @param filepath - Path to the file (for error messages)
|
|
102
|
+
* @returns GuardResult with all detected leaks
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* import { guard } from '@nexus_js/compiler/guard';
|
|
106
|
+
* const result = guard(source, 'src/routes/+page.nx');
|
|
107
|
+
* if (!result.passed) {
|
|
108
|
+
* for (const leak of result.leaks.filter(l => l.severity === 'error')) {
|
|
109
|
+
* console.error(`[Guard] ${leak.message} (line ${leak.line})`);
|
|
110
|
+
* }
|
|
111
|
+
* process.exit(1);
|
|
112
|
+
* }
|
|
113
|
+
*/
|
|
114
|
+
export function guard(source, filepath) {
|
|
115
|
+
const t0 = Date.now();
|
|
116
|
+
const leaks = [];
|
|
117
|
+
const serverBlock = extractServerBlock(source);
|
|
118
|
+
const clientScripts = extractClientScripts(source);
|
|
119
|
+
// ── Step 1: Find secret variable names defined in the server block ─────────
|
|
120
|
+
const serverSecrets = new Map();
|
|
121
|
+
for (const pattern of PATTERNS) {
|
|
122
|
+
const regex = new RegExp(pattern.regex.source, 'gi');
|
|
123
|
+
let m;
|
|
124
|
+
while ((m = regex.exec(serverBlock)) !== null) {
|
|
125
|
+
// Look backwards for a variable assignment: const apiKey = process.env.API_KEY
|
|
126
|
+
const beforeMatch = serverBlock.slice(0, m.index);
|
|
127
|
+
const assignMatch = /(?:const|let|var)\s+(\w+)\s*=\s*$/.exec(beforeMatch.trimEnd());
|
|
128
|
+
if (assignMatch?.[1]) {
|
|
129
|
+
serverSecrets.set(assignMatch[1], { type: pattern.type, severity: pattern.severity });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// ── Step 2: Scan client scripts for secret variable references ─────────────
|
|
134
|
+
for (const { content, startLine } of clientScripts) {
|
|
135
|
+
// Check if any server-assigned secret variable is used here
|
|
136
|
+
for (const [varName, info] of serverSecrets) {
|
|
137
|
+
const varRe = new RegExp(`\\b${varName}\\b`, 'g');
|
|
138
|
+
const lines = content.split('\n');
|
|
139
|
+
for (let i = 0; i < lines.length; i++) {
|
|
140
|
+
const col = (lines[i] ?? '').search(varRe);
|
|
141
|
+
if (col !== -1) {
|
|
142
|
+
leaks.push({
|
|
143
|
+
type: info.type,
|
|
144
|
+
variable: varName,
|
|
145
|
+
line: startLine + i,
|
|
146
|
+
column: col,
|
|
147
|
+
severity: 'error',
|
|
148
|
+
message: `Secret "${varName}" defined in server block may leak to client bundle`,
|
|
149
|
+
hint: `Pass only sanitized values to island props. Never forward server secrets as component attributes.`,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Check for direct secret patterns in client code
|
|
155
|
+
for (const pattern of PATTERNS) {
|
|
156
|
+
const found = scanBlock(content, startLine, pattern);
|
|
157
|
+
for (const f of found) {
|
|
158
|
+
// Skip if already caught by step 1
|
|
159
|
+
if (!leaks.some((l) => l.line === f.line && l.variable === f.variable)) {
|
|
160
|
+
leaks.push({
|
|
161
|
+
type: pattern.type,
|
|
162
|
+
variable: f.variable,
|
|
163
|
+
line: f.line,
|
|
164
|
+
column: f.column,
|
|
165
|
+
severity: pattern.severity,
|
|
166
|
+
message: `${pattern.type === 'env-var' ? 'process.env' : 'Secret pattern'} "${f.variable}" in client-facing script`,
|
|
167
|
+
hint: pattern.hint,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Deduplicate by line+variable
|
|
174
|
+
const seen = new Set();
|
|
175
|
+
const unique = leaks.filter((l) => {
|
|
176
|
+
const key = `${l.line}:${l.variable}`;
|
|
177
|
+
if (seen.has(key))
|
|
178
|
+
return false;
|
|
179
|
+
seen.add(key);
|
|
180
|
+
return true;
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
filepath,
|
|
184
|
+
passed: unique.filter((l) => l.severity === 'error').length === 0,
|
|
185
|
+
leaks: unique,
|
|
186
|
+
scanned: source.split('\n').length,
|
|
187
|
+
duration: Date.now() - t0,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/** Format a GuardResult for terminal output with ANSI colors. */
|
|
191
|
+
export function formatGuardResult(result) {
|
|
192
|
+
const c = {
|
|
193
|
+
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
|
|
194
|
+
red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m',
|
|
195
|
+
cyan: '\x1b[36m', gray: '\x1b[90m',
|
|
196
|
+
};
|
|
197
|
+
if (result.passed && result.leaks.length === 0) {
|
|
198
|
+
return ` ${c.green}🛡️ Guard${c.reset} ${result.filepath} ${c.dim}${result.scanned} lines — no leaks found${c.reset}`;
|
|
199
|
+
}
|
|
200
|
+
const lines = [
|
|
201
|
+
` ${c.red}🛡️ Guard${c.reset} ${result.filepath} ${c.dim}${result.scanned} lines${c.reset}`,
|
|
202
|
+
];
|
|
203
|
+
for (const leak of result.leaks) {
|
|
204
|
+
const sCol = leak.severity === 'error' ? c.red : c.yellow;
|
|
205
|
+
lines.push(`\n ${sCol}${leak.severity.toUpperCase().padEnd(7)}${c.reset}` +
|
|
206
|
+
` line ${leak.line} ${c.bold}"${leak.variable}"${c.reset}` +
|
|
207
|
+
`\n ${c.dim}${leak.message}${c.reset}` +
|
|
208
|
+
`\n ${c.cyan}Hint: ${leak.hint}${c.reset}`);
|
|
209
|
+
}
|
|
210
|
+
return lines.join('\n');
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { parse } from './parser.js';
|
|
2
|
+
export { generate } from './codegen.js';
|
|
3
|
+
export { extractServerActionsFromSource } from './server-actions-extract.js';
|
|
4
|
+
export type { ParsedComponent, CompileOptions, CompileResult, NexusBlock, IslandDirective, IslandHydration, ServerAction, IslandManifest, IslandEntry, RouteManifest, RouteEntry, CompileWarning, } from './types.js';
|
|
5
|
+
import type { CompileOptions, CompileResult } from './types.js';
|
|
6
|
+
/** High-level API: compile a .nx source string end-to-end */
|
|
7
|
+
export declare function compile(source: string, filepath: string, opts?: Partial<CompileOptions>): CompileResult;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAC;AAC7E,YAAY,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACb,UAAU,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,cAAc,EACd,WAAW,EACX,aAAa,EACb,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAC;AAIpB,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhE,6DAA6D;AAC7D,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,OAAO,CAAC,cAAc,CAAM,GACjC,aAAa,CAYf"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { parse } from './parser.js';
|
|
2
|
+
export { generate } from './codegen.js';
|
|
3
|
+
export { extractServerActionsFromSource } from './server-actions-extract.js';
|
|
4
|
+
import { parse } from './parser.js';
|
|
5
|
+
import { generate } from './codegen.js';
|
|
6
|
+
/** High-level API: compile a .nx source string end-to-end */
|
|
7
|
+
export function compile(source, filepath, opts = {}) {
|
|
8
|
+
const options = {
|
|
9
|
+
mode: opts.mode ?? 'server',
|
|
10
|
+
dev: opts.dev ?? false,
|
|
11
|
+
ssr: opts.ssr ?? true,
|
|
12
|
+
emitIslandManifest: opts.emitIslandManifest ?? true,
|
|
13
|
+
target: opts.target ?? 'node',
|
|
14
|
+
...(opts.appRoot !== undefined ? { appRoot: opts.appRoot } : {}),
|
|
15
|
+
};
|
|
16
|
+
const parsed = parse(source, filepath);
|
|
17
|
+
return generate(parsed, options);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=index.js.map
|