Skip to content
This repository was archived by the owner on Sep 4, 2025. It is now read-only.

Commit c4b9305

Browse files
flyingrobotsclaude
andcommitted
feat(core): Complete Wave 2 - P1.T002, P1.T003, P1.T008
✅ P1.T002: Created data-core pure JavaScript package - 1,384 lines of pure JavaScript logic with zero I/O - SqlGraph.js: SQL dependency analysis and topological sorting - DiffEngine.js: Schema diff calculation and migration generation - PlanCompiler.js: Execution plan compilation with rollback support - Port/adapter pattern with comprehensive JSDoc interfaces ✅ P1.T003: Created data-host-node JavaScript adapters - 1,294 lines of Node.js-specific port implementations - FileSystemAdapter: fs/promises wrapper with error normalization - ProcessAdapter: child_process wrapper with timeout/signal handling - EnvironmentAdapter: process.env wrapper with type conversion - GlobAdapter: Pattern matching and file watching capabilities ✅ P1.T008: Setup AI-powered JSDoc generation pipeline - Git pre-commit hook with husky integration - Automatic JSDoc generation for staged JavaScript files - Fallback heuristic generation when AI unavailable - Manual generation scripts via npm commands - Non-blocking with SKIP_JSDOC escape hatch Next: P1.T004, P1.T006, P1.T007 can run in parallel 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 6c16557 commit c4b9305

19 files changed

+3681
-2
lines changed

.husky/pre-commit

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/bin/sh
2+
3+
# D.A.T.A. Pre-commit Hook with JSDoc Generation
4+
# 1. Generates JSDoc for staged JavaScript files
5+
# 2. Runs ESLint checks for async/await issues
6+
7+
echo "🖖 D.A.T.A. Pre-commit Hook - Ensuring code quality and documentation..."
8+
9+
# Get the root directory of the git repository
10+
GIT_ROOT=$(git rev-parse --show-toplevel)
11+
12+
# Change to the git root directory
13+
cd "$GIT_ROOT" || exit 1
14+
15+
# Get list of staged JavaScript files (exclude node_modules and only include src/, bin/, scripts/)
16+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$' | grep -E '^(src/|bin/|scripts/)' | grep -v node_modules)
17+
18+
if [ -z "$STAGED_FILES" ]; then
19+
echo "ℹ️ No JavaScript files to process"
20+
exit 0
21+
fi
22+
23+
echo "📁 Processing files:"
24+
echo "$STAGED_FILES" | sed 's/^/ - /'
25+
echo ""
26+
27+
# Step 1: Generate JSDoc for staged files
28+
echo "🤖 Generating JSDoc documentation..."
29+
30+
# Check if JSDoc generation should be skipped
31+
if [ "$SKIP_JSDOC" = "true" ]; then
32+
echo "⏭️ Skipping JSDoc generation (SKIP_JSDOC=true)"
33+
else
34+
# Convert file list to space-separated arguments for the JSDoc generator
35+
JSDOC_FILES=""
36+
for file in $STAGED_FILES; do
37+
JSDOC_FILES="$JSDOC_FILES $file"
38+
done
39+
40+
# Run JSDoc generation
41+
node "$GIT_ROOT/scripts/jsdoc/generate-jsdoc.js" $JSDOC_FILES
42+
43+
JSDOC_EXIT=$?
44+
45+
if [ $JSDOC_EXIT -eq 0 ]; then
46+
echo "✅ JSDoc generation completed"
47+
48+
# Re-stage files that may have been updated with JSDoc
49+
for file in $STAGED_FILES; do
50+
if [ -f "$file" ]; then
51+
git add "$file"
52+
fi
53+
done
54+
else
55+
echo "⚠️ JSDoc generation had issues, but continuing with commit"
56+
echo "💡 Tip: Set SKIP_JSDOC=true to skip JSDoc generation"
57+
fi
58+
fi
59+
60+
echo ""
61+
62+
# Step 2: Run ESLint checks
63+
echo "🔍 Running ESLint checks..."
64+
65+
# Run ESLint on staged files
66+
npx eslint $STAGED_FILES
67+
68+
ESLINT_EXIT=$?
69+
70+
if [ $ESLINT_EXIT -eq 0 ]; then
71+
echo "✅ ESLint checks passed!"
72+
else
73+
echo "❌ ESLint found issues. Please fix them before committing."
74+
echo ""
75+
echo "💡 Tip: You can run 'npm run lint:fix' to auto-fix some issues"
76+
echo "💡 Tip: Set SKIP_JSDOC=true if JSDoc generation is causing issues"
77+
exit 1
78+
fi
79+
80+
# Step 3: Check specifically for async/await issues
81+
echo ""
82+
echo "🔍 Checking for floating promises and async issues..."
83+
84+
# Look for common async/await problems in staged files
85+
for file in $STAGED_FILES; do
86+
# Check for .then() without catch
87+
if grep -E '\.then\([^)]*\)[^.]*(;|$)' "$file" > /dev/null 2>&1; then
88+
echo "⚠️ Warning: $file may have unhandled promises (.then without .catch)"
89+
fi
90+
91+
# Check for async functions without await
92+
if grep -E 'async\s+[^{]*\{[^}]*\}' "$file" | grep -v await > /dev/null 2>&1; then
93+
echo "⚠️ Warning: $file may have async functions without await"
94+
fi
95+
done
96+
97+
echo ""
98+
echo "🎯 Pre-commit checks complete! Code quality and documentation ensured."
99+
echo ""
100+
echo "💡 To skip JSDoc generation: SKIP_JSDOC=true git commit"
101+
echo "💡 To manually generate JSDoc: npm run jsdoc:generate"
102+
echo "💡 To generate JSDoc for specific files: npm run jsdoc:files -- file1.js file2.js"
103+
104+
exit 0

package-lock.json

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@
2525
"migrate:squash": "data db migrate squash",
2626
"migrate:dev": "npm run migrate:generate && npm run migrate:test",
2727
"migrate:prod": "npm run migrate:test && npm run migrate:promote",
28-
"migrate:ci": "npm run migrate:verify && npm run migrate:test"
28+
"migrate:ci": "npm run migrate:verify && npm run migrate:test",
29+
"jsdoc:generate": "find src bin scripts -name '*.js' -type f | xargs node ./scripts/jsdoc/generate-jsdoc.js",
30+
"jsdoc:generate:verbose": "find src bin scripts -name '*.js' -type f | xargs node ./scripts/jsdoc/generate-jsdoc.js --verbose",
31+
"jsdoc:generate:force": "find src bin scripts -name '*.js' -type f | xargs node ./scripts/jsdoc/generate-jsdoc.js --force",
32+
"jsdoc:files": "node ./scripts/jsdoc/generate-jsdoc.js",
33+
"jsdoc:dry-run": "find src bin scripts -name '*.js' -type f | xargs node ./scripts/jsdoc/generate-jsdoc.js --dry-run --verbose",
34+
"prepare": "husky"
2935
},
3036
"keywords": [
3137
"supabase",
@@ -65,6 +71,7 @@
6571
"@vitest/coverage-v8": "^2.0.0",
6672
"eslint": "^9.34.0",
6773
"eslint-plugin-promise": "^7.2.1",
74+
"husky": "^9.1.7",
6875
"vitest": "^2.0.0"
6976
},
7077
"engines": {

packages/data-core/example.js

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/**
2+
* Example usage demonstrating dependency injection with data-core package
3+
* This file shows how to implement port adapters and use the core logic
4+
*/
5+
6+
import {
7+
DataCore,
8+
FileSystemPort,
9+
CryptoPort,
10+
ProcessPort,
11+
EnvironmentPort,
12+
SchemaState
13+
} from './index.js';
14+
15+
// Example adapter implementations for Node.js environment
16+
// These would typically be in separate adapter packages
17+
18+
class NodeFileSystemAdapter extends FileSystemPort {
19+
async readFile(path) {
20+
const fs = await import('fs/promises');
21+
return fs.readFile(path, 'utf8');
22+
}
23+
24+
async glob(patterns, cwd = process.cwd()) {
25+
const glob = await import('glob');
26+
const results = [];
27+
for (const pattern of patterns) {
28+
const matches = await glob.glob(pattern, { cwd });
29+
results.push(...matches);
30+
}
31+
return results;
32+
}
33+
}
34+
35+
class NodeCryptoAdapter extends CryptoPort {
36+
hash(data, algorithm = 'sha256') {
37+
const crypto = await import('crypto');
38+
return crypto.createHash(algorithm).update(data).digest('hex');
39+
}
40+
}
41+
42+
class NodeProcessAdapter extends ProcessPort {
43+
async spawn(command, args = [], options = {}) {
44+
const { spawn } = await import('child_process');
45+
const { promisify } = await import('util');
46+
47+
return new Promise((resolve, reject) => {
48+
const child = spawn(command, args, options);
49+
let stdout = '';
50+
let stderr = '';
51+
52+
child.stdout?.on('data', (data) => stdout += data.toString());
53+
child.stderr?.on('data', (data) => stderr += data.toString());
54+
55+
child.on('close', (exitCode) => {
56+
resolve({ stdout, stderr, exitCode });
57+
});
58+
59+
child.on('error', reject);
60+
});
61+
}
62+
}
63+
64+
class NodeEnvironmentAdapter extends EnvironmentPort {
65+
get(key, defaultValue) {
66+
return process.env[key] ?? defaultValue;
67+
}
68+
69+
has(key) {
70+
return key in process.env;
71+
}
72+
}
73+
74+
// Example usage function
75+
export async function demonstrateCoreUsage() {
76+
console.log('🚀 D.A.T.A. Core Package Demonstration');
77+
console.log('=====================================\n');
78+
79+
// 1. Create adapter instances (dependency injection)
80+
const fileSystemPort = new NodeFileSystemAdapter();
81+
const cryptoPort = new NodeCryptoAdapter();
82+
const processPort = new NodeProcessAdapter();
83+
const environmentPort = new NodeEnvironmentAdapter();
84+
85+
// 2. Initialize DataCore with injected dependencies
86+
const dataCore = new DataCore(
87+
fileSystemPort,
88+
cryptoPort,
89+
processPort,
90+
environmentPort
91+
);
92+
93+
console.log('✅ DataCore initialized with injected adapters\n');
94+
95+
// 3. Display package information
96+
const packageInfo = dataCore.getPackageInfo();
97+
console.log('📦 Package Information:');
98+
console.log(` Name: ${packageInfo.name}`);
99+
console.log(` Version: ${packageInfo.version}`);
100+
console.log(` Type: ${packageInfo.type}`);
101+
console.log(` I/O Dependencies: ${packageInfo.ioDepencencies}`);
102+
console.log(` Port Interfaces: ${packageInfo.portInterfaces.join(', ')}`);
103+
console.log(` Core Engines: ${packageInfo.coreEngines.join(', ')}\n`);
104+
105+
// 4. Demonstrate schema state creation
106+
console.log('🏗️ Creating sample schema states...');
107+
const currentSchema = dataCore.createSampleSchema('current');
108+
const targetSchema = dataCore.createSampleSchema('target');
109+
110+
// Modify target schema to demonstrate diff calculation
111+
targetSchema.addObject('tables', 'comments', {
112+
columns: ['id', 'post_id', 'content', 'created_at'],
113+
sql: 'CREATE TABLE comments (id SERIAL PRIMARY KEY, post_id INTEGER REFERENCES posts(id), content TEXT, created_at TIMESTAMP)'
114+
});
115+
116+
targetSchema.addObject('indexes', 'idx_comments_post_id', {
117+
table: 'comments',
118+
columns: ['post_id'],
119+
sql: 'CREATE INDEX idx_comments_post_id ON comments(post_id)'
120+
});
121+
122+
console.log(` Current schema checksum: ${currentSchema.checksum}`);
123+
console.log(` Target schema checksum: ${targetSchema.checksum}\n`);
124+
125+
// 5. Generate migration plan
126+
console.log('🔄 Generating migration plan...');
127+
const migrationResult = dataCore.generateMigrationPlan(currentSchema, targetSchema, {
128+
enableRollback: true,
129+
parallelExecution: false
130+
});
131+
132+
console.log(` Operations generated: ${migrationResult.operations.length}`);
133+
console.log(` Execution steps: ${migrationResult.executionPlan.stepCount}`);
134+
console.log(` Estimated time: ${Math.round(migrationResult.executionPlan.estimatedTime / 1000)}s`);
135+
console.log(` Plan valid: ${migrationResult.validation.valid}`);
136+
137+
if (migrationResult.validation.warnings.length > 0) {
138+
console.log(` Warnings: ${migrationResult.validation.warnings.length}`);
139+
}
140+
141+
if (migrationResult.rollbackPlan) {
142+
console.log(` Rollback steps: ${migrationResult.rollbackPlan.stepCount}`);
143+
}
144+
145+
console.log('\n📋 Migration Operations:');
146+
migrationResult.operations.forEach((op, index) => {
147+
const destructiveFlag = op.isDestructive ? '⚠️ ' : '✅ ';
148+
console.log(` ${index + 1}. ${destructiveFlag}${op.objectName}: ${op.sql.substring(0, 60)}...`);
149+
});
150+
151+
console.log('\n🎯 Example demonstrates:');
152+
console.log(' ✓ Pure JavaScript implementation (zero I/O dependencies)');
153+
console.log(' ✓ Dependency injection via port/adapter pattern');
154+
console.log(' ✓ Runtime validation using instanceof checks');
155+
console.log(' ✓ Comprehensive JSDoc annotations');
156+
console.log(' ✓ ESM exports with clean API surface');
157+
console.log(' ✓ Business logic separation from I/O concerns\n');
158+
159+
return migrationResult;
160+
}
161+
162+
// Export individual adapters for reuse
163+
export {
164+
NodeFileSystemAdapter,
165+
NodeCryptoAdapter,
166+
NodeProcessAdapter,
167+
NodeEnvironmentAdapter
168+
};

0 commit comments

Comments
 (0)