cerber-core 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.
Files changed (67) hide show
  1. package/.cerber-example/BIBLE.md +132 -0
  2. package/.cerber-example/CERBER_LAW.md +200 -0
  3. package/.cerber-example/connections/contracts/booking-to-pricing.json +44 -0
  4. package/.cerber-example/connections/contracts/pricing-to-booking.json +37 -0
  5. package/.cerber-example/modules/booking-calendar/MODULE.md +225 -0
  6. package/.cerber-example/modules/booking-calendar/contract.json +106 -0
  7. package/.cerber-example/modules/booking-calendar/dependencies.json +8 -0
  8. package/.cerber-example/modules/pricing-engine/MODULE.md +160 -0
  9. package/.cerber-example/modules/pricing-engine/contract.json +64 -0
  10. package/.cerber-example/modules/pricing-engine/dependencies.json +8 -0
  11. package/CHANGELOG.md +68 -0
  12. package/LICENSE +21 -0
  13. package/README.md +1379 -0
  14. package/bin/cerber +105 -0
  15. package/bin/cerber-focus +31 -0
  16. package/bin/cerber-guardian +90 -0
  17. package/bin/cerber-health +113 -0
  18. package/bin/cerber-morning +19 -0
  19. package/bin/cerber-repair +21 -0
  20. package/dist/cerber/index.d.ts +47 -0
  21. package/dist/cerber/index.d.ts.map +1 -0
  22. package/dist/cerber/index.js +154 -0
  23. package/dist/cerber/index.js.map +1 -0
  24. package/dist/guardian/index.d.ts +70 -0
  25. package/dist/guardian/index.d.ts.map +1 -0
  26. package/dist/guardian/index.js +271 -0
  27. package/dist/guardian/index.js.map +1 -0
  28. package/dist/index.d.ts +9 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +9 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/types.d.ts +76 -0
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/types.js +5 -0
  35. package/dist/types.js.map +1 -0
  36. package/examples/backend-schema.ts +72 -0
  37. package/examples/frontend-schema.ts +67 -0
  38. package/examples/health-checks.ts +196 -0
  39. package/examples/solo-integration/README.md +457 -0
  40. package/examples/solo-integration/package.json +47 -0
  41. package/examples/team-integration/README.md +347 -0
  42. package/examples/team-integration/package.json +23 -0
  43. package/package.json +104 -0
  44. package/solo/README.md +258 -0
  45. package/solo/config/performance-budget.json +53 -0
  46. package/solo/config/solo-contract.json +71 -0
  47. package/solo/lib/feature-flags.ts +177 -0
  48. package/solo/scripts/cerber-auto-repair.js +260 -0
  49. package/solo/scripts/cerber-daily-check.js +282 -0
  50. package/solo/scripts/cerber-dashboard.js +191 -0
  51. package/solo/scripts/cerber-deps-health.js +247 -0
  52. package/solo/scripts/cerber-docs-sync.js +304 -0
  53. package/solo/scripts/cerber-flags-check.js +229 -0
  54. package/solo/scripts/cerber-performance-budget.js +271 -0
  55. package/solo/scripts/cerber-rollback.js +229 -0
  56. package/solo/scripts/cerber-snapshot.js +319 -0
  57. package/team/README.md +327 -0
  58. package/team/config/team-contract.json +27 -0
  59. package/team/lib/module-system.ts +157 -0
  60. package/team/scripts/cerber-add-module.sh +195 -0
  61. package/team/scripts/cerber-connections-check.sh +186 -0
  62. package/team/scripts/cerber-focus.sh +170 -0
  63. package/team/scripts/cerber-module-check.sh +165 -0
  64. package/team/scripts/cerber-team-morning.sh +210 -0
  65. package/team/templates/BIBLE_TEMPLATE.md +52 -0
  66. package/team/templates/CONNECTION_TEMPLATE.json +20 -0
  67. package/team/templates/MODULE_TEMPLATE.md +60 -0
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Cerber SOLO - Performance Budget Enforcer
5
+ *
6
+ * Extends Cerber Core with automation for solo developers
7
+ *
8
+ * Enforces:
9
+ * - Total bundle size limit
10
+ * - Largest chunk limit
11
+ * - Image size constraints
12
+ * - Blocks build if exceeded
13
+ *
14
+ * @author Stefan Pitek
15
+ * @copyright 2026 Stefan Pitek
16
+ * @license MIT
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+ const { execSync } = require('child_process');
22
+
23
+ console.log('📊 Cerber SOLO - Performance Budget Enforcer\n');
24
+
25
+ // Load configuration
26
+ function loadConfig() {
27
+ const configPath = path.join(__dirname, '../config/performance-budget.json');
28
+
29
+ if (!fs.existsSync(configPath)) {
30
+ console.log('⚠️ No performance-budget.json found, using defaults');
31
+ return {
32
+ bundleSize: { max: 500, warning: 400, unit: 'KB' },
33
+ largestChunk: { max: 250, warning: 200, unit: 'KB' },
34
+ images: { max: 200, unit: 'KB' }
35
+ };
36
+ }
37
+
38
+ try {
39
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
40
+ } catch (error) {
41
+ console.log(`❌ Error loading config: ${error.message}`);
42
+ process.exit(1);
43
+ }
44
+ }
45
+
46
+ const config = loadConfig();
47
+ const violations = [];
48
+ const warnings = [];
49
+
50
+ /**
51
+ * Get file size in KB
52
+ */
53
+ function getFileSizeKB(filePath) {
54
+ const stats = fs.statSync(filePath);
55
+ return stats.size / 1024;
56
+ }
57
+
58
+ /**
59
+ * Find all JS bundle files
60
+ */
61
+ function findBundleFiles() {
62
+ const distDirs = ['dist', 'build', 'out', '.next'];
63
+
64
+ for (const dir of distDirs) {
65
+ const distPath = path.join(process.cwd(), dir);
66
+ if (fs.existsSync(distPath)) {
67
+ console.log(`📁 Found build directory: ${dir}`);
68
+ return { dir, path: distPath };
69
+ }
70
+ }
71
+
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * Recursively find all files matching pattern
77
+ */
78
+ function findFiles(dir, pattern, results = []) {
79
+ if (!fs.existsSync(dir)) return results;
80
+
81
+ const files = fs.readdirSync(dir);
82
+
83
+ files.forEach(file => {
84
+ const filePath = path.join(dir, file);
85
+ const stat = fs.statSync(filePath);
86
+
87
+ if (stat.isDirectory()) {
88
+ findFiles(filePath, pattern, results);
89
+ } else if (pattern.test(file)) {
90
+ results.push(filePath);
91
+ }
92
+ });
93
+
94
+ return results;
95
+ }
96
+
97
+ /**
98
+ * Check bundle sizes
99
+ */
100
+ function checkBundleSizes() {
101
+ console.log('\n📦 Checking bundle sizes...\n');
102
+
103
+ const buildInfo = findBundleFiles();
104
+
105
+ if (!buildInfo) {
106
+ console.log('⚠️ No build directory found (dist/build/out/.next)');
107
+ console.log(' Run your build command first (npm run build)');
108
+ return;
109
+ }
110
+
111
+ // Find all JS files
112
+ const jsFiles = findFiles(buildInfo.path, /\.js$/);
113
+
114
+ if (jsFiles.length === 0) {
115
+ console.log('ℹ️ No JavaScript bundles found');
116
+ return;
117
+ }
118
+
119
+ let totalSize = 0;
120
+ let largestFile = null;
121
+ let largestSize = 0;
122
+
123
+ jsFiles.forEach(file => {
124
+ const sizeKB = getFileSizeKB(file);
125
+ totalSize += sizeKB;
126
+
127
+ if (sizeKB > largestSize) {
128
+ largestSize = sizeKB;
129
+ largestFile = file;
130
+ }
131
+
132
+ const relativePath = path.relative(process.cwd(), file);
133
+
134
+ // Check individual chunk size
135
+ if (config.largestChunk && sizeKB > config.largestChunk.max) {
136
+ console.log(` 🔴 ${relativePath}: ${sizeKB.toFixed(1)} KB (EXCEEDS ${config.largestChunk.max} KB)`);
137
+ violations.push({
138
+ type: 'chunk-size',
139
+ file: relativePath,
140
+ size: sizeKB,
141
+ limit: config.largestChunk.max
142
+ });
143
+ } else if (config.largestChunk && sizeKB > config.largestChunk.warning) {
144
+ console.log(` 🟡 ${relativePath}: ${sizeKB.toFixed(1)} KB (warning: ${config.largestChunk.warning} KB)`);
145
+ warnings.push({
146
+ type: 'chunk-size',
147
+ file: relativePath,
148
+ size: sizeKB,
149
+ limit: config.largestChunk.warning
150
+ });
151
+ } else {
152
+ console.log(` ✅ ${relativePath}: ${sizeKB.toFixed(1)} KB`);
153
+ }
154
+ });
155
+
156
+ console.log(`\n📊 Total bundle size: ${totalSize.toFixed(1)} KB`);
157
+
158
+ if (config.bundleSize) {
159
+ if (totalSize > config.bundleSize.max) {
160
+ console.log(` 🔴 EXCEEDS limit of ${config.bundleSize.max} KB`);
161
+ violations.push({
162
+ type: 'total-size',
163
+ size: totalSize,
164
+ limit: config.bundleSize.max
165
+ });
166
+ } else if (totalSize > config.bundleSize.warning) {
167
+ console.log(` 🟡 Warning: Approaching limit (${config.bundleSize.warning} KB)`);
168
+ warnings.push({
169
+ type: 'total-size',
170
+ size: totalSize,
171
+ limit: config.bundleSize.warning
172
+ });
173
+ } else {
174
+ console.log(` ✅ Within budget (${config.bundleSize.max} KB)`);
175
+ }
176
+ }
177
+
178
+ if (largestFile) {
179
+ console.log(`\n📦 Largest chunk: ${path.relative(process.cwd(), largestFile)} (${largestSize.toFixed(1)} KB)`);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Check image sizes
185
+ */
186
+ function checkImageSizes() {
187
+ console.log('\n🖼️ Checking image sizes...\n');
188
+
189
+ const imageDirs = ['public', 'static', 'assets', 'src/assets'];
190
+ let imagesFound = false;
191
+
192
+ imageDirs.forEach(dir => {
193
+ const dirPath = path.join(process.cwd(), dir);
194
+ if (!fs.existsSync(dirPath)) return;
195
+
196
+ const images = findFiles(dirPath, /\.(jpg|jpeg|png|gif|webp|svg)$/i);
197
+
198
+ if (images.length === 0) return;
199
+
200
+ imagesFound = true;
201
+
202
+ images.forEach(file => {
203
+ const sizeKB = getFileSizeKB(file);
204
+ const relativePath = path.relative(process.cwd(), file);
205
+
206
+ if (config.images && sizeKB > config.images.max) {
207
+ console.log(` 🔴 ${relativePath}: ${sizeKB.toFixed(1)} KB (EXCEEDS ${config.images.max} KB)`);
208
+ violations.push({
209
+ type: 'image-size',
210
+ file: relativePath,
211
+ size: sizeKB,
212
+ limit: config.images.max
213
+ });
214
+ } else if (sizeKB > 100) {
215
+ console.log(` 🟡 ${relativePath}: ${sizeKB.toFixed(1)} KB`);
216
+ } else {
217
+ console.log(` ✅ ${relativePath}: ${sizeKB.toFixed(1)} KB`);
218
+ }
219
+ });
220
+ });
221
+
222
+ if (!imagesFound) {
223
+ console.log('ℹ️ No images found in common directories');
224
+ }
225
+ }
226
+
227
+ // Run checks
228
+ checkBundleSizes();
229
+ checkImageSizes();
230
+
231
+ // Summary
232
+ console.log('\n' + '='.repeat(60));
233
+
234
+ if (violations.length > 0) {
235
+ console.log('\n❌ PERFORMANCE BUDGET VIOLATED\n');
236
+ violations.forEach(v => {
237
+ if (v.type === 'total-size') {
238
+ console.log(` 🔴 Total bundle size: ${v.size.toFixed(1)} KB (limit: ${v.limit} KB)`);
239
+ } else if (v.type === 'chunk-size') {
240
+ console.log(` 🔴 ${v.file}: ${v.size.toFixed(1)} KB (limit: ${v.limit} KB)`);
241
+ } else if (v.type === 'image-size') {
242
+ console.log(` 🔴 ${v.file}: ${v.size.toFixed(1)} KB (limit: ${v.limit} KB)`);
243
+ }
244
+ });
245
+
246
+ console.log('\n💡 Recommendations:');
247
+ console.log(' - Enable code splitting');
248
+ console.log(' - Use dynamic imports');
249
+ console.log(' - Compress images');
250
+ console.log(' - Remove unused dependencies');
251
+ console.log(' - Analyze bundle with webpack-bundle-analyzer');
252
+
253
+ console.log('\n='.repeat(60));
254
+ process.exit(1);
255
+ } else if (warnings.length > 0) {
256
+ console.log('\n⚠️ Performance warnings detected\n');
257
+ warnings.forEach(w => {
258
+ if (w.type === 'total-size') {
259
+ console.log(` 🟡 Total bundle size: ${w.size.toFixed(1)} KB (warning: ${w.limit} KB)`);
260
+ } else if (w.type === 'chunk-size') {
261
+ console.log(` 🟡 ${w.file}: ${w.size.toFixed(1)} KB (warning: ${w.limit} KB)`);
262
+ }
263
+ });
264
+
265
+ console.log('\n='.repeat(60));
266
+ process.exit(0);
267
+ } else {
268
+ console.log('\n✅ All performance budgets met!\n');
269
+ console.log('='.repeat(60));
270
+ process.exit(0);
271
+ }
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Cerber SOLO - Smart Rollback
5
+ *
6
+ * Extends Cerber Core with automation for solo developers
7
+ *
8
+ * Features:
9
+ * - Rollback specific file from commit
10
+ * - Conflict detection
11
+ * - Safety checks
12
+ * - Dry-run mode
13
+ *
14
+ * Usage: node cerber-rollback.js <commit-hash> --file=path/to/file.ts [--dry-run]
15
+ *
16
+ * @author Stefan Pitek
17
+ * @copyright 2026 Stefan Pitek
18
+ * @license MIT
19
+ */
20
+
21
+ const { execSync } = require('child_process');
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+
25
+ console.log('⏪ Cerber SOLO - Smart Rollback\n');
26
+
27
+ // Parse arguments
28
+ const args = process.argv.slice(2);
29
+
30
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
31
+ console.log('Usage: node cerber-rollback.js <commit-hash> --file=path/to/file.ts [--dry-run]\n');
32
+ console.log('Options:');
33
+ console.log(' <commit-hash> Git commit hash to rollback from');
34
+ console.log(' --file=<path> File path to rollback');
35
+ console.log(' --dry-run Show what would be done without making changes');
36
+ console.log(' --help, -h Show this help message\n');
37
+ console.log('Examples:');
38
+ console.log(' node cerber-rollback.js abc123 --file=src/api/users.ts');
39
+ console.log(' node cerber-rollback.js abc123 --file=src/api/users.ts --dry-run');
40
+ process.exit(0);
41
+ }
42
+
43
+ let commitHash = null;
44
+ let filePath = null;
45
+ let isDryRun = false;
46
+
47
+ args.forEach(arg => {
48
+ if (arg === '--dry-run') {
49
+ isDryRun = true;
50
+ } else if (arg.startsWith('--file=')) {
51
+ filePath = arg.split('=')[1];
52
+ } else if (!arg.startsWith('--')) {
53
+ commitHash = arg;
54
+ }
55
+ });
56
+
57
+ if (!commitHash) {
58
+ console.error('❌ Error: Commit hash is required\n');
59
+ console.log('Usage: node cerber-rollback.js <commit-hash> --file=path/to/file.ts\n');
60
+ process.exit(1);
61
+ }
62
+
63
+ if (!filePath) {
64
+ console.error('❌ Error: File path is required\n');
65
+ console.log('Usage: node cerber-rollback.js <commit-hash> --file=path/to/file.ts\n');
66
+ process.exit(1);
67
+ }
68
+
69
+ console.log(`Commit: ${commitHash}`);
70
+ console.log(`File: ${filePath}`);
71
+ console.log(`Mode: ${isDryRun ? 'DRY RUN' : 'LIVE'}\n`);
72
+
73
+ /**
74
+ * Check if we're in a git repository
75
+ */
76
+ function checkGitRepo() {
77
+ try {
78
+ execSync('git rev-parse --git-dir', { stdio: 'pipe' });
79
+ console.log('✅ Git repository detected\n');
80
+ return true;
81
+ } catch (error) {
82
+ console.error('❌ Error: Not a git repository\n');
83
+ return false;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Verify commit exists
89
+ */
90
+ function verifyCommit(hash) {
91
+ try {
92
+ execSync(`git cat-file -t ${hash}`, { stdio: 'pipe' });
93
+ const commitMsg = execSync(`git log -1 --pretty=format:"%s" ${hash}`, {
94
+ encoding: 'utf8',
95
+ stdio: 'pipe'
96
+ });
97
+ console.log(`✅ Commit found: "${commitMsg}"\n`);
98
+ return true;
99
+ } catch (error) {
100
+ console.error(`❌ Error: Commit ${hash} not found\n`);
101
+ return false;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Check if file exists in commit
107
+ */
108
+ function checkFileInCommit(hash, file) {
109
+ try {
110
+ execSync(`git cat-file -e ${hash}:${file}`, { stdio: 'pipe' });
111
+ console.log(`✅ File exists in commit ${hash}\n`);
112
+ return true;
113
+ } catch (error) {
114
+ console.error(`❌ Error: File ${file} does not exist in commit ${hash}\n`);
115
+ return false;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Check for uncommitted changes
121
+ */
122
+ function checkUncommittedChanges(file) {
123
+ try {
124
+ const status = execSync(`git status --porcelain ${file}`, {
125
+ encoding: 'utf8',
126
+ stdio: 'pipe'
127
+ });
128
+
129
+ if (status.trim()) {
130
+ console.log(`⚠️ Warning: File ${file} has uncommitted changes\n`);
131
+ console.log(' Current changes will be lost if you proceed!\n');
132
+ return true;
133
+ }
134
+
135
+ console.log(`✅ No uncommitted changes for ${file}\n`);
136
+ return false;
137
+ } catch (error) {
138
+ return false;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Show file diff
144
+ */
145
+ function showDiff(hash, file) {
146
+ try {
147
+ console.log('📊 Changes that will be rolled back:\n');
148
+ console.log('─'.repeat(60));
149
+
150
+ const diff = execSync(`git diff HEAD ${hash} -- ${file}`, {
151
+ encoding: 'utf8',
152
+ maxBuffer: 10 * 1024 * 1024
153
+ });
154
+
155
+ if (diff) {
156
+ // Show first 30 lines
157
+ const lines = diff.split('\n').slice(0, 30);
158
+ console.log(lines.join('\n'));
159
+
160
+ if (diff.split('\n').length > 30) {
161
+ console.log('\n... (diff truncated, showing first 30 lines) ...');
162
+ }
163
+ } else {
164
+ console.log('(no changes - file is identical)');
165
+ }
166
+
167
+ console.log('─'.repeat(60));
168
+ console.log();
169
+ } catch (error) {
170
+ console.log('⚠️ Could not generate diff\n');
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Perform rollback
176
+ */
177
+ function rollbackFile(hash, file) {
178
+ try {
179
+ console.log(`🔄 Rolling back ${file} to ${hash}...\n`);
180
+
181
+ if (!isDryRun) {
182
+ execSync(`git checkout ${hash} -- ${file}`, { stdio: 'inherit' });
183
+ console.log('✅ Rollback successful!\n');
184
+ console.log('💡 Changes are staged. Run `git status` to review.\n');
185
+ console.log(' To commit: git commit -m "rollback: revert changes to ${file}"');
186
+ console.log(' To undo: git checkout HEAD -- ${file}\n');
187
+ } else {
188
+ console.log('🔍 DRY RUN: Would execute: git checkout ${hash} -- ${file}\n');
189
+ }
190
+
191
+ return true;
192
+ } catch (error) {
193
+ console.error(`❌ Error during rollback: ${error.message}\n`);
194
+ return false;
195
+ }
196
+ }
197
+
198
+ // Run checks and rollback
199
+ if (!checkGitRepo()) {
200
+ process.exit(1);
201
+ }
202
+
203
+ if (!verifyCommit(commitHash)) {
204
+ process.exit(1);
205
+ }
206
+
207
+ if (!checkFileInCommit(commitHash, filePath)) {
208
+ process.exit(1);
209
+ }
210
+
211
+ const hasUncommitted = checkUncommittedChanges(filePath);
212
+
213
+ if (hasUncommitted && !isDryRun) {
214
+ console.log('⚠️ SAFETY CHECK: Uncommitted changes detected\n');
215
+ console.log(' Options:');
216
+ console.log(' 1. Commit your current changes first');
217
+ console.log(' 2. Stash your changes: git stash');
218
+ console.log(' 3. Run with --dry-run to preview\n');
219
+ console.log('❌ Rollback aborted for safety\n');
220
+ process.exit(1);
221
+ }
222
+
223
+ showDiff(commitHash, filePath);
224
+
225
+ const success = rollbackFile(commitHash, filePath);
226
+
227
+ console.log('='.repeat(60));
228
+
229
+ process.exit(success ? 0 : 1);