@9apes/cli 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 (53) hide show
  1. package/README.md +145 -0
  2. package/dist/command.d.ts +8 -0
  3. package/dist/command.d.ts.map +1 -0
  4. package/dist/command.js +90 -0
  5. package/dist/command.js.map +1 -0
  6. package/dist/config.d.ts +7 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +37 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/index-dev.d.ts +3 -0
  11. package/dist/index-dev.d.ts.map +1 -0
  12. package/dist/index-dev.js +13 -0
  13. package/dist/index-dev.js.map +1 -0
  14. package/dist/index-local.d.ts +3 -0
  15. package/dist/index-local.d.ts.map +1 -0
  16. package/dist/index-local.js +13 -0
  17. package/dist/index-local.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +11 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/inject.d.ts +14 -0
  23. package/dist/inject.d.ts.map +1 -0
  24. package/dist/inject.js +161 -0
  25. package/dist/inject.js.map +1 -0
  26. package/dist/sourcemap.d.ts +48 -0
  27. package/dist/sourcemap.d.ts.map +1 -0
  28. package/dist/sourcemap.js +262 -0
  29. package/dist/sourcemap.js.map +1 -0
  30. package/dist/status.d.ts +17 -0
  31. package/dist/status.d.ts.map +1 -0
  32. package/dist/status.js +72 -0
  33. package/dist/status.js.map +1 -0
  34. package/dist/upload.d.ts +25 -0
  35. package/dist/upload.d.ts.map +1 -0
  36. package/dist/upload.js +119 -0
  37. package/dist/upload.js.map +1 -0
  38. package/dist/vite-plugin.d.ts +22 -0
  39. package/dist/vite-plugin.d.ts.map +1 -0
  40. package/dist/vite-plugin.js +95 -0
  41. package/dist/vite-plugin.js.map +1 -0
  42. package/package.json +61 -0
  43. package/src/command.ts +100 -0
  44. package/src/config.ts +44 -0
  45. package/src/index-dev.ts +16 -0
  46. package/src/index-local.ts +16 -0
  47. package/src/index.ts +12 -0
  48. package/src/inject.ts +195 -0
  49. package/src/sourcemap.ts +317 -0
  50. package/src/status.ts +94 -0
  51. package/src/upload.ts +190 -0
  52. package/src/vite-plugin.ts +160 -0
  53. package/tsconfig.json +30 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@9apes/cli",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "A CLI tool built with TypeScript",
6
+ "main": "dist/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./vite": {
13
+ "types": "./dist/vite-plugin.d.ts",
14
+ "import": "./dist/vite-plugin.js"
15
+ }
16
+ },
17
+ "bin": {
18
+ "apeslocal-cli": "dist/index-local.js",
19
+ "apesdev-cli": "dist/index-dev.js"
20
+ },
21
+ "keywords": [
22
+ "cli",
23
+ "typescript",
24
+ "node"
25
+ ],
26
+ "author": "",
27
+ "license": "ISC",
28
+ "devDependencies": {
29
+ "@types/cli-progress": "^3.11.6",
30
+ "@types/node": "24.9.1",
31
+ "@typescript-eslint/eslint-plugin": "8.46.2",
32
+ "@typescript-eslint/parser": "8.46.2",
33
+ "eslint": "8.57.0",
34
+ "eslint-config-prettier": "10.1.8",
35
+ "prettier": "3.6.2",
36
+ "typescript": "5.9.3",
37
+ "vite": "^6.3.1"
38
+ },
39
+ "peerDependencies": {
40
+ "vite": ">=6"
41
+ },
42
+ "dependencies": {
43
+ "@inquirer/prompts": "7.9.0",
44
+ "axios": "^1.6.0",
45
+ "cli-progress": "^3.12.0",
46
+ "commander": "14.0.1",
47
+ "fast-glob": "^3.3.3",
48
+ "form-data": "^4.0.0",
49
+ "p-limit": "^7.2.0"
50
+ },
51
+ "scripts": {
52
+ "build": "tsc",
53
+ "dev": "tsc --watch",
54
+ "start": "node dist/index.js",
55
+ "clean": "rm -rf dist",
56
+ "lint": "eslint src/**/*.ts",
57
+ "lint:fix": "eslint src/**/*.ts --fix",
58
+ "format": "prettier --write src/**/*.ts",
59
+ "format:check": "prettier --check src/**/*.ts"
60
+ }
61
+ }
package/src/command.ts ADDED
@@ -0,0 +1,100 @@
1
+ import { program } from 'commander';
2
+ import { input } from '@inquirer/prompts';
3
+ import { loadConfig, saveConfig } from './config.js';
4
+ import { injectDebugIdsAndUpload } from './sourcemap.js';
5
+
6
+ /**
7
+ * Setup commands for the CLI program
8
+ * @param configFileName - Name of the config file (e.g., 'config-local.json' or 'config-dev.json')
9
+ * @param baseUrl - Base URL for the API (e.g., 'http://127.0.0.1:8000/api/v1' or 'https://dev.nineapes.com/api/v1')
10
+ * @param cliName - Name of the CLI for error messages (e.g., 'apeslocal-cli' or 'apesdev-cli')
11
+ */
12
+ export function setupCommands(configFileName: string, baseUrl: string, cliName: string) {
13
+ // Config command with subcommands
14
+ const configCommand = program
15
+ .command('config')
16
+ .description('Manage configuration');
17
+
18
+ // Config set subcommand
19
+ configCommand
20
+ .command('set')
21
+ .description('Set API key')
22
+ .argument('[apikey]', 'API key (optional, will prompt if not provided)')
23
+ .action(async (apikey?: string) => {
24
+ try {
25
+ let finalApiKey = apikey;
26
+
27
+ if (!finalApiKey) {
28
+ finalApiKey = await input({
29
+ message: 'Enter your API key:',
30
+ });
31
+ }
32
+
33
+ const configPath = saveConfig({ apiKey: finalApiKey }, configFileName);
34
+ console.log('✅ API key saved successfully at : ' + configPath);
35
+ } catch (error) {
36
+ console.error('❌ Error setting API key:', error);
37
+ process.exit(1);
38
+ }
39
+ });
40
+
41
+ // Config show subcommand
42
+ configCommand
43
+ .command('show')
44
+ .description('Show current API key')
45
+ .action(() => {
46
+ try {
47
+ const config = loadConfig(configFileName);
48
+
49
+ if (!config) {
50
+ console.log(`❌ No configuration found. Run "${cliName} config set" to set your API key.`);
51
+ process.exit(1);
52
+ }
53
+
54
+ console.log('Current API key:', config.apiKey);
55
+ } catch (error) {
56
+ console.error('❌ Error reading config:', error);
57
+ process.exit(1);
58
+ }
59
+ });
60
+
61
+ // Sourcemaps command with subcommands
62
+ const sourcemapsCommand = program
63
+ .command('sourcemaps')
64
+ .description('Manage source maps');
65
+
66
+ // Sourcemaps inject subcommand
67
+ sourcemapsCommand
68
+ .command('inject')
69
+ .description('Inject debug IDs into JavaScript files and source maps')
70
+ .argument('<path>', 'Directory path to process')
71
+ .requiredOption('--projectid <id>', 'Project ID (required)')
72
+ .option('--release-version <version>', 'Release version (optional)')
73
+ .option('--apikey <apikey>', 'Api key (optional, if config not set)')
74
+ .action(async (path: string, options: { projectid: string; releaseVersion?: string; apikey?: string }) => {
75
+ try {
76
+ // Load configuration
77
+ const config = loadConfig(configFileName);
78
+ const apiKey = options.apikey || config?.apiKey;
79
+
80
+ if (!apiKey) {
81
+ console.log(`❌ No API key found. Run "${cliName} config set" to set your API key or use --apikey <apikey> to set the API key.`);
82
+ process.exit(1);
83
+ }
84
+
85
+ // Validate path parameter
86
+ if (!path) {
87
+ console.log('❌ Path argument is required.');
88
+ process.exit(1);
89
+ }
90
+
91
+ // Inject debug IDs into JavaScript files and source maps
92
+ await injectDebugIdsAndUpload(path, { apiKey }, options.projectid, baseUrl, options.releaseVersion);
93
+
94
+ } catch (error) {
95
+ console.error('❌ Error processing sourcemaps:', error);
96
+ process.exit(1);
97
+ }
98
+ });
99
+ }
100
+
package/src/config.ts ADDED
@@ -0,0 +1,44 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ export interface Config {
5
+ apiKey: string;
6
+ }
7
+
8
+ export function getConfigPath(configFileName: string): string {
9
+ return path.join(process.cwd(), configFileName);
10
+ }
11
+
12
+ export function loadConfig(configFileName: string): Config | null {
13
+ try {
14
+ const configPath = getConfigPath(configFileName);
15
+ if (!fs.existsSync(configPath)) {
16
+ return null;
17
+ }
18
+
19
+ const configData = fs.readFileSync(configPath, 'utf8');
20
+ return JSON.parse(configData) as Config;
21
+ } catch (error) {
22
+ console.error('Error loading config:', error);
23
+ return null;
24
+ }
25
+ }
26
+
27
+ export function saveConfig(config: Config, configFileName: string): string {
28
+ try {
29
+ const configPath = getConfigPath(configFileName);
30
+ const configDir = path.dirname(configPath);
31
+
32
+ // Create directory if it doesn't exist
33
+ if (!fs.existsSync(configDir)) {
34
+ fs.mkdirSync(configDir, { recursive: true });
35
+ }
36
+
37
+ // Write config file
38
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
39
+ return configPath;
40
+ } catch (error) {
41
+ console.error('Error saving config:', error);
42
+ throw error;
43
+ }
44
+ }
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import { setupCommands } from './command.js';
4
+
5
+ // CLI version and description
6
+ program
7
+ .name('apesdev-cli')
8
+ .description('A CLI tool for dev environment')
9
+ .version('1.0.0');
10
+
11
+ // Setup commands with dev mode
12
+ setupCommands('config-dev.json', 'https://dev.nineapes.com/api/v1', 'apesdev-cli');
13
+
14
+ // Parse command line arguments
15
+ program.parse();
16
+
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import { setupCommands } from './command.js';
4
+
5
+ // CLI version and description
6
+ program
7
+ .name('apeslocal-cli')
8
+ .description('A CLI tool for local development environment')
9
+ .version('1.0.0');
10
+
11
+ // Setup commands with local mode
12
+ setupCommands('config-local.json', 'http://127.0.0.1:8000/api/v1', 'apeslocal-cli');
13
+
14
+ // Parse command line arguments
15
+ program.parse();
16
+
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import './command.js';
4
+
5
+ // CLI version and description
6
+ program
7
+ .name('apes-cli')
8
+ .description('A CLI tool built with TypeScript')
9
+ .version('1.0.0');
10
+
11
+ // Parse command line arguments
12
+ program.parse();
package/src/inject.ts ADDED
@@ -0,0 +1,195 @@
1
+ import { promises as fs } from 'fs';
2
+
3
+
4
+ /**
5
+ * Read file as buffer and decode to UTF-8 with automatic BOM stripping
6
+ * @param jsFile - Path to JavaScript file
7
+ * @returns File content as UTF-8 string
8
+ */
9
+ async function readFileAsUtf8(jsFile: string): Promise<string> {
10
+ const buffer = await fs.readFile(jsFile);
11
+ const decoder = new TextDecoder('utf-8');
12
+ return decoder.decode(buffer);
13
+ }
14
+
15
+
16
+ /**
17
+ * Find existing debug ID snippet in file content
18
+ * @param content - File content to search
19
+ * @returns Object with debug ID and its index if found, null otherwise
20
+ */
21
+ function findExistingDebugId(content: string): { id: string; index: number } | null {
22
+ const debugIdPattern = /_apesDebugIdIdentifier="apes-dbid-([^"]+)"/;
23
+ const match = content.match(debugIdPattern);
24
+ if (!match || match.index === undefined) {
25
+ return null;
26
+ }
27
+ return {
28
+ id: match[1],
29
+ index: match.index,
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Find the start position of the debug ID snippet
35
+ * @param content - File content to search
36
+ * @param debugIdIndex - Index where the debug ID identifier is found
37
+ * @returns Start index of the snippet, or -1 if not found
38
+ */
39
+ function findSnippetStart(content: string, debugIdIndex: number): number {
40
+ const snippetStartMarker = ';{try{';
41
+ for (let i = debugIdIndex; i >= 0; i--) {
42
+ if (content.slice(i, i + snippetStartMarker.length) === snippetStartMarker) {
43
+ return i;
44
+ }
45
+ }
46
+ return -1;
47
+ }
48
+
49
+ /**
50
+ * Find the end position of the debug ID snippet
51
+ * @param content - File content to search
52
+ * @param debugIdIndex - Index where the debug ID identifier is found
53
+ * @returns End index (exclusive) of the snippet, or -1 if not found
54
+ */
55
+ function findSnippetEnd(content: string, debugIdIndex: number): number {
56
+ const snippetEndMarker = '};';
57
+ for (let i = debugIdIndex; i < content.length; i++) {
58
+ if (content.slice(i, i + snippetEndMarker.length) === snippetEndMarker && i > debugIdIndex) {
59
+ return i + snippetEndMarker.length;
60
+ }
61
+ }
62
+ return -1;
63
+ }
64
+
65
+ /**
66
+ * Remove existing debug ID snippet from file content
67
+ * @param content - File content
68
+ * @param existingDebugId - Object containing the existing debug ID and its index
69
+ * @returns Content with the snippet removed
70
+ */
71
+ function removeExistingSnippet(
72
+ content: string,
73
+ existingDebugId: { id: string; index: number }
74
+ ): string {
75
+ const snippetStart = findSnippetStart(content, existingDebugId.index);
76
+ const snippetEnd = findSnippetEnd(content, existingDebugId.index);
77
+
78
+ if (snippetStart >= 0 && snippetEnd > snippetStart) {
79
+ return content.slice(0, snippetStart) + content.slice(snippetEnd);
80
+ }
81
+
82
+ return content;
83
+ }
84
+
85
+ /**
86
+ * Calculate the insertion position for the debug ID snippet
87
+ * Inserts after shebang (if present) and/or "use strict" directive (if present)
88
+ * @param content - File content
89
+ * @returns Index where the snippet should be inserted
90
+ */
91
+ function calculateInsertionPosition(content: string): number {
92
+ let position = 0;
93
+ let insertPosition = 0;
94
+
95
+ // Check for shebang (must be at start, ends at first newline)
96
+ const shebangMatch = content.match(/^#!.*(\r?\n)?/);
97
+ if (shebangMatch) {
98
+ position += shebangMatch[0].length;
99
+ insertPosition = position;
100
+ }
101
+
102
+ // Check for "use strict" after shebang
103
+ // Pattern: optional whitespace, then "use strict" with quotes, optional semicolon, optional whitespace
104
+ const useStrictPattern = /^\s*["']use strict["'];?\s*/;
105
+ const remainingContent = content.slice(position);
106
+ const useStrictMatch = remainingContent.match(useStrictPattern);
107
+
108
+ if (useStrictMatch) {
109
+ insertPosition = position + useStrictMatch[0].length;
110
+ }
111
+
112
+ return insertPosition;
113
+ }
114
+
115
+ /**
116
+ * Inject debug ID into JavaScript file
117
+ * @param jsFile - Path to JavaScript file
118
+ * @param debugIdSnippet - Debug ID snippet to inject
119
+ * @param debugId - Debug ID
120
+ */
121
+ export async function injectDebugIdIntoFile(
122
+ jsFile: string,
123
+ debugIdSnippet: string,
124
+ debugId: string
125
+ ): Promise<void> {
126
+ try {
127
+ let content = await readFileAsUtf8(jsFile);
128
+ const existingDebugId = findExistingDebugId(content);
129
+
130
+ // If existing debug ID matches the one to inject, skip injection
131
+ if (existingDebugId && existingDebugId.id === debugId) {
132
+ return;
133
+ }
134
+
135
+ // If different debug ID exists, remove the old snippet
136
+ if (existingDebugId) {
137
+ content = removeExistingSnippet(content, existingDebugId);
138
+ }
139
+
140
+ // Calculate insertion position and inject the new snippet
141
+ const insertPosition = calculateInsertionPosition(content);
142
+ const before = content.slice(0, insertPosition);
143
+ const after = content.slice(insertPosition);
144
+ const modifiedContent = before + debugIdSnippet + after;
145
+
146
+ // Write back to file
147
+ await fs.writeFile(jsFile, modifiedContent, 'utf-8');
148
+ } catch (error) {
149
+ const errorMessage = error instanceof Error ? error.message : String(error);
150
+ throw new Error(`Failed to inject debug ID into ${jsFile}: ${errorMessage}`);
151
+ }
152
+ }
153
+
154
+
155
+
156
+ /**
157
+ * Inject debug ID into source map file
158
+ * @param mapFile - Path to source map file
159
+ * @param debugId - Debug ID to inject
160
+ */
161
+ export async function injectDebugIdIntoMapFile(
162
+ mapFile: string | null,
163
+ debugId: string
164
+ ): Promise<void> {
165
+ try {
166
+ if (!mapFile) {
167
+ return;
168
+ }
169
+
170
+ const content = await fs.readFile(mapFile, 'utf-8');
171
+
172
+ let sourcemap: Record<string, unknown>;
173
+ try {
174
+ sourcemap = JSON.parse(content);
175
+ } catch (parseError) {
176
+ throw new Error(`Invalid JSON in sourcemap file: ${parseError}`);
177
+ }
178
+
179
+ // Skip if already has the same debug_id
180
+ if (sourcemap.debug_id === debugId) {
181
+ return;
182
+ }
183
+
184
+ // Add the debug_id field
185
+ sourcemap.debug_id = debugId;
186
+
187
+ // Use compact JSON (no pretty-printing)
188
+ const modifiedContent = JSON.stringify(sourcemap);
189
+ await fs.writeFile(mapFile, modifiedContent, 'utf-8');
190
+
191
+ } catch (error) {
192
+ const errorMessage = error instanceof Error ? error.message : String(error);
193
+ throw new Error(`Failed to inject debug ID into ${mapFile}: ${errorMessage}`);
194
+ }
195
+ }