ready-to-ship 1.0.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.
@@ -0,0 +1,152 @@
1
+ const path = require('path');
2
+ const { findFiles, readFile, fileExists } = require('../utils/fileHelpers');
3
+ const { error, success, verdict, printIssues } = require('../utils/logHelpers');
4
+ const { extractRoutes } = require('../utils/parseHelpers');
5
+
6
+ /**
7
+ * Validate API endpoints and health checks
8
+ */
9
+ async function validate(projectPath = process.cwd()) {
10
+ const issues = [];
11
+ const warnings = [];
12
+
13
+ // Find route files
14
+ const routePatterns = [
15
+ '**/routes/**/*.{js,ts,jsx,tsx}',
16
+ '**/routes.{js,ts,jsx,tsx}',
17
+ '**/api/**/*.{js,ts,jsx,tsx}',
18
+ '**/src/**/*.{js,ts,jsx,tsx}'
19
+ ];
20
+
21
+ let routeFiles = [];
22
+ for (const pattern of routePatterns) {
23
+ const files = await findFiles(pattern, projectPath);
24
+ routeFiles.push(...files);
25
+ }
26
+
27
+ // If no route files found, try to find main app file
28
+ if (routeFiles.length === 0) {
29
+ const mainFiles = await findFiles('**/{app,server,index,main}.{js,ts}', projectPath);
30
+ routeFiles.push(...mainFiles);
31
+ }
32
+
33
+ if (routeFiles.length === 0) {
34
+ warnings.push('No route files found. Make sure your project structure is standard.');
35
+ verdict(false, 'API: NOT READY (No routes found)');
36
+ return { passed: false, issues, warnings };
37
+ }
38
+
39
+ // Check for health endpoint
40
+ let hasHealthEndpoint = false;
41
+ const healthPatterns = ['/health', '/healthz', '/ping', '/status', '/api/health'];
42
+
43
+ for (const filePath of routeFiles) {
44
+ const code = await readFile(filePath);
45
+ if (!code) continue;
46
+
47
+ const routes = extractRoutes(code);
48
+
49
+ // Check if any route matches health patterns
50
+ const healthRoutes = routes.filter(route =>
51
+ healthPatterns.some(pattern => route.path === pattern || route.path.startsWith(pattern + '/'))
52
+ );
53
+
54
+ if (healthRoutes.length > 0) {
55
+ hasHealthEndpoint = true;
56
+ break;
57
+ }
58
+ }
59
+
60
+ if (!hasHealthEndpoint) {
61
+ issues.push('/health endpoint missing (recommended for monitoring and load balancers)');
62
+ }
63
+
64
+ // Check route consistency
65
+ const allRoutes = [];
66
+ for (const filePath of routeFiles) {
67
+ const code = await readFile(filePath);
68
+ if (!code) continue;
69
+
70
+ const routes = extractRoutes(code);
71
+ routes.forEach(route => {
72
+ allRoutes.push({
73
+ ...route,
74
+ file: path.relative(projectPath, filePath)
75
+ });
76
+ });
77
+ }
78
+
79
+ // Group routes by path
80
+ const routesByPath = {};
81
+ allRoutes.forEach(route => {
82
+ if (!routesByPath[route.path]) {
83
+ routesByPath[route.path] = [];
84
+ }
85
+ routesByPath[route.path].push(route.method);
86
+ });
87
+
88
+ // Auth/action endpoints that don't need GET (false positive prevention)
89
+ const actionEndpoints = [
90
+ 'login', 'logout', 'register', 'signin', 'signout', 'signup',
91
+ 'refresh-token', 'reset-password', 'forgot-password', 'verify',
92
+ 'authenticate', 'authorize', 'callback', 'webhook'
93
+ ];
94
+
95
+ const isActionEndpoint = (path) => {
96
+ const pathLower = path.toLowerCase();
97
+ return actionEndpoints.some(action => pathLower.includes(action));
98
+ };
99
+
100
+ // Check for common REST inconsistencies
101
+ Object.keys(routesByPath).forEach(path => {
102
+ const methods = routesByPath[path];
103
+
104
+ // Skip action endpoints (login, register, etc.)
105
+ if (isActionEndpoint(path)) {
106
+ return;
107
+ }
108
+
109
+ // If POST exists, usually should have GET for the collection
110
+ if (methods.includes('POST') && !methods.includes('GET')) {
111
+ const collectionPath = path.replace(/\/[^/]+$/, '');
112
+ if (collectionPath !== path && !routesByPath[collectionPath]?.includes('GET')) {
113
+ warnings.push(`POST ${path} exists but no GET endpoint for collection`);
114
+ }
115
+ }
116
+
117
+ // If PUT/PATCH exists, usually should have GET for the resource
118
+ if ((methods.includes('PUT') || methods.includes('PATCH')) && !methods.includes('GET')) {
119
+ warnings.push(`${methods.find(m => ['PUT', 'PATCH'].includes(m))} ${path} exists but no GET endpoint`);
120
+ }
121
+ });
122
+
123
+ // Print results
124
+ console.log('\n🔹 API VALIDATION\n');
125
+
126
+ if (hasHealthEndpoint) {
127
+ success('Health endpoint found');
128
+ }
129
+
130
+ if (issues.length === 0 && warnings.length === 0) {
131
+ success('API structure looks good');
132
+ verdict(true, 'API: READY');
133
+ return { passed: true, issues: [], warnings, routes: allRoutes };
134
+ }
135
+
136
+ printIssues(issues, 'error');
137
+ printIssues(warnings, 'warning');
138
+
139
+ verdict(false, 'API: NOT READY');
140
+
141
+ return {
142
+ passed: issues.length === 0,
143
+ issues,
144
+ warnings,
145
+ routes: allRoutes
146
+ };
147
+ }
148
+
149
+ module.exports = {
150
+ validate
151
+ };
152
+
@@ -0,0 +1,152 @@
1
+ const path = require('path');
2
+ const { getAllFiles, readFile } = require('../utils/fileHelpers');
3
+ const { error, success, verdict, printIssues } = require('../utils/logHelpers');
4
+ const { extractRoutes, hasAuthMiddleware, extractJWTExpiry } = require('../utils/parseHelpers');
5
+
6
+ /**
7
+ * Validate authentication and route protection
8
+ */
9
+ async function validate(projectPath = process.cwd()) {
10
+ const issues = [];
11
+ const warnings = [];
12
+ const vulnerableRoutes = [];
13
+
14
+ // Find route files
15
+ const routePatterns = [
16
+ '**/routes/**/*.{js,ts,jsx,tsx}',
17
+ '**/routes.{js,ts,jsx,tsx}',
18
+ '**/api/**/*.{js,ts,jsx,tsx}',
19
+ '**/controllers/**/*.{js,ts,jsx,tsx}',
20
+ '**/src/**/*.{js,ts,jsx,tsx}'
21
+ ];
22
+
23
+ let routeFiles = [];
24
+ for (const pattern of routePatterns) {
25
+ const files = await require('../utils/fileHelpers').findFiles(pattern, projectPath);
26
+ routeFiles.push(...files);
27
+ }
28
+
29
+ // If no route files found, try to find main app file
30
+ if (routeFiles.length === 0) {
31
+ const mainFiles = await require('../utils/fileHelpers').findFiles('**/{app,server,index,main}.{js,ts}', projectPath);
32
+ routeFiles.push(...mainFiles);
33
+ }
34
+
35
+ if (routeFiles.length === 0) {
36
+ warnings.push('No route files found. Make sure your project structure is standard.');
37
+ verdict(false, 'AUTH: NOT READY (No routes found)');
38
+ return { passed: false, issues, warnings, vulnerableRoutes: [] };
39
+ }
40
+
41
+ // Analyze each route file
42
+ for (const filePath of routeFiles) {
43
+ const code = await readFile(filePath);
44
+ if (!code) continue;
45
+
46
+ const routes = extractRoutes(code);
47
+ const lines = code.split('\n');
48
+
49
+ routes.forEach(route => {
50
+ const routePath = route.path;
51
+ const method = route.method;
52
+ const isSensitive = isSensitiveRoute(routePath);
53
+
54
+ if (isSensitive) {
55
+ // Check if route has auth middleware
56
+ const hasAuth = hasAuthMiddleware(code, route.line);
57
+
58
+ if (!hasAuth) {
59
+ vulnerableRoutes.push({
60
+ method,
61
+ path: routePath,
62
+ file: path.relative(projectPath, filePath),
63
+ line: route.line
64
+ });
65
+ issues.push(`Route ${method} ${routePath} missing auth middleware (${path.relative(projectPath, filePath)})`);
66
+ }
67
+ }
68
+ });
69
+
70
+ // Check JWT expiry configuration
71
+ const jwtExpiry = extractJWTExpiry(code);
72
+ if (jwtExpiry !== null) {
73
+ const maxRecommendedExpiry = 7 * 24 * 60 * 60; // 7 days in seconds
74
+ const oneYearInSeconds = 365 * 24 * 60 * 60;
75
+
76
+ if (jwtExpiry > oneYearInSeconds) {
77
+ issues.push(`JWT expiry too long: ${formatDuration(jwtExpiry)} (recommended: < 7 days)`);
78
+ } else if (jwtExpiry > maxRecommendedExpiry) {
79
+ warnings.push(`JWT expiry is ${formatDuration(jwtExpiry)} (recommended: < 7 days)`);
80
+ }
81
+ }
82
+ }
83
+
84
+ // Print results
85
+ console.log('\n🔹 AUTH VALIDATION\n');
86
+
87
+ if (issues.length === 0 && warnings.length === 0) {
88
+ success('All routes are properly protected');
89
+ verdict(true, 'AUTH: READY');
90
+ return { passed: true, issues: [], warnings: [], vulnerableRoutes: [] };
91
+ }
92
+
93
+ printIssues(issues, 'error');
94
+ printIssues(warnings, 'warning');
95
+
96
+ if (vulnerableRoutes.length > 0) {
97
+ console.log('\n📋 Vulnerable Routes:');
98
+ vulnerableRoutes.forEach(route => {
99
+ error(`${route.method} ${route.path} (${route.file}:${route.line})`);
100
+ });
101
+ }
102
+
103
+ verdict(false, 'AUTH: NOT READY');
104
+
105
+ return {
106
+ passed: issues.length === 0,
107
+ issues,
108
+ warnings,
109
+ vulnerableRoutes
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Check if route is sensitive and requires auth
115
+ */
116
+ function isSensitiveRoute(path) {
117
+ const sensitivePatterns = [
118
+ /^\/admin/i,
119
+ /^\/api\/admin/i,
120
+ /\/users/i,
121
+ /\/profile/i,
122
+ /\/settings/i,
123
+ /\/account/i,
124
+ /\/dashboard/i,
125
+ /\/delete/i,
126
+ /\/update/i,
127
+ /\/create/i,
128
+ /\/edit/i,
129
+ /\/password/i,
130
+ /\/auth\/change/i,
131
+ /\/api\/v\d+\/.*(?:user|admin|auth|profile|settings)/i
132
+ ];
133
+
134
+ return sensitivePatterns.some(pattern => pattern.test(path));
135
+ }
136
+
137
+ /**
138
+ * Format duration in seconds to human-readable string
139
+ */
140
+ function formatDuration(seconds) {
141
+ if (seconds < 60) return `${seconds} seconds`;
142
+ if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes`;
143
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours`;
144
+ if (seconds < 2592000) return `${Math.floor(seconds / 86400)} days`;
145
+ if (seconds < 31536000) return `${Math.floor(seconds / 2592000)} months`;
146
+ return `${Math.floor(seconds / 31536000)} years`;
147
+ }
148
+
149
+ module.exports = {
150
+ validate
151
+ };
152
+
@@ -0,0 +1,178 @@
1
+ const path = require('path');
2
+ const { findFiles, readFile, parseEnvFile, fileExists } = require('../utils/fileHelpers');
3
+ const { error, success, verdict, printIssues } = require('../utils/logHelpers');
4
+
5
+ /**
6
+ * Validate database configuration
7
+ */
8
+ async function validate(projectPath = process.cwd()) {
9
+ const issues = [];
10
+ const warnings = [];
11
+
12
+ // Check for database connection strings in .env
13
+ const envPath = path.join(projectPath, '.env');
14
+ let envVars = {};
15
+
16
+ if (await fileExists(envPath)) {
17
+ envVars = await parseEnvFile(envPath);
18
+ }
19
+
20
+ // Detect database type from env vars
21
+ const dbVars = Object.keys(envVars).filter(key =>
22
+ /DATABASE|DB|MONGO|POSTGRES|MYSQL|REDIS/i.test(key)
23
+ );
24
+
25
+ let hasDbConfig = dbVars.length > 0;
26
+ let dbType = null;
27
+
28
+ if (envVars.DATABASE_URL) {
29
+ const dbUrl = envVars.DATABASE_URL.toLowerCase();
30
+ if (dbUrl.includes('mongodb')) dbType = 'MongoDB';
31
+ else if (dbUrl.includes('postgres')) dbType = 'PostgreSQL';
32
+ else if (dbUrl.includes('mysql')) dbType = 'MySQL';
33
+ else if (dbUrl.includes('redis')) dbType = 'Redis';
34
+ }
35
+
36
+ // Check package.json for database drivers
37
+ const packageJsonPath = path.join(projectPath, 'package.json');
38
+ const packageJsonContent = await readFile(packageJsonPath);
39
+ let packageJson = {};
40
+
41
+ if (packageJsonContent) {
42
+ try {
43
+ packageJson = JSON.parse(packageJsonContent);
44
+ } catch (e) {
45
+ // Invalid JSON
46
+ }
47
+ }
48
+
49
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
50
+ const dbPackages = {
51
+ 'mongoose': 'MongoDB',
52
+ 'mongodb': 'MongoDB',
53
+ 'pg': 'PostgreSQL',
54
+ 'mysql2': 'MySQL',
55
+ 'mysql': 'MySQL',
56
+ 'redis': 'Redis',
57
+ 'ioredis': 'Redis',
58
+ 'prisma': 'Prisma ORM',
59
+ 'sequelize': 'Sequelize ORM',
60
+ 'typeorm': 'TypeORM'
61
+ };
62
+
63
+ let detectedDbPackage = null;
64
+ Object.keys(dbPackages).forEach(pkg => {
65
+ if (deps[pkg]) {
66
+ detectedDbPackage = dbPackages[pkg];
67
+ if (!dbType) dbType = dbPackages[pkg];
68
+ }
69
+ });
70
+
71
+ // Check for database connection files
72
+ const dbFiles = await findFiles('**/{db,database,config}/**/*.{js,ts}', projectPath);
73
+ const connectionFiles = await findFiles('**/*connection*.{js,ts}', projectPath);
74
+ const modelFiles = await findFiles('**/{models,schemas}/**/*.{js,ts}', projectPath);
75
+
76
+ let hasConnectionFile = dbFiles.length > 0 || connectionFiles.length > 0;
77
+ let hasModels = modelFiles.length > 0;
78
+
79
+ // Check for connection error handling
80
+ let hasConnectionHandling = false;
81
+ for (const filePath of [...dbFiles, ...connectionFiles].slice(0, 10)) {
82
+ const code = await readFile(filePath);
83
+ if (code) {
84
+ if (/catch|error|on\('error'\)/i.test(code)) {
85
+ hasConnectionHandling = true;
86
+ break;
87
+ }
88
+ }
89
+ }
90
+
91
+ // Validation checks
92
+ if (!hasDbConfig && !detectedDbPackage) {
93
+ warnings.push('No database configuration detected');
94
+ } else {
95
+ if (dbType) {
96
+ success(`Database type detected: ${dbType}`);
97
+ }
98
+
99
+ // Check for connection string security
100
+ if (envVars.DATABASE_URL) {
101
+ const dbUrl = envVars.DATABASE_URL;
102
+ if (dbUrl.includes('localhost') || dbUrl.includes('127.0.0.1')) {
103
+ warnings.push('Database URL points to localhost (ensure production uses remote database)');
104
+ }
105
+
106
+ // Check for credentials in URL (should be in env)
107
+ if (!dbUrl.startsWith('${') && dbUrl.includes('@') && !dbUrl.includes('://')) {
108
+ // Might be okay, but check format
109
+ }
110
+ }
111
+
112
+ // Check for missing connection handling
113
+ if (!hasConnectionHandling && hasConnectionFile) {
114
+ issues.push('Database connection error handling not detected');
115
+ }
116
+
117
+ // Check for connection pooling
118
+ let hasPooling = false;
119
+ for (const filePath of [...dbFiles, ...connectionFiles].slice(0, 10)) {
120
+ const code = await readFile(filePath);
121
+ if (code && /pool|pooling|max.*connection/i.test(code)) {
122
+ hasPooling = true;
123
+ break;
124
+ }
125
+ }
126
+
127
+ if (!hasPooling && detectedDbPackage) {
128
+ warnings.push('Connection pooling not detected (recommended for production)');
129
+ }
130
+ }
131
+
132
+ // Check for migration files
133
+ const migrationFiles = await findFiles('**/{migrations,migrate}/**/*.{js,ts,sql}', projectPath);
134
+ if (migrationFiles.length === 0 && detectedDbPackage) {
135
+ warnings.push('No migration files detected (recommended for database versioning)');
136
+ }
137
+
138
+ // Print results
139
+ console.log('\n🔹 DATABASE VALIDATION\n');
140
+
141
+ if (hasDbConfig || detectedDbPackage) {
142
+ if (hasConnectionFile) success('Database connection file found');
143
+ if (hasModels) success('Database models/schemas found');
144
+ if (hasConnectionHandling) success('Connection error handling found');
145
+ }
146
+
147
+ if (issues.length === 0 && warnings.length === 0 && (hasDbConfig || detectedDbPackage)) {
148
+ success('Database configuration looks good');
149
+ verdict(true, 'DATABASE: READY');
150
+ return { passed: true, issues: [], warnings: [] };
151
+ }
152
+
153
+ if (!hasDbConfig && !detectedDbPackage) {
154
+ verdict(true, 'DATABASE: SKIPPED (No database detected)');
155
+ return { passed: true, issues: [], warnings: [], skipped: true };
156
+ }
157
+
158
+ printIssues(issues, 'error');
159
+ printIssues(warnings, 'warning');
160
+
161
+ if (issues.length === 0) {
162
+ verdict(true, 'DATABASE: READY (with warnings)');
163
+ } else {
164
+ verdict(false, 'DATABASE: NOT READY');
165
+ }
166
+
167
+ return {
168
+ passed: issues.length === 0,
169
+ issues,
170
+ warnings,
171
+ dbType
172
+ };
173
+ }
174
+
175
+ module.exports = {
176
+ validate
177
+ };
178
+
@@ -0,0 +1,151 @@
1
+ const path = require('path');
2
+ const { readFile, fileExists } = require('../utils/fileHelpers');
3
+ const { error, success, verdict, printIssues, warning } = require('../utils/logHelpers');
4
+
5
+ /**
6
+ * Validate dependencies
7
+ */
8
+ async function validate(projectPath = process.cwd()) {
9
+ const issues = [];
10
+ const warnings = [];
11
+
12
+ const packageJsonPath = path.join(projectPath, 'package.json');
13
+ const packageLockPath = path.join(projectPath, 'package-lock.json');
14
+ const yarnLockPath = path.join(projectPath, 'yarn.lock');
15
+
16
+ // Check for package.json
17
+ if (!await fileExists(packageJsonPath)) {
18
+ issues.push('package.json not found');
19
+ verdict(false, 'DEPENDENCIES: NOT READY');
20
+ return { passed: false, issues, warnings: [] };
21
+ }
22
+
23
+ const packageJsonContent = await readFile(packageJsonPath);
24
+ let packageJson = {};
25
+
26
+ try {
27
+ packageJson = JSON.parse(packageJsonContent);
28
+ } catch (e) {
29
+ issues.push('package.json is malformed');
30
+ verdict(false, 'DEPENDENCIES: NOT READY');
31
+ return { passed: false, issues, warnings: [] };
32
+ }
33
+
34
+ // Check for lock file
35
+ const hasLockFile = await fileExists(packageLockPath) || await fileExists(yarnLockPath);
36
+ if (!hasLockFile) {
37
+ warnings.push('Lock file (package-lock.json or yarn.lock) not found (recommended for reproducible builds)');
38
+ }
39
+
40
+ // Check for critical dependencies
41
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
42
+ const criticalDeps = {
43
+ 'express': 'Express.js',
44
+ 'fastify': 'Fastify',
45
+ 'koa': 'Koa',
46
+ 'nestjs': 'NestJS'
47
+ };
48
+
49
+ let hasFramework = false;
50
+ Object.keys(criticalDeps).forEach(dep => {
51
+ if (deps[dep]) {
52
+ hasFramework = true;
53
+ }
54
+ });
55
+
56
+ if (!hasFramework) {
57
+ warnings.push('No major web framework detected (Express, Fastify, Koa, NestJS)');
58
+ }
59
+
60
+ // Check for outdated packages (basic check)
61
+ const nodeVersion = process.version;
62
+ const nodeMajor = parseInt(nodeVersion.replace('v', '').split('.')[0]);
63
+
64
+ if (nodeMajor < 14) {
65
+ warnings.push(`Node.js version ${nodeVersion} is outdated (recommended: >= 14.0.0)`);
66
+ }
67
+
68
+ // Check for common security-related packages
69
+ const securityPackages = ['helmet', 'cors', 'express-rate-limit', 'bcrypt', 'jsonwebtoken'];
70
+ const missingSecurity = securityPackages.filter(pkg => !deps[pkg]);
71
+
72
+ if (missingSecurity.length > 0) {
73
+ warnings.push(`Missing security packages: ${missingSecurity.join(', ')}`);
74
+ }
75
+
76
+ // Check for vulnerable patterns in dependencies
77
+ const vulnerablePatterns = {
78
+ 'express': { min: '4.17.0', reason: 'Older versions have security vulnerabilities' },
79
+ 'lodash': { min: '4.17.21', reason: 'Older versions have security vulnerabilities' }
80
+ };
81
+
82
+ Object.keys(vulnerablePatterns).forEach(pkg => {
83
+ if (deps[pkg]) {
84
+ const version = deps[pkg].replace(/[\^~]/, '');
85
+ const pattern = vulnerablePatterns[pkg];
86
+ // Basic version check (simplified)
87
+ if (version && version.includes('4.') && pkg === 'express') {
88
+ warnings.push(`${pkg} version might be outdated - ${pattern.reason}`);
89
+ }
90
+ }
91
+ });
92
+
93
+ // Check for too many dependencies (maintenance burden)
94
+ const totalDeps = Object.keys(deps).length;
95
+ if (totalDeps > 100) {
96
+ warnings.push(`Large number of dependencies (${totalDeps}) - consider reviewing for unused packages`);
97
+ }
98
+
99
+ // Check for dev dependencies in production
100
+ if (packageJson.scripts && packageJson.scripts.start) {
101
+ const startScript = packageJson.scripts.start;
102
+ if (startScript.includes('nodemon') || startScript.includes('ts-node-dev')) {
103
+ warnings.push('Development tools in start script (use production tools in production)');
104
+ }
105
+ }
106
+
107
+ // Try to run npm audit (if available)
108
+ let auditIssues = [];
109
+ try {
110
+ // Check if npm audit is available (don't run it, just check)
111
+ const hasNpmAudit = true; // Assume available
112
+ if (hasNpmAudit) {
113
+ warnings.push('Run "npm audit" to check for known vulnerabilities');
114
+ }
115
+ } catch (e) {
116
+ // npm audit not available or failed
117
+ }
118
+
119
+ // Print results
120
+ console.log('\n🔹 DEPENDENCIES VALIDATION\n');
121
+
122
+ if (hasLockFile) success('Lock file found');
123
+ if (hasFramework) success('Web framework detected');
124
+
125
+ if (issues.length === 0 && warnings.length === 0) {
126
+ success('Dependencies look good');
127
+ verdict(true, 'DEPENDENCIES: READY');
128
+ return { passed: true, issues: [], warnings: [] };
129
+ }
130
+
131
+ printIssues(issues, 'error');
132
+ printIssues(warnings, 'warning');
133
+
134
+ if (issues.length === 0) {
135
+ verdict(true, 'DEPENDENCIES: READY (with warnings)');
136
+ } else {
137
+ verdict(false, 'DEPENDENCIES: NOT READY');
138
+ }
139
+
140
+ return {
141
+ passed: issues.length === 0,
142
+ issues,
143
+ warnings,
144
+ totalDependencies: totalDeps
145
+ };
146
+ }
147
+
148
+ module.exports = {
149
+ validate
150
+ };
151
+