@oalacea/demon 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.
package/lib/config.js ADDED
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Demon - Configuration Utilities
3
+ *
4
+ * Handles project configuration and settings.
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ /**
11
+ * Get project root directory
12
+ */
13
+ function getProjectRoot() {
14
+ let currentDir = process.cwd();
15
+
16
+ while (currentDir !== path.parse(currentDir).root) {
17
+ const pkgPath = path.join(currentDir, 'package.json');
18
+
19
+ if (fs.existsSync(pkgPath)) {
20
+ return currentDir;
21
+ }
22
+
23
+ currentDir = path.dirname(currentDir);
24
+ }
25
+
26
+ return process.cwd();
27
+ }
28
+
29
+ /**
30
+ * Read package.json
31
+ */
32
+ function readPackageJson(projectDir = getProjectRoot()) {
33
+ const pkgPath = path.join(projectDir, 'package.json');
34
+
35
+ if (!fs.existsSync(pkgPath)) {
36
+ return null;
37
+ }
38
+
39
+ try {
40
+ return JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Get all dependencies
48
+ */
49
+ function getDependencies(projectDir = getProjectRoot()) {
50
+ const pkg = readPackageJson(projectDir);
51
+
52
+ if (!pkg) {
53
+ return {};
54
+ }
55
+
56
+ return {
57
+ ...(pkg.dependencies || {}),
58
+ ...(pkg.devDependencies || {}),
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Check if a dependency exists
64
+ */
65
+ function hasDependency(depName, projectDir = getProjectRoot()) {
66
+ const deps = getDependencies(projectDir);
67
+ return depName in deps;
68
+ }
69
+
70
+ /**
71
+ * Get dependency version
72
+ */
73
+ function getDependencyVersion(depName, projectDir = getProjectRoot()) {
74
+ const deps = getDependencies(projectDir);
75
+ return deps[depName] || null;
76
+ }
77
+
78
+ /**
79
+ * Check if project uses TypeScript
80
+ */
81
+ function isTypeScriptProject(projectDir = getProjectRoot()) {
82
+ return (
83
+ hasDependency('typescript', projectDir) ||
84
+ fs.existsSync(path.join(projectDir, 'tsconfig.json'))
85
+ );
86
+ }
87
+
88
+ /**
89
+ * Get test runner
90
+ */
91
+ function getTestRunner(projectDir = getProjectRoot()) {
92
+ const deps = getDependencies(projectDir);
93
+
94
+ if (deps.vitest) return 'vitest';
95
+ if (deps.jest) return 'jest';
96
+ if (deps.mocha) return 'mocha';
97
+ if (deps.jasmine) return 'jasmine';
98
+
99
+ // Check config files
100
+ const vitestConfig = path.join(projectDir, 'vitest.config.ts');
101
+ const jestConfig = path.join(projectDir, 'jest.config.js');
102
+
103
+ if (fs.existsSync(vitestConfig)) return 'vitest';
104
+ if (fs.existsSync(jestConfig)) return 'jest';
105
+
106
+ return 'vitest'; // Default
107
+ }
108
+
109
+ /**
110
+ * Get framework
111
+ */
112
+ function getFramework(projectDir = getProjectRoot()) {
113
+ const deps = getDependencies(projectDir);
114
+
115
+ if (deps.next) return 'Next.js';
116
+ if (deps['@remix-run/node']) return 'Remix';
117
+ if (deps['@sveltejs/kit']) return 'SvelteKit';
118
+ if (deps.nuxt) return 'Nuxt';
119
+ if (deps.astro) return 'Astro';
120
+ if (deps.gatsby) return 'Gatsby';
121
+ if (deps.vite) return 'Vite';
122
+ if (deps.express) return 'Express';
123
+ if (deps.nest) return 'NestJS';
124
+
125
+ return 'Unknown';
126
+ }
127
+
128
+ /**
129
+ * Get database config
130
+ */
131
+ function getDatabaseConfig(projectDir = getProjectRoot()) {
132
+ const deps = getDependencies(projectDir);
133
+
134
+ if (deps['@prisma/client']) {
135
+ return {
136
+ type: 'Prisma',
137
+ schemaPath: path.join(projectDir, 'prisma', 'schema.prisma'),
138
+ };
139
+ }
140
+
141
+ if (deps['drizzle-orm']) {
142
+ return {
143
+ type: 'Drizzle',
144
+ };
145
+ }
146
+
147
+ if (deps.typeorm) {
148
+ return {
149
+ type: 'TypeORM',
150
+ };
151
+ }
152
+
153
+ if (deps.mongoose) {
154
+ return {
155
+ type: 'Mongoose',
156
+ };
157
+ }
158
+
159
+ return null;
160
+ }
161
+
162
+ /**
163
+ * Get environment variable
164
+ */
165
+ function getEnvVar(key, defaultValue = null) {
166
+ return process.env[key] || defaultValue;
167
+ }
168
+
169
+ /**
170
+ * Get Demon config from package.json
171
+ */
172
+ function getDemonConfig(projectDir = getProjectRoot()) {
173
+ const pkg = readPackageJson(projectDir);
174
+
175
+ if (!pkg || !pkg.demon) {
176
+ return {};
177
+ }
178
+
179
+ return pkg.demon;
180
+ }
181
+
182
+ /**
183
+ * Get source directory
184
+ */
185
+ function getSourceDir(projectDir = getProjectRoot()) {
186
+ const srcDir = path.join(projectDir, 'src');
187
+ const appDir = path.join(projectDir, 'app');
188
+
189
+ if (fs.existsSync(appDir)) {
190
+ return 'app';
191
+ }
192
+
193
+ if (fs.existsSync(srcDir)) {
194
+ return 'src';
195
+ }
196
+
197
+ return '.';
198
+ }
199
+
200
+ /**
201
+ * Get test directory
202
+ */
203
+ function getTestDir(projectDir = getProjectRoot()) {
204
+ const testsDir = path.join(projectDir, 'tests');
205
+ const testDir = path.join(projectDir, 'test');
206
+ const srcTestsDir = path.join(getSourceDir(projectDir), 'tests');
207
+
208
+ if (fs.existsSync(testsDir)) {
209
+ return 'tests';
210
+ }
211
+
212
+ if (fs.existsSync(testDir)) {
213
+ return 'test';
214
+ }
215
+
216
+ if (fs.existsSync(srcTestsDir)) {
217
+ return path.join(getSourceDir(projectDir), 'tests');
218
+ }
219
+
220
+ return 'tests';
221
+ }
222
+
223
+ module.exports = {
224
+ getProjectRoot,
225
+ readPackageJson,
226
+ getDependencies,
227
+ hasDependency,
228
+ getDependencyVersion,
229
+ isTypeScriptProject,
230
+ getTestRunner,
231
+ getFramework,
232
+ getDatabaseConfig,
233
+ getEnvVar,
234
+ getDemonConfig,
235
+ getSourceDir,
236
+ getTestDir,
237
+ };
package/lib/docker.js ADDED
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Demon - Docker Management
3
+ *
4
+ * Handles Docker container operations for the Demon toolkit.
5
+ */
6
+
7
+ const { execSync } = require('child_process');
8
+
9
+ const CONFIG = {
10
+ imageName: 'demon-tools',
11
+ containerName: 'demon-tools',
12
+ dockerfile: path.join(__dirname, '..', 'bin', 'Dockerfile'),
13
+ };
14
+
15
+ /**
16
+ * Check if Docker is running
17
+ */
18
+ function isDockerRunning() {
19
+ try {
20
+ execSync('docker info', { stdio: 'pipe' });
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Check if image exists
29
+ */
30
+ function imageExists() {
31
+ try {
32
+ const output = execSync(`docker images -q ${CONFIG.imageName}`, { stdio: 'pipe' });
33
+ return output.trim().length > 0;
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Build the Docker image
41
+ */
42
+ function buildImage(options = {}) {
43
+ const cmd = `docker build -t ${CONFIG.imageName} -f "${CONFIG.dockerfile}" "${path.dirname(CONFIG.dockerfile)}"`;
44
+
45
+ return execSync(cmd, {
46
+ stdio: options.silent ? 'pipe' : 'inherit',
47
+ timeout: options.timeout || 600000, // 10 minutes
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Check if container is running
53
+ */
54
+ function isContainerRunning() {
55
+ try {
56
+ const output = execSync(
57
+ `docker ps --filter "name=^${CONFIG.containerName}$" --format "{{.Names}}"`,
58
+ { stdio: 'pipe' }
59
+ );
60
+ return output.trim() === CONFIG.containerName;
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Check if container exists
68
+ */
69
+ function containerExists() {
70
+ try {
71
+ const output = execSync(
72
+ `docker ps -a --filter "name=^${CONFIG.containerName}$" --format "{{.Names}}"`,
73
+ { stdio: 'pipe' }
74
+ );
75
+ return output.trim() === CONFIG.containerName;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Start container
83
+ */
84
+ function startContainer() {
85
+ const cmd = `docker start ${CONFIG.containerName}`;
86
+ return execSync(cmd, { stdio: 'pipe' });
87
+ }
88
+
89
+ /**
90
+ * Create container
91
+ */
92
+ function createContainer(options = {}) {
93
+ const isLinux = process.platform === 'linux';
94
+ const networkFlag = isLinux ? '--network=host' : '';
95
+ const cmd = `docker run -d --name ${CONFIG.containerName} ${networkFlag} ${CONFIG.imageName}`;
96
+
97
+ return execSync(cmd, { stdio: 'pipe' });
98
+ }
99
+
100
+ /**
101
+ * Stop container
102
+ */
103
+ function stopContainer() {
104
+ try {
105
+ return execSync(`docker stop ${CONFIG.containerName}`, { stdio: 'pipe' });
106
+ } catch {
107
+ return null;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Remove container
113
+ */
114
+ function removeContainer() {
115
+ try {
116
+ return execSync(`docker rm -f ${CONFIG.containerName}`, { stdio: 'pipe' });
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Execute command in container
124
+ */
125
+ function execInContainer(command, options = {}) {
126
+ const cmd = `docker exec ${CONFIG.containerName} ${command}`;
127
+
128
+ try {
129
+ return {
130
+ success: true,
131
+ output: execSync(cmd, {
132
+ encoding: 'utf-8',
133
+ stdio: options.silent ? 'pipe' : 'inherit',
134
+ timeout: options.timeout || 60000,
135
+ }),
136
+ };
137
+ } catch (error) {
138
+ return {
139
+ success: false,
140
+ error: error.message,
141
+ output: error.stdout || '',
142
+ };
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Get container logs
148
+ */
149
+ function getLogs(options = {}) {
150
+ const tail = options.tail || 100;
151
+ const cmd = `docker logs --tail ${tail} ${CONFIG.containerName}`;
152
+
153
+ try {
154
+ return execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' });
155
+ } catch {
156
+ return '';
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Setup container (build, start, or create as needed)
162
+ */
163
+ async function setupContainer(options = {}) {
164
+ // Check Docker
165
+ if (!isDockerRunning()) {
166
+ throw new Error('Docker is not running');
167
+ }
168
+
169
+ // Build image if needed
170
+ if (!imageExists()) {
171
+ if (options.onBuildStart) {
172
+ options.onBuildStart();
173
+ }
174
+ buildImage({ silent: options.silent });
175
+ if (options.onBuildComplete) {
176
+ options.onBuildComplete();
177
+ }
178
+ }
179
+
180
+ // Start or create container
181
+ if (isContainerRunning()) {
182
+ return { status: 'running' };
183
+ }
184
+
185
+ if (containerExists()) {
186
+ startContainer();
187
+ return { status: 'started' };
188
+ }
189
+
190
+ createContainer();
191
+ return { status: 'created' };
192
+ }
193
+
194
+ module.exports = {
195
+ isDockerRunning,
196
+ imageExists,
197
+ buildImage,
198
+ isContainerRunning,
199
+ containerExists,
200
+ startContainer,
201
+ createContainer,
202
+ stopContainer,
203
+ removeContainer,
204
+ execInContainer,
205
+ getLogs,
206
+ setupContainer,
207
+ };