@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,160 @@
1
+ import path from 'path';
2
+ import pLimit from 'p-limit';
3
+ import { injectDebugIdIntoFile, injectDebugIdIntoMapFile } from './inject.js';
4
+ import {
5
+ generateDebugId,
6
+ getDebugIdSnippet,
7
+ getGitCommitHash,
8
+ scanForJavaScriptFiles,
9
+ validateDirectoryPath,
10
+ } from './sourcemap.js';
11
+ import { uploadFile, type UploadError } from './upload.js';
12
+
13
+ export interface NineapesSourcemapsOptions {
14
+ projectId: string;
15
+ apiKey: string;
16
+ baseUrl?: string;
17
+ releaseVersion?: string;
18
+ }
19
+
20
+ const DEFAULT_BASE_URL = 'https://dev.nineapes.com/api/v1';
21
+
22
+ interface BuildConfigLike {
23
+ outDir: string;
24
+ }
25
+
26
+ interface ResolvedConfigLike {
27
+ root: string;
28
+ build: BuildConfigLike;
29
+ }
30
+
31
+ interface VitePluginLike {
32
+ name: string;
33
+ apply?: 'build';
34
+ configResolved?: (config: ResolvedConfigLike) => void;
35
+ closeBundle?: () => Promise<void>;
36
+ }
37
+
38
+ export function withNineapesSourcemaps(options: NineapesSourcemapsOptions): VitePluginLike {
39
+ let resolvedConfig: ResolvedConfigLike | null = null;
40
+
41
+ return {
42
+ name: 'nineapes-sourcemaps',
43
+ apply: 'build',
44
+ configResolved(config) {
45
+ resolvedConfig = config;
46
+ },
47
+ async closeBundle() {
48
+ if (!resolvedConfig) {
49
+ throw new Error('Vite config is not resolved');
50
+ }
51
+
52
+ const outDir = path.resolve(resolvedConfig.root, resolvedConfig.build.outDir);
53
+ const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
54
+
55
+ console.log(`[nineapes-sourcemaps] Processing output directory: ${outDir}`);
56
+
57
+ const resolvedPath = await validateDirectoryPath(outDir);
58
+ const [filePairs, commitHash] = await Promise.all([
59
+ scanForJavaScriptFiles(resolvedPath),
60
+ getGitCommitHash(resolvedPath),
61
+ ]);
62
+
63
+ if (filePairs.length === 0) {
64
+ console.log('[nineapes-sourcemaps] No JavaScript files found, skipping sourcemap upload');
65
+ return;
66
+ }
67
+
68
+ const totalUploadCount = filePairs.reduce((count, pair) => {
69
+ return count + 1 + (pair.mapFile ? 1 : 0);
70
+ }, 0);
71
+
72
+ const injectLimiter = pLimit(15);
73
+ const uploadLimiter = pLimit(10);
74
+
75
+ let injectCompleted = 0;
76
+ let injectFailed = 0;
77
+ let uploadCompleted = 0;
78
+ let uploadFailed = 0;
79
+ const uploadPromises: Promise<UploadError[]>[] = [];
80
+
81
+ const injectionResults = await Promise.allSettled(
82
+ filePairs.map((filePair) =>
83
+ injectLimiter(async () => {
84
+ const debugId = generateDebugId();
85
+
86
+ await Promise.all([
87
+ injectDebugIdIntoFile(filePair.jsFile, getDebugIdSnippet(debugId), debugId),
88
+ injectDebugIdIntoMapFile(filePair.mapFile, debugId),
89
+ ]);
90
+
91
+ injectCompleted++;
92
+
93
+ const uploadPromise = uploadLimiter(async () =>
94
+ uploadFile(
95
+ filePair,
96
+ debugId,
97
+ { apiKey: options.apiKey },
98
+ options.projectId,
99
+ baseUrl,
100
+ options.releaseVersion,
101
+ commitHash,
102
+ (success) => {
103
+ if (success) {
104
+ uploadCompleted++;
105
+ } else {
106
+ uploadFailed++;
107
+ }
108
+ }
109
+ )
110
+ );
111
+
112
+ uploadPromises.push(uploadPromise);
113
+ })
114
+ )
115
+ );
116
+
117
+ const injectionErrors: string[] = [];
118
+ for (const result of injectionResults) {
119
+ if (result.status === 'rejected') {
120
+ injectFailed++;
121
+ const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
122
+ injectionErrors.push(message);
123
+ }
124
+ }
125
+
126
+ if (injectionErrors.length > 0) {
127
+ throw new Error(
128
+ `[nineapes-sourcemaps] Injection failed for ${injectFailed} file(s): ${injectionErrors.join('\n')}`
129
+ );
130
+ }
131
+
132
+ const uploadResults = await Promise.allSettled(uploadPromises);
133
+ const uploadErrors: UploadError[] = [];
134
+ for (const result of uploadResults) {
135
+ if (result.status === 'fulfilled') {
136
+ uploadErrors.push(...result.value);
137
+ } else {
138
+ uploadErrors.push({
139
+ fileName: 'unknown',
140
+ errorMessage: result.reason instanceof Error ? result.reason.message : String(result.reason),
141
+ });
142
+ }
143
+ }
144
+
145
+ if (uploadErrors.length > 0) {
146
+ const formattedErrors = uploadErrors
147
+ .map((error) => {
148
+ const code = error.statusCode ? ` status=${error.statusCode}` : '';
149
+ return `- ${error.fileName}${code}: ${error.errorMessage}`;
150
+ })
151
+ .join('\n');
152
+ throw new Error(`[nineapes-sourcemaps] Upload failed:\n${formattedErrors}`);
153
+ }
154
+
155
+ console.log(
156
+ `[nineapes-sourcemaps] Done. Injected ${injectCompleted} file pair(s), uploaded ${uploadCompleted}/${totalUploadCount} file(s)`
157
+ );
158
+ },
159
+ };
160
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "nodenext",
5
+ "lib": ["ES2020"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "removeComments": false,
16
+ "noImplicitAny": true,
17
+ "strictNullChecks": true,
18
+ "strictFunctionTypes": true,
19
+ "noImplicitThis": true,
20
+ "noImplicitReturns": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+ "moduleResolution": "nodenext",
23
+ "allowSyntheticDefaultImports": true,
24
+ "experimentalDecorators": true,
25
+ "emitDecoratorMetadata": true,
26
+ "resolveJsonModule": true
27
+ },
28
+ "include": ["src/**/*"],
29
+ "exclude": ["node_modules", "dist"]
30
+ }