@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('Cannot use server-side imports in frontend code');
31
- cliLogger.info('Server imports (@zap-js/server, @zap-js/client/node) should only be used in routes/api/ or routes/ws/');
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
- for (const serverImport of SERVER_ONLY_IMPORTS) {
29
- // Check for both single and double quotes
30
- const patterns = [
31
- `from '${serverImport}'`,
32
- `from "${serverImport}"`,
33
- `require('${serverImport}')`,
34
- `require("${serverImport}")`,
35
- ];
36
- for (const pattern of patterns) {
37
- if (content.includes(pattern)) {
38
- errors.push(`${filePath}: Illegal server import '${serverImport}' in frontend code`);
39
- break; // Only report once per file
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.2",
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.2",
75
- "@zap-js/darwin-x64": "0.1.2",
76
- "@zap-js/linux-x64": "0.1.2"
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",