@zap-js/client 0.1.2 → 0.1.4
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.
|
@@ -27,8 +27,16 @@ export async function buildCommand(options) {
|
|
|
27
27
|
cliLogger.error(error);
|
|
28
28
|
}
|
|
29
29
|
cliLogger.newline();
|
|
30
|
-
cliLogger.error('
|
|
31
|
-
cliLogger.
|
|
30
|
+
cliLogger.error('Server imports found in restricted locations');
|
|
31
|
+
cliLogger.newline();
|
|
32
|
+
cliLogger.info('Allowed locations for server imports:');
|
|
33
|
+
cliLogger.info(' - routes/api/** (server-side routes)');
|
|
34
|
+
cliLogger.info(' - routes/ws/** (WebSocket routes)');
|
|
35
|
+
cliLogger.info(' - src/api/** (API clients)');
|
|
36
|
+
cliLogger.info(' - src/services/** (business logic)');
|
|
37
|
+
cliLogger.info(' - src/generated/** (generated code)');
|
|
38
|
+
cliLogger.newline();
|
|
39
|
+
cliLogger.info('Move server imports to allowed directories or remove them.');
|
|
32
40
|
throw new Error('Build validation failed');
|
|
33
41
|
}
|
|
34
42
|
if (validation.warnings.length > 0) {
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
+
import { parse } from '@babel/parser';
|
|
4
|
+
import traverse from '@babel/traverse';
|
|
3
5
|
const SERVER_ONLY_IMPORTS = [
|
|
4
6
|
'@zap-js/server',
|
|
5
7
|
'@zap-js/client/node',
|
|
6
8
|
'@zap-js/client/server',
|
|
7
9
|
];
|
|
10
|
+
// Path classification tiers
|
|
11
|
+
const ALWAYS_ALLOWED = ['/routes/api/', '/routes/ws/'];
|
|
12
|
+
const BUSINESS_LOGIC_ALLOWED = ['/src/api/', '/src/services/', '/src/generated/', '/src/lib/api/'];
|
|
13
|
+
const UI_LAYER_BLOCKED = ['/src/components/', '/src/pages/', '/src/ui/'];
|
|
14
|
+
const ALLOWED_FILE_PATTERNS = [/\/rpc-client\.(ts|js)$/, /\/api-client\.(ts|js)$/];
|
|
8
15
|
/**
|
|
9
16
|
* Validates that frontend code doesn't import server-only packages
|
|
10
17
|
* Returns array of error messages (empty if valid)
|
|
@@ -15,34 +22,84 @@ export function validateNoServerImportsInFrontend(srcDir) {
|
|
|
15
22
|
// Only scan TypeScript/JavaScript files
|
|
16
23
|
if (!filePath.match(/\.(tsx?|jsx?)$/))
|
|
17
24
|
return;
|
|
18
|
-
// Skip API routes (these are server-side)
|
|
19
|
-
if (filePath.includes('/routes/api/'))
|
|
20
|
-
return;
|
|
21
|
-
if (filePath.includes('/routes/ws/'))
|
|
22
|
-
return;
|
|
23
25
|
// Skip node_modules
|
|
24
26
|
if (filePath.includes('node_modules'))
|
|
25
27
|
return;
|
|
28
|
+
// Tier 1: Check server-side paths first (early exit)
|
|
29
|
+
for (const allowed of ALWAYS_ALLOWED) {
|
|
30
|
+
if (filePath.includes(allowed))
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Tier 2: Check business logic paths
|
|
34
|
+
for (const allowed of BUSINESS_LOGIC_ALLOWED) {
|
|
35
|
+
if (filePath.includes(allowed))
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Tier 3: Check special file patterns
|
|
39
|
+
for (const pattern of ALLOWED_FILE_PATTERNS) {
|
|
40
|
+
if (pattern.test(filePath))
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// Now scan file for server imports using AST parsing
|
|
26
44
|
try {
|
|
27
45
|
const content = readFileSync(filePath, 'utf-8');
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
let serverImportFound = null;
|
|
47
|
+
// Parse file to AST
|
|
48
|
+
const ast = parse(content, {
|
|
49
|
+
sourceType: 'module',
|
|
50
|
+
plugins: ['typescript', 'jsx'],
|
|
51
|
+
errorRecovery: true,
|
|
52
|
+
});
|
|
53
|
+
// Traverse AST to find actual import declarations
|
|
54
|
+
traverse(ast, {
|
|
55
|
+
ImportDeclaration(path) {
|
|
56
|
+
const importSource = path.node.source.value;
|
|
57
|
+
// Check if this is a server-only import
|
|
58
|
+
for (const serverImport of SERVER_ONLY_IMPORTS) {
|
|
59
|
+
if (importSource === serverImport || importSource.startsWith(serverImport + '/')) {
|
|
60
|
+
serverImportFound = serverImport;
|
|
61
|
+
path.stop(); // Stop traversal once we find one
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
40
64
|
}
|
|
65
|
+
},
|
|
66
|
+
CallExpression(path) {
|
|
67
|
+
// Also check for require() calls
|
|
68
|
+
if (path.node.callee.type === 'Identifier' &&
|
|
69
|
+
path.node.callee.name === 'require' &&
|
|
70
|
+
path.node.arguments.length > 0 &&
|
|
71
|
+
path.node.arguments[0].type === 'StringLiteral') {
|
|
72
|
+
const requireSource = path.node.arguments[0].value;
|
|
73
|
+
for (const serverImport of SERVER_ONLY_IMPORTS) {
|
|
74
|
+
if (requireSource === serverImport || requireSource.startsWith(serverImport + '/')) {
|
|
75
|
+
serverImportFound = serverImport;
|
|
76
|
+
path.stop();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
// Tier 4: Classify and report errors
|
|
84
|
+
if (serverImportFound) {
|
|
85
|
+
// Check if in UI layer (explicit block)
|
|
86
|
+
let inUILayer = false;
|
|
87
|
+
for (const blocked of UI_LAYER_BLOCKED) {
|
|
88
|
+
if (filePath.includes(blocked)) {
|
|
89
|
+
inUILayer = true;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (inUILayer) {
|
|
94
|
+
errors.push(`${filePath}: Server import '${serverImportFound}' in UI layer`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
errors.push(`${filePath}: Server import '${serverImportFound}' in unclassified path`);
|
|
41
98
|
}
|
|
42
99
|
}
|
|
43
100
|
}
|
|
44
101
|
catch (err) {
|
|
45
|
-
// Ignore files that can't be read
|
|
102
|
+
// Ignore files that can't be parsed or read
|
|
46
103
|
}
|
|
47
104
|
}
|
|
48
105
|
function scanDir(dir) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zap-js/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "High-performance fullstack React framework - Client package",
|
|
5
5
|
"homepage": "https://github.com/saint0x/zapjs",
|
|
6
6
|
"repository": {
|
|
@@ -53,6 +53,9 @@
|
|
|
53
53
|
"test:coverage": "bun test --coverage"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
+
"@babel/parser": "^7.28.5",
|
|
57
|
+
"@babel/traverse": "^7.28.5",
|
|
58
|
+
"@babel/types": "^7.28.5",
|
|
56
59
|
"@msgpack/msgpack": "^3.1.2",
|
|
57
60
|
"@types/fs-extra": "^11.0.4",
|
|
58
61
|
"chalk": "^5.3.0",
|
|
@@ -71,9 +74,9 @@
|
|
|
71
74
|
"ws": "^8.16.0"
|
|
72
75
|
},
|
|
73
76
|
"optionalDependencies": {
|
|
74
|
-
"@zap-js/darwin-arm64": "0.1.
|
|
75
|
-
"@zap-js/darwin-x64": "0.1.
|
|
76
|
-
"@zap-js/linux-x64": "0.1.
|
|
77
|
+
"@zap-js/darwin-arm64": "0.1.4",
|
|
78
|
+
"@zap-js/darwin-x64": "0.1.4",
|
|
79
|
+
"@zap-js/linux-x64": "0.1.4"
|
|
77
80
|
},
|
|
78
81
|
"peerDependencies": {
|
|
79
82
|
"react": "^18.0.0",
|