owox 0.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/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # OWOX Data Marts CLI
2
+
3
+ A command-line interface for running OWOX Data Marts application. This CLI provides a simple way to start the pre-built OWOX Data Marts server with frontend and backend components.
4
+
5
+ [![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io)
6
+ [![Version](https://img.shields.io/npm/v/owox.svg)](https://npmjs.org/package/owox)
7
+ [![Downloads/week](https://img.shields.io/npm/dw/owox.svg)](https://npmjs.org/package/owox)
8
+
9
+ <!-- toc -->
10
+
11
+ - [Usage](#usage)
12
+ - [Local Development: npm link](#local-development-npm-link)
13
+ - [Commands](#commands)
14
+ - [FAQ: Understanding the `bin` Folder](#faq-understanding-the-bin-folder)
15
+ <!-- tocstop -->
16
+
17
+ # Usage
18
+
19
+ <!-- usage -->
20
+
21
+ ```sh-session
22
+ $ npm install -g owox
23
+ $ owox serve
24
+ Starting OWOX Data Marts...
25
+ Starting in production mode...
26
+ Starting server on port 3000...
27
+ $ owox --help
28
+ USAGE
29
+ $ owox COMMAND
30
+ ...
31
+ ```
32
+
33
+ <!-- usagestop -->
34
+
35
+ # Local Development: npm link
36
+
37
+ For local development and testing of this CLI, especially when it's not published to a public npm registry, you can use `npm link`. This command creates a symbolic link from your local package to the global npm directory, allowing you to run `owox` from any directory on your system as if it were globally installed.
38
+
39
+ ## Using `npm link`
40
+
41
+ To link your local `owox` CLI globally, navigate to the `apps/owox` directory and execute:
42
+
43
+ ```sh-session
44
+ $ npm link
45
+ ```
46
+
47
+ After successfully linking, you can run `owox` commands from any directory:
48
+
49
+ ```sh-session
50
+ $ owox serve --port 8080
51
+ ```
52
+
53
+ ## Using `npm unlink -g owox`
54
+
55
+ If you need to remove the global symbolic link to your local `owox` CLI, navigate to the `apps/owox` directory and execute:
56
+
57
+ ```sh-session
58
+ $ npm unlink -g owox
59
+ ```
60
+
61
+ This will remove the global link, and `owox` will no longer be accessible globally unless re-linked or installed through an npm registry.
62
+
63
+ # Commands
64
+
65
+ <!-- commands -->
66
+
67
+ - [`owox serve`](#owox-serve)
68
+ - [`owox help [COMMAND]`](#owox-help-command)
69
+
70
+ ## `owox serve`
71
+
72
+ Start the OWOX Data Marts application in production mode
73
+
74
+ ```
75
+ USAGE
76
+ $ owox serve [-p <value>]
77
+
78
+ FLAGS
79
+ -p, --port=<value> [default: 3000] Port number for the application
80
+
81
+ DESCRIPTION
82
+ Start the OWOX Data Marts application in production mode
83
+
84
+ EXAMPLES
85
+ $ owox serve
86
+ $ owox serve --port 8080
87
+ $ owox serve -p 3001
88
+ $ PORT=8080 owox serve
89
+ ```
90
+
91
+ _See code: [src/commands/serve.ts](https://github.com/OWOX/owox-data-marts/blob/v0.0.0/src/commands/serve.ts)_
92
+
93
+ ## `owox help [COMMAND]`
94
+
95
+ Display help for owox.
96
+
97
+ ```
98
+ USAGE
99
+ $ owox help [COMMAND...] [-n]
100
+
101
+ ARGUMENTS
102
+ COMMAND... Command to show help for.
103
+
104
+ FLAGS
105
+ -n, --nested-commands Include all nested commands in the output.
106
+
107
+ DESCRIPTION
108
+ Display help for owox.
109
+ ```
110
+
111
+ _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.0.21/src/commands/help.ts)_
112
+
113
+ <!-- commandsstop -->
114
+
115
+ # FAQ
116
+
117
+ This section explains the purpose of the files located in the `bin` directory of the CLI.
118
+
119
+ ### Why are there files in the `bin` folder (`dev.cmd`, `run.js`, `dev.js`, `run.cmd`)? Why do some have a `.cmd` format?
120
+
121
+ The `bin` folder contains the executable entry points for your CLI. Their presence and format are designed to support different operating systems and operational modes (development/production).
122
+
123
+ - **`run.js` (and `run.cmd`)**: These are the primary "production" entry points for your CLI.
124
+
125
+ - **`run.js`**: This is the main executable file for Unix-based systems (Linux, macOS). The `#!/usr/bin/env node` (shebang) line at the beginning tells the operating system to execute this file using Node.js. This file launches the compiled version of your CLI (from the `dist` folder).
126
+ - **`run.cmd`**: This is the equivalent of `run.js` for Windows operating systems. Windows does not understand shebangs, so a separate `.cmd` (or `.bat`) file is required to explicitly instruct the system to execute the Node.js script using the `node` interpreter.
127
+
128
+ - **`dev.js` (and `dev.cmd`)**: These are entry points specifically designed for **development**.
129
+ - **`dev.js`**: This executable file is for Unix-based systems when running in development mode. Notice the shebang line `#!/usr/bin/env -S node --loader ts-node/esm --disable-warning=ExperimentalWarning`. This allows Node.js to run your TypeScript code **without prior compilation** by using the `ts-node/esm` loader. This significantly speeds up development as you don't need to wait for compilation every time you make changes. The `development: true` flag is passed to `@oclif/core` to enable development-specific features.
130
+ - **`dev.cmd`**: This is the Windows equivalent of `dev.js`, also used for running in development mode with `ts-node/esm`.
131
+
132
+ In summary, the `.cmd` files ensure compatibility with Windows, while the `run` and `dev` pairs provide distinct entry points for the "production-ready" (compiled) CLI version and the active development version (directly from TypeScript source files).
package/bin/dev.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node --loader ts-node/esm --no-warnings=ExperimentalWarning "%~dp0\dev" %*
package/bin/dev.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env -S node --loader ts-node/esm --disable-warning=ExperimentalWarning
2
+
3
+ import {execute} from '@oclif/core'
4
+
5
+ await execute({development: true, dir: import.meta.url})
package/bin/run.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\run" %*
package/bin/run.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {execute} from '@oclif/core'
4
+
5
+ await execute({dir: import.meta.url})
@@ -0,0 +1,91 @@
1
+ import { Command } from '@oclif/core';
2
+ /**
3
+ * Command to start the OWOX Data Marts application.
4
+ * Requires @owox/backend to be installed.
5
+ */
6
+ export default class Serve extends Command {
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ port: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ private childProcess?;
13
+ private isShuttingDown;
14
+ /**
15
+ * Main execution method for the serve command
16
+ */
17
+ run(): Promise<void>;
18
+ /**
19
+ * Attaches event handlers to the child process
20
+ */
21
+ private attachProcessEventHandlers;
22
+ /**
23
+ * Creates environment variables for the child process
24
+ * @param port - Port number to set in environment
25
+ * @returns Environment variables object
26
+ */
27
+ private createProcessEnvironment;
28
+ /**
29
+ * Extracts PID from ps command output line
30
+ */
31
+ private extractPidFromProcessLine;
32
+ /**
33
+ * Gets the path to the backend package
34
+ * @returns The full path to the backend entry point
35
+ */
36
+ private getBackendPath;
37
+ /**
38
+ * Handles child process exit events
39
+ * @param code - Exit code
40
+ * @param signal - Exit signal
41
+ */
42
+ private handleProcessExit;
43
+ /**
44
+ * Handles shutdown signals
45
+ * @param signal - The received shutdown signal
46
+ */
47
+ private handleShutdownSignal;
48
+ /**
49
+ * Handles startup errors
50
+ * @param error - The error that occurred during startup
51
+ */
52
+ private handleStartupError;
53
+ /**
54
+ * Checks if the backend package is available
55
+ * @param backendPath - Path to the backend entry point
56
+ * @returns True if backend package exists and is accessible
57
+ */
58
+ private isBackendAvailable;
59
+ /**
60
+ * Kills all processes marked with PROCESS_MARKER
61
+ */
62
+ private killMarkedProcesses;
63
+ /**
64
+ * Kills a process by PID
65
+ */
66
+ private killProcess;
67
+ /**
68
+ * Sets up graceful shutdown handlers for system signals
69
+ */
70
+ private setupGracefulShutdown;
71
+ /**
72
+ * Spawns a child process with the given options
73
+ * @param options - Process spawn options
74
+ */
75
+ private spawnProcess;
76
+ /**
77
+ * Starts the backend application
78
+ * @param backendPath - Path to the backend entry point
79
+ * @param port - Port number to run the application on
80
+ */
81
+ private startBackend;
82
+ /**
83
+ * Waits for processes to cleanup
84
+ */
85
+ private waitForCleanup;
86
+ /**
87
+ * Waits for the child process to complete
88
+ * @returns Promise that resolves when process exits successfully
89
+ */
90
+ private waitForProcessCompletion;
91
+ }
@@ -0,0 +1,265 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { exec, spawn } from 'node:child_process';
3
+ import { existsSync } from 'node:fs';
4
+ import { createRequire } from 'node:module';
5
+ import { join } from 'node:path';
6
+ import { promisify } from 'node:util';
7
+ const execAsync = promisify(exec);
8
+ const require = createRequire(import.meta.url);
9
+ /**
10
+ * Constants for the serve command
11
+ */
12
+ const CONSTANTS = {
13
+ CLEANUP_DELAY_MS: 1000,
14
+ DEFAULT_PORT: 3000,
15
+ PROCESS_MARKER: 'owox-app',
16
+ SHUTDOWN_SIGNALS: ['SIGINT', 'SIGTERM'],
17
+ };
18
+ /**
19
+ * Command to start the OWOX Data Marts application.
20
+ * Requires @owox/backend to be installed.
21
+ */
22
+ export default class Serve extends Command {
23
+ static description = 'Start the OWOX Data Marts application';
24
+ static examples = [
25
+ '<%= config.bin %> serve',
26
+ '<%= config.bin %> serve --port 8080',
27
+ '<%= config.bin %> serve -p 3001',
28
+ '$ PORT=8080 <%= config.bin %> serve',
29
+ ];
30
+ static flags = {
31
+ port: Flags.integer({
32
+ char: 'p',
33
+ default: CONSTANTS.DEFAULT_PORT,
34
+ description: 'Port number for the application',
35
+ env: 'PORT',
36
+ }),
37
+ };
38
+ childProcess;
39
+ isShuttingDown = false;
40
+ /**
41
+ * Main execution method for the serve command
42
+ */
43
+ async run() {
44
+ const { flags } = await this.parse(Serve);
45
+ this.log('🚀 Starting OWOX Data Marts...');
46
+ this.setupGracefulShutdown();
47
+ const backendPath = this.getBackendPath();
48
+ if (!this.isBackendAvailable(backendPath)) {
49
+ this.error('@owox/backend package not found. Please ensure it is installed:\n' +
50
+ 'npm install @owox/backend', { exit: 1 });
51
+ }
52
+ try {
53
+ await this.killMarkedProcesses();
54
+ await this.startBackend(backendPath, flags.port);
55
+ }
56
+ catch (error) {
57
+ this.handleStartupError(error);
58
+ }
59
+ }
60
+ /**
61
+ * Attaches event handlers to the child process
62
+ */
63
+ attachProcessEventHandlers() {
64
+ if (!this.childProcess)
65
+ return;
66
+ this.childProcess.on('error', (error) => {
67
+ if (!this.isShuttingDown) {
68
+ this.error(`Application process error: ${error.message}`);
69
+ }
70
+ });
71
+ this.childProcess.on('exit', (code, signal) => {
72
+ if (!this.isShuttingDown) {
73
+ this.handleProcessExit(code, signal);
74
+ }
75
+ });
76
+ }
77
+ /**
78
+ * Creates environment variables for the child process
79
+ * @param port - Port number to set in environment
80
+ * @returns Environment variables object
81
+ */
82
+ createProcessEnvironment(port) {
83
+ return { ...process.env, PORT: port.toString() };
84
+ }
85
+ /**
86
+ * Extracts PID from ps command output line
87
+ */
88
+ extractPidFromProcessLine(processLine) {
89
+ const pid = processLine.trim().split(/\s+/)[1];
90
+ const numericPid = Number.parseInt(pid, 10);
91
+ return !Number.isNaN(numericPid) && numericPid > 0 ? numericPid : null;
92
+ }
93
+ /**
94
+ * Gets the path to the backend package
95
+ * @returns The full path to the backend entry point
96
+ */
97
+ getBackendPath() {
98
+ try {
99
+ // Try to resolve using Node.js module resolution
100
+ return require.resolve('@owox/backend');
101
+ }
102
+ catch {
103
+ // Fallback to node_modules path for ESM compatibility
104
+ return join(this.config.root, 'node_modules', '@owox/backend', 'dist', 'main.js');
105
+ }
106
+ }
107
+ /**
108
+ * Handles child process exit events
109
+ * @param code - Exit code
110
+ * @param signal - Exit signal
111
+ */
112
+ handleProcessExit(code, signal) {
113
+ if (code !== null && code !== 0) {
114
+ this.error(`Application process exited with code ${code}`);
115
+ }
116
+ else if (signal) {
117
+ this.warn(`Application process terminated by signal ${signal}`);
118
+ }
119
+ }
120
+ /**
121
+ * Handles shutdown signals
122
+ * @param signal - The received shutdown signal
123
+ */
124
+ handleShutdownSignal(signal) {
125
+ if (this.isShuttingDown)
126
+ return;
127
+ this.isShuttingDown = true;
128
+ this.log(`Received ${signal}, shutting down gracefully...`);
129
+ if (this.childProcess?.kill()) {
130
+ this.log('Stopping application...');
131
+ }
132
+ }
133
+ /**
134
+ * Handles startup errors
135
+ * @param error - The error that occurred during startup
136
+ */
137
+ handleStartupError(error) {
138
+ const message = error instanceof Error ? error.message : String(error);
139
+ this.error(`Failed to start application: ${message}`, { exit: 1 });
140
+ }
141
+ /**
142
+ * Checks if the backend package is available
143
+ * @param backendPath - Path to the backend entry point
144
+ * @returns True if backend package exists and is accessible
145
+ */
146
+ isBackendAvailable(backendPath) {
147
+ return existsSync(backendPath);
148
+ }
149
+ /**
150
+ * Kills all processes marked with PROCESS_MARKER
151
+ */
152
+ async killMarkedProcesses() {
153
+ try {
154
+ const { stdout } = await execAsync(`ps -ef | grep "${CONSTANTS.PROCESS_MARKER}" | grep -v grep`);
155
+ if (!stdout.trim()) {
156
+ this.log(`🔍 No previous zombie processes found`);
157
+ return;
158
+ }
159
+ const processes = stdout.trim().split('\n');
160
+ this.warn(`🧹 Found ${processes.length} zombie processes, cleaning up...`);
161
+ const killPromises = processes.map(async (processLine) => {
162
+ const pid = this.extractPidFromProcessLine(processLine);
163
+ if (pid) {
164
+ await this.killProcess(pid);
165
+ }
166
+ });
167
+ await Promise.all(killPromises);
168
+ await this.waitForCleanup();
169
+ this.log(`✅ Zombie cleanup completed`);
170
+ }
171
+ catch {
172
+ // This is normal if no previous processes are found
173
+ this.log(`🔍 No previous zombie processes found`);
174
+ }
175
+ }
176
+ /**
177
+ * Kills a process by PID
178
+ */
179
+ async killProcess(pid) {
180
+ try {
181
+ process.kill(pid, 'SIGTERM');
182
+ this.log(`💀 Killed zombie process PID: ${pid}`);
183
+ }
184
+ catch {
185
+ this.log(`🔍 Process ${pid} already terminated`);
186
+ }
187
+ }
188
+ /**
189
+ * Sets up graceful shutdown handlers for system signals
190
+ */
191
+ setupGracefulShutdown() {
192
+ for (const signal of CONSTANTS.SHUTDOWN_SIGNALS) {
193
+ process.on(signal, () => this.handleShutdownSignal(signal));
194
+ }
195
+ }
196
+ /**
197
+ * Spawns a child process with the given options
198
+ * @param options - Process spawn options
199
+ */
200
+ async spawnProcess(options) {
201
+ const env = this.createProcessEnvironment(options.port);
202
+ this.log(`📦 Starting server on port ${options.port}...`);
203
+ // Add process marker to arguments
204
+ const argsWithMarker = [...options.args, `--${CONSTANTS.PROCESS_MARKER}`];
205
+ this.childProcess = spawn(options.command, argsWithMarker, {
206
+ env,
207
+ stdio: 'inherit',
208
+ });
209
+ if (this.childProcess.pid) {
210
+ this.log(`📦 Server process started with PID: ${this.childProcess.pid}`);
211
+ }
212
+ else {
213
+ throw new Error('Failed to start server process');
214
+ }
215
+ this.attachProcessEventHandlers();
216
+ return this.waitForProcessCompletion();
217
+ }
218
+ /**
219
+ * Starts the backend application
220
+ * @param backendPath - Path to the backend entry point
221
+ * @param port - Port number to run the application on
222
+ */
223
+ async startBackend(backendPath, port) {
224
+ this.log('Starting backend application...');
225
+ const options = {
226
+ args: [backendPath],
227
+ command: 'node',
228
+ port,
229
+ };
230
+ await this.spawnProcess(options);
231
+ }
232
+ /**
233
+ * Waits for processes to cleanup
234
+ */
235
+ async waitForCleanup() {
236
+ return new Promise(resolve => {
237
+ setTimeout(() => resolve(), CONSTANTS.CLEANUP_DELAY_MS);
238
+ });
239
+ }
240
+ /**
241
+ * Waits for the child process to complete
242
+ * @returns Promise that resolves when process exits successfully
243
+ */
244
+ waitForProcessCompletion() {
245
+ return new Promise((resolve, reject) => {
246
+ if (!this.childProcess) {
247
+ reject(new Error('Failed to start child process'));
248
+ return;
249
+ }
250
+ this.childProcess.on('exit', (code) => {
251
+ if (this.isShuttingDown) {
252
+ this.log('Application stopped successfully.');
253
+ resolve();
254
+ }
255
+ else if (code === 0 || code === null) {
256
+ this.log('Application process exited successfully.');
257
+ resolve();
258
+ }
259
+ else {
260
+ reject(new Error(`Application process failed with exit code ${code}`));
261
+ }
262
+ });
263
+ });
264
+ }
265
+ }
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
@@ -0,0 +1,42 @@
1
+ {
2
+ "commands": {
3
+ "serve": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "description": "Start the OWOX Data Marts application",
7
+ "examples": [
8
+ "<%= config.bin %> serve",
9
+ "<%= config.bin %> serve --port 8080",
10
+ "<%= config.bin %> serve -p 3001",
11
+ "$ PORT=8080 <%= config.bin %> serve"
12
+ ],
13
+ "flags": {
14
+ "port": {
15
+ "char": "p",
16
+ "description": "Port number for the application",
17
+ "env": "PORT",
18
+ "name": "port",
19
+ "default": 3000,
20
+ "hasDynamicHelp": false,
21
+ "multiple": false,
22
+ "type": "option"
23
+ }
24
+ },
25
+ "hasDynamicHelp": false,
26
+ "hiddenAliases": [],
27
+ "id": "serve",
28
+ "pluginAlias": "owox",
29
+ "pluginName": "owox",
30
+ "pluginType": "core",
31
+ "strict": true,
32
+ "enableJsonFlag": false,
33
+ "isESM": true,
34
+ "relativePath": [
35
+ "dist",
36
+ "commands",
37
+ "serve.js"
38
+ ]
39
+ }
40
+ },
41
+ "version": "0.0.0"
42
+ }
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "owox",
3
+ "description": "OWOX Data Marts CLI: Simple command-line interface to start the OWOX Data Marts application with backend and frontend components.",
4
+ "version": "0.0.0",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "bin": {
9
+ "owox": "./bin/run.js"
10
+ },
11
+ "bugs": "https://github.com/OWOX/owox-data-marts/issues",
12
+ "dependencies": {
13
+ "@oclif/core": "^4",
14
+ "@oclif/plugin-help": "^6",
15
+ "@oclif/plugin-plugins": "^5",
16
+ "@owox/backend": "0.0.0"
17
+ },
18
+ "devDependencies": {
19
+ "@eslint/compat": "^1",
20
+ "@oclif/prettier-config": "^0.2.1",
21
+ "@oclif/test": "^4",
22
+ "@types/chai": "^4",
23
+ "@types/mocha": "^10",
24
+ "@types/node": "^18",
25
+ "chai": "^4",
26
+ "eslint": "^9",
27
+ "eslint-config-oclif": "^6",
28
+ "eslint-config-prettier": "^10",
29
+ "mocha": "^10",
30
+ "oclif": "^4",
31
+ "ts-node": "^10",
32
+ "typescript": "^5"
33
+ },
34
+ "engines": {
35
+ "node": ">=22.16.0"
36
+ },
37
+ "files": [
38
+ "./bin",
39
+ "./dist",
40
+ "./oclif.manifest.json"
41
+ ],
42
+ "homepage": "https://github.com/OWOX/owox-data-marts",
43
+ "keywords": [
44
+ "data-marts",
45
+ "cli",
46
+ "server",
47
+ "nestjs",
48
+ "backend",
49
+ "owox",
50
+ "data-orchestration",
51
+ "sqlite",
52
+ "mysql",
53
+ "bigquery",
54
+ "athena"
55
+ ],
56
+ "license": "ELv2",
57
+ "main": "dist/index.js",
58
+ "type": "module",
59
+ "oclif": {
60
+ "bin": "owox",
61
+ "dirname": "owox",
62
+ "commands": "./dist/commands",
63
+ "plugins": [
64
+ "@oclif/plugin-help",
65
+ "@oclif/plugin-plugins"
66
+ ],
67
+ "topicSeparator": " "
68
+ },
69
+ "repository": "OWOX/owox-data-marts",
70
+ "scripts": {
71
+ "build": "tsc -b",
72
+ "lint": "eslint",
73
+ "posttest": "npm run lint",
74
+ "prepack": "oclif manifest",
75
+ "prepublishOnly": "npm audit && npm run test && npm run lint",
76
+ "test": "mocha --forbid-only \"test/**/*.test.ts\"",
77
+ "version": "oclif readme && git add README.md"
78
+ },
79
+ "types": "dist/index.d.ts"
80
+ }