@depctdev/depct 0.1.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 (3) hide show
  1. package/README.md +34 -0
  2. package/package.json +17 -0
  3. package/src/index.cjs +235 -0
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # depct CLI (MVP)
2
+
3
+ Run Node applications with Depct instrumentation without manually setting `NODE_OPTIONS`.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx @kiranpoudel/depct run -- node server.js
9
+ ```
10
+
11
+ That command auto-injects `depct-loader` and defaults to verbose diagnostics to match your manual setup:
12
+
13
+ - `DEPCT_DEBUG=true` (unless already set)
14
+ - `DEPCT_DEBUG_EVENTS=true` (unless already set)
15
+ - `DEPCT_PROJECT_ROOT=$PWD` (unless already set)
16
+
17
+ ## Options
18
+
19
+ - `--project-root <path>` -> `DEPCT_PROJECT_ROOT`
20
+ - `--project-id <id>` -> `DEPCT_PROJECT_ID`
21
+ - `--server-url <url>` -> `DEPCT_SERVER_URL`
22
+ - `--events-path <path>` -> `DEPCT_EVENTS_PATH`
23
+ - `--flush-max-events <n>` -> `DEPCT_FLUSH_MAX_EVENTS`
24
+ - `--flush-interval-ms <n>` -> `DEPCT_FLUSH_INTERVAL_MS`
25
+ - `--sample-rate <0..1>` -> `DEPCT_SAMPLE_RATE`
26
+ - `--debug` / `--no-debug`
27
+ - `--debug-events` / `--no-debug-events`
28
+
29
+ Examples:
30
+
31
+ ```bash
32
+ npx @kiranpoudel/depct run --project-id my-api -- node server.js
33
+ npx @kiranpoudel/depct run --no-debug-events -- node server.js
34
+ ```
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@depctdev/depct",
3
+ "version": "0.1.0",
4
+ "description": "CLI for running Node apps with Depct instrumentation",
5
+ "bin": {
6
+ "depct": "src/index.cjs"
7
+ },
8
+ "type": "commonjs",
9
+ "main": "./src/index.cjs",
10
+ "files": [
11
+ "src"
12
+ ],
13
+ "license": "MIT",
14
+ "dependencies": {
15
+ "depct-loader": "0.1.0"
16
+ }
17
+ }
package/src/index.cjs ADDED
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+ const { spawn } = require("node:child_process");
6
+
7
+ const HELP = `depct CLI
8
+
9
+ Usage:
10
+ depct run [options] -- <command> [args...]
11
+ depct run [options] <command> [args...]
12
+
13
+ Examples:
14
+ depct run -- node server.js
15
+ depct run --project-id my-api --server-url http://localhost:3100 -- node server.js
16
+ depct run --no-debug-events -- node server.js
17
+
18
+ Options:
19
+ --project-root <path> Set DEPCT_PROJECT_ROOT (default: current directory)
20
+ --project-id <id> Set DEPCT_PROJECT_ID
21
+ --server-url <url> Set DEPCT_SERVER_URL
22
+ --events-path <path> Set DEPCT_EVENTS_PATH
23
+ --flush-max-events <n> Set DEPCT_FLUSH_MAX_EVENTS
24
+ --flush-interval-ms <n> Set DEPCT_FLUSH_INTERVAL_MS
25
+ --sample-rate <0..1> Set DEPCT_SAMPLE_RATE
26
+ --debug / --no-debug Set DEPCT_DEBUG (default: true)
27
+ --debug-events / --no-debug-events
28
+ Set DEPCT_DEBUG_EVENTS (default: true)
29
+ -h, --help Show help
30
+ `;
31
+
32
+ function fail(message) {
33
+ process.stderr.write(`depct: ${message}\n`);
34
+ process.stderr.write("Run `depct --help` for usage.\n");
35
+ process.exit(1);
36
+ }
37
+
38
+ function printHelp(exitCode = 0) {
39
+ process.stdout.write(`${HELP}\n`);
40
+ process.exit(exitCode);
41
+ }
42
+
43
+ function quoteNodeOptionValue(value) {
44
+ const escaped = String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
45
+ return `"${escaped}"`;
46
+ }
47
+
48
+ function resolveLoaderEntry() {
49
+ try {
50
+ return require.resolve("depct-loader");
51
+ } catch {
52
+ const fallback = path.resolve(__dirname, "../../loader/src/index.cjs");
53
+ if (fs.existsSync(fallback)) return fallback;
54
+ }
55
+
56
+ fail(
57
+ "Unable to resolve loader entrypoint. Ensure `depct-loader` is installed or available in the monorepo."
58
+ );
59
+ }
60
+
61
+ function withNodeOptions(existingValue, loaderEntry) {
62
+ const trimmed = String(existingValue || "").trim();
63
+ if (trimmed.includes(loaderEntry)) {
64
+ return trimmed;
65
+ }
66
+
67
+ const requireOpt = `--require ${quoteNodeOptionValue(loaderEntry)}`;
68
+ return trimmed ? `${trimmed} ${requireOpt}` : requireOpt;
69
+ }
70
+
71
+ function inferProjectIdFromPackageJson(rootDir) {
72
+ try {
73
+ const packageJsonPath = path.resolve(rootDir, "package.json");
74
+ const raw = fs.readFileSync(packageJsonPath, "utf8");
75
+ const parsed = JSON.parse(raw);
76
+ if (parsed && typeof parsed.name === "string" && parsed.name.trim()) {
77
+ return parsed.name.trim();
78
+ }
79
+ } catch {
80
+ // no-op
81
+ }
82
+
83
+ return undefined;
84
+ }
85
+
86
+ function parseRunArgs(argv) {
87
+ const options = {
88
+ debug: undefined,
89
+ debugEvents: undefined,
90
+ };
91
+
92
+ let i = 0;
93
+ let command = [];
94
+
95
+ while (i < argv.length) {
96
+ const arg = argv[i];
97
+
98
+ if (arg === "--") {
99
+ command = argv.slice(i + 1);
100
+ break;
101
+ }
102
+
103
+ if (!arg.startsWith("-")) {
104
+ command = argv.slice(i);
105
+ break;
106
+ }
107
+
108
+ const next = argv[i + 1];
109
+ const readValue = (flag) => {
110
+ if (next == null || next.startsWith("-")) {
111
+ fail(`Missing value for ${flag}`);
112
+ }
113
+ i += 1;
114
+ return next;
115
+ };
116
+
117
+ if (arg === "-h" || arg === "--help") {
118
+ printHelp(0);
119
+ } else if (arg === "--project-root") {
120
+ options.projectRoot = readValue(arg);
121
+ } else if (arg === "--project-id") {
122
+ options.projectId = readValue(arg);
123
+ } else if (arg === "--server-url") {
124
+ options.serverUrl = readValue(arg);
125
+ } else if (arg === "--events-path") {
126
+ options.eventsPath = readValue(arg);
127
+ } else if (arg === "--flush-max-events") {
128
+ options.flushMaxEvents = readValue(arg);
129
+ } else if (arg === "--flush-interval-ms") {
130
+ options.flushIntervalMs = readValue(arg);
131
+ } else if (arg === "--sample-rate") {
132
+ options.sampleRate = readValue(arg);
133
+ } else if (arg === "--debug") {
134
+ options.debug = true;
135
+ } else if (arg === "--no-debug") {
136
+ options.debug = false;
137
+ } else if (arg === "--debug-events") {
138
+ options.debugEvents = true;
139
+ } else if (arg === "--no-debug-events") {
140
+ options.debugEvents = false;
141
+ } else {
142
+ fail(`Unknown option: ${arg}`);
143
+ }
144
+
145
+ i += 1;
146
+ }
147
+
148
+ if (command.length === 0) {
149
+ fail("Missing target command. Use `depct run -- node server.js`.");
150
+ }
151
+
152
+ return {
153
+ options,
154
+ command,
155
+ };
156
+ }
157
+
158
+ function applyEnvOptions(baseEnv, options) {
159
+ const env = { ...baseEnv };
160
+ const setIfProvided = (key, value) => {
161
+ if (value !== undefined) env[key] = String(value);
162
+ };
163
+
164
+ setIfProvided("DEPCT_PROJECT_ROOT", options.projectRoot && path.resolve(options.projectRoot));
165
+ setIfProvided("DEPCT_PROJECT_ID", options.projectId);
166
+ setIfProvided("DEPCT_SERVER_URL", options.serverUrl);
167
+ setIfProvided("DEPCT_EVENTS_PATH", options.eventsPath);
168
+ setIfProvided("DEPCT_FLUSH_MAX_EVENTS", options.flushMaxEvents);
169
+ setIfProvided("DEPCT_FLUSH_INTERVAL_MS", options.flushIntervalMs);
170
+ setIfProvided("DEPCT_SAMPLE_RATE", options.sampleRate);
171
+
172
+ if (!env.DEPCT_PROJECT_ROOT) {
173
+ env.DEPCT_PROJECT_ROOT = process.cwd();
174
+ }
175
+
176
+ if (!env.DEPCT_PROJECT_ID) {
177
+ const inferredProjectId = inferProjectIdFromPackageJson(env.DEPCT_PROJECT_ROOT);
178
+ if (inferredProjectId) {
179
+ env.DEPCT_PROJECT_ID = inferredProjectId;
180
+ }
181
+ }
182
+
183
+ // Keep verbose parity with the manual command unless explicitly disabled.
184
+ if (options.debug !== undefined) {
185
+ env.DEPCT_DEBUG = options.debug ? "true" : "false";
186
+ } else if (!env.DEPCT_DEBUG) {
187
+ env.DEPCT_DEBUG = "true";
188
+ }
189
+
190
+ if (options.debugEvents !== undefined) {
191
+ env.DEPCT_DEBUG_EVENTS = options.debugEvents ? "true" : "false";
192
+ } else if (!env.DEPCT_DEBUG_EVENTS) {
193
+ env.DEPCT_DEBUG_EVENTS = "true";
194
+ }
195
+
196
+ return env;
197
+ }
198
+
199
+ function run(argv) {
200
+ if (argv.length === 0 || argv[0] === "-h" || argv[0] === "--help") {
201
+ printHelp(0);
202
+ }
203
+
204
+ const commandName = argv[0];
205
+ if (commandName !== "run") {
206
+ fail(`Unknown command: ${commandName}`);
207
+ }
208
+
209
+ const { options, command } = parseRunArgs(argv.slice(1));
210
+ const [execPath, ...execArgs] = command;
211
+ const loaderEntry = resolveLoaderEntry();
212
+ const env = applyEnvOptions(process.env, options);
213
+ env.NODE_OPTIONS = withNodeOptions(env.NODE_OPTIONS, loaderEntry);
214
+
215
+ const child = spawn(execPath, execArgs, {
216
+ stdio: "inherit",
217
+ cwd: process.cwd(),
218
+ env,
219
+ shell: false,
220
+ });
221
+
222
+ child.on("error", (error) => {
223
+ fail(`Failed to launch command "${execPath}": ${error.message}`);
224
+ });
225
+
226
+ child.on("exit", (code, signal) => {
227
+ if (signal) {
228
+ process.kill(process.pid, signal);
229
+ return;
230
+ }
231
+ process.exit(code ?? 0);
232
+ });
233
+ }
234
+
235
+ run(process.argv.slice(2));