@9apes/cli 0.1.0 → 0.1.1

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.
@@ -0,0 +1,13 @@
1
+ import { type NineapesSourcemapsOptions } from './pipeline.js';
2
+ interface AstroBuildDoneOptions {
3
+ dir: URL;
4
+ }
5
+ interface AstroIntegrationLike {
6
+ name: string;
7
+ hooks: {
8
+ 'astro:build:done': (options: AstroBuildDoneOptions) => Promise<void>;
9
+ };
10
+ }
11
+ export declare function withNineapesSourcemaps(options: NineapesSourcemapsOptions): AstroIntegrationLike;
12
+ export {};
13
+ //# sourceMappingURL=astro-integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astro-integration.d.ts","sourceRoot":"","sources":["../src/astro-integration.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAErF,UAAU,qBAAqB;IAC7B,GAAG,EAAE,GAAG,CAAC;CACV;AAED,UAAU,oBAAoB;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QACL,kBAAkB,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACvE,CAAC;CACH;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,yBAAyB,GAAG,oBAAoB,CAU/F"}
@@ -0,0 +1,14 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { runSourcemapPipeline } from './pipeline.js';
3
+ export function withNineapesSourcemaps(options) {
4
+ return {
5
+ name: 'nineapes-sourcemaps',
6
+ hooks: {
7
+ 'astro:build:done': async ({ dir }) => {
8
+ const outDir = fileURLToPath(dir);
9
+ await runSourcemapPipeline(outDir, options);
10
+ },
11
+ },
12
+ };
13
+ }
14
+ //# sourceMappingURL=astro-integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astro-integration.js","sourceRoot":"","sources":["../src/astro-integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAkC,MAAM,eAAe,CAAC;AAarF,MAAM,UAAU,sBAAsB,CAAC,OAAkC;IACvE,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,KAAK,EAAE;YACL,kBAAkB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBACpC,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;gBAClC,MAAM,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC9C,CAAC;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface InlineScriptRecord {
2
+ htmlFile: string;
3
+ tempJsFile: string;
4
+ contentStart: number;
5
+ contentEnd: number;
6
+ }
7
+ export declare function extractInlineScripts(dirPath: string): Promise<InlineScriptRecord[]>;
8
+ export declare function patchHtmlWithInjectedScripts(records: InlineScriptRecord[]): Promise<void>;
9
+ //# sourceMappingURL=inline-scripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-scripts.d.ts","sourceRoot":"","sources":["../src/inline-scripts.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAoBD,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAuDzF;AAED,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC/F"}
@@ -0,0 +1,93 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import fg from 'fast-glob';
4
+ const SCRIPT_TAG_REGEX = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
5
+ const SOURCE_MAP_REGEX = /\/\/# sourceMappingURL=([^\s'"]+)/;
6
+ function resolveTempJsPath(dirPath, htmlFile, jsRef) {
7
+ const cleanedRef = jsRef.replace(/^\/+/, '');
8
+ if (jsRef.includes('/')) {
9
+ return path.resolve(dirPath, cleanedRef);
10
+ }
11
+ const fromHtmlDir = path.resolve(path.dirname(htmlFile), jsRef);
12
+ if (fromHtmlDir.includes(`${path.sep}_astro${path.sep}`)) {
13
+ return fromHtmlDir;
14
+ }
15
+ return path.resolve(dirPath, '_astro', path.basename(jsRef));
16
+ }
17
+ export async function extractInlineScripts(dirPath) {
18
+ const htmlFiles = await fg('**/*.html', {
19
+ cwd: dirPath,
20
+ absolute: true,
21
+ ignore: ['**/node_modules/**', '**/.git/**', '**/.*/**'],
22
+ });
23
+ const records = [];
24
+ for (const htmlFile of htmlFiles) {
25
+ const htmlContent = await fs.readFile(htmlFile, 'utf-8');
26
+ for (const match of htmlContent.matchAll(SCRIPT_TAG_REGEX)) {
27
+ const fullTag = match[0];
28
+ const attributes = match[1] ?? '';
29
+ const scriptContent = match[2] ?? '';
30
+ const tagStart = match.index ?? -1;
31
+ if (tagStart < 0 || /\bsrc\s*=/.test(attributes)) {
32
+ continue;
33
+ }
34
+ const sourceMapMatch = scriptContent.match(SOURCE_MAP_REGEX);
35
+ if (!sourceMapMatch) {
36
+ continue;
37
+ }
38
+ const sourceMapRef = sourceMapMatch[1];
39
+ if (sourceMapRef.startsWith('data:') || sourceMapRef.includes('://')) {
40
+ continue;
41
+ }
42
+ const jsRef = sourceMapRef.replace(/\.map$/, '');
43
+ if (!jsRef.endsWith('.js') && !jsRef.endsWith('.mjs')) {
44
+ continue;
45
+ }
46
+ const tempJsFile = resolveTempJsPath(dirPath, htmlFile, jsRef);
47
+ await fs.mkdir(path.dirname(tempJsFile), { recursive: true });
48
+ await fs.writeFile(tempJsFile, scriptContent, 'utf-8');
49
+ const relativeContentStart = fullTag.indexOf(scriptContent);
50
+ const contentStart = tagStart + relativeContentStart;
51
+ const contentEnd = contentStart + scriptContent.length;
52
+ records.push({
53
+ htmlFile,
54
+ tempJsFile,
55
+ contentStart,
56
+ contentEnd,
57
+ });
58
+ }
59
+ }
60
+ return records;
61
+ }
62
+ export async function patchHtmlWithInjectedScripts(records) {
63
+ if (records.length === 0) {
64
+ return;
65
+ }
66
+ const groupedByHtml = new Map();
67
+ for (const record of records) {
68
+ const existing = groupedByHtml.get(record.htmlFile) ?? [];
69
+ existing.push(record);
70
+ groupedByHtml.set(record.htmlFile, existing);
71
+ }
72
+ for (const [htmlFile, htmlRecords] of groupedByHtml) {
73
+ let htmlContent = await fs.readFile(htmlFile, 'utf-8');
74
+ const sortedRecords = [...htmlRecords].sort((a, b) => b.contentStart - a.contentStart);
75
+ for (const record of sortedRecords) {
76
+ const injectedContent = await fs.readFile(record.tempJsFile, 'utf-8');
77
+ htmlContent =
78
+ htmlContent.slice(0, record.contentStart) +
79
+ injectedContent +
80
+ htmlContent.slice(record.contentEnd);
81
+ }
82
+ await fs.writeFile(htmlFile, htmlContent, 'utf-8');
83
+ }
84
+ await Promise.all(records.map(async (record) => {
85
+ try {
86
+ await fs.unlink(record.tempJsFile);
87
+ }
88
+ catch {
89
+ // ignore cleanup failures for already-removed temp files
90
+ }
91
+ }));
92
+ }
93
+ //# sourceMappingURL=inline-scripts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-scripts.js","sourceRoot":"","sources":["../src/inline-scripts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface NineapesSourcemapsOptions {
2
+ projectId: string;
3
+ apiKey: string;
4
+ baseUrl?: string;
5
+ releaseVersion?: string;
6
+ }
7
+ export declare function runSourcemapPipeline(outDir: string, options: NineapesSourcemapsOptions): Promise<void>;
8
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CA8Hf"}
@@ -0,0 +1,102 @@
1
+ import pLimit from 'p-limit';
2
+ import { injectDebugIdIntoFile, injectDebugIdIntoMapFile } from './inject.js';
3
+ import { generateDebugId, getDebugIdSnippet, getGitCommitHash, scanForJavaScriptFiles, validateDirectoryPath, } from './sourcemap.js';
4
+ import { uploadFile } from './upload.js';
5
+ import { extractInlineScripts, patchHtmlWithInjectedScripts } from './inline-scripts.js';
6
+ const DEFAULT_BASE_URL = 'https://dev.nineapes.com/api/v1';
7
+ export async function runSourcemapPipeline(outDir, options) {
8
+ const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
9
+ console.log(`[nineapes-sourcemaps] Processing output directory: ${outDir}`);
10
+ const resolvedPath = await validateDirectoryPath(outDir);
11
+ const inlineScriptRecords = await extractInlineScripts(resolvedPath);
12
+ let pipelineError = null;
13
+ try {
14
+ const [filePairs, commitHash] = await Promise.all([
15
+ scanForJavaScriptFiles(resolvedPath),
16
+ getGitCommitHash(resolvedPath),
17
+ ]);
18
+ if (filePairs.length === 0) {
19
+ console.log('[nineapes-sourcemaps] No JavaScript files found, skipping sourcemap upload');
20
+ return;
21
+ }
22
+ const totalUploadCount = filePairs.reduce((count, pair) => {
23
+ return count + 1 + (pair.mapFile ? 1 : 0);
24
+ }, 0);
25
+ const injectLimiter = pLimit(15);
26
+ const uploadLimiter = pLimit(10);
27
+ let injectCompleted = 0;
28
+ let injectFailed = 0;
29
+ let uploadCompleted = 0;
30
+ let uploadFailed = 0;
31
+ const uploadPromises = [];
32
+ const injectionResults = await Promise.allSettled(filePairs.map((filePair) => injectLimiter(async () => {
33
+ const debugId = generateDebugId();
34
+ await Promise.all([
35
+ injectDebugIdIntoFile(filePair.jsFile, getDebugIdSnippet(debugId), debugId),
36
+ injectDebugIdIntoMapFile(filePair.mapFile, debugId),
37
+ ]);
38
+ injectCompleted++;
39
+ const uploadPromise = uploadLimiter(async () => uploadFile(filePair, debugId, { apiKey: options.apiKey }, options.projectId, baseUrl, options.releaseVersion, commitHash, (success) => {
40
+ if (success) {
41
+ uploadCompleted++;
42
+ }
43
+ else {
44
+ uploadFailed++;
45
+ }
46
+ }));
47
+ uploadPromises.push(uploadPromise);
48
+ })));
49
+ const injectionErrors = [];
50
+ for (const result of injectionResults) {
51
+ if (result.status === 'rejected') {
52
+ injectFailed++;
53
+ const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
54
+ injectionErrors.push(message);
55
+ }
56
+ }
57
+ if (injectionErrors.length > 0) {
58
+ throw new Error(`[nineapes-sourcemaps] Injection failed for ${injectFailed} file(s): ${injectionErrors.join('\n')}`);
59
+ }
60
+ const uploadResults = await Promise.allSettled(uploadPromises);
61
+ const uploadErrors = [];
62
+ for (const result of uploadResults) {
63
+ if (result.status === 'fulfilled') {
64
+ uploadErrors.push(...result.value);
65
+ }
66
+ else {
67
+ uploadErrors.push({
68
+ fileName: 'unknown',
69
+ errorMessage: result.reason instanceof Error ? result.reason.message : String(result.reason),
70
+ });
71
+ }
72
+ }
73
+ if (uploadErrors.length > 0) {
74
+ const formattedErrors = uploadErrors
75
+ .map((error) => {
76
+ const code = error.statusCode ? ` status=${error.statusCode}` : '';
77
+ return `- ${error.fileName}${code}: ${error.errorMessage}`;
78
+ })
79
+ .join('\n');
80
+ throw new Error(`[nineapes-sourcemaps] Upload failed:\n${formattedErrors}`);
81
+ }
82
+ console.log(`[nineapes-sourcemaps] Done. Injected ${injectCompleted} file pair(s), uploaded ${uploadCompleted}/${totalUploadCount} file(s)`);
83
+ }
84
+ catch (error) {
85
+ pipelineError = error;
86
+ }
87
+ finally {
88
+ try {
89
+ await patchHtmlWithInjectedScripts(inlineScriptRecords);
90
+ }
91
+ catch (patchError) {
92
+ if (!pipelineError) {
93
+ throw patchError;
94
+ }
95
+ console.error('[nineapes-sourcemaps] Failed to patch inline HTML scripts:', patchError);
96
+ }
97
+ }
98
+ if (pipelineError) {
99
+ throw pipelineError;
100
+ }
101
+ }
102
+ //# sourceMappingURL=pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,4 @@
1
- export interface NineapesSourcemapsOptions {
2
- projectId: string;
3
- apiKey: string;
4
- baseUrl?: string;
5
- releaseVersion?: string;
6
- }
1
+ import { type NineapesSourcemapsOptions } from './pipeline.js';
7
2
  interface BuildConfigLike {
8
3
  outDir: string;
9
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACtD,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,yBAAyB,GAAG,cAAc,CA0HzF"}
1
+ {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAErF,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACtD,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,yBAAyB,GAAG,cAAc,CAkBzF"}
@@ -1,9 +1,5 @@
1
1
  import path from 'path';
2
- import pLimit from 'p-limit';
3
- import { injectDebugIdIntoFile, injectDebugIdIntoMapFile } from './inject.js';
4
- import { generateDebugId, getDebugIdSnippet, getGitCommitHash, scanForJavaScriptFiles, validateDirectoryPath, } from './sourcemap.js';
5
- import { uploadFile } from './upload.js';
6
- const DEFAULT_BASE_URL = 'https://dev.nineapes.com/api/v1';
2
+ import { runSourcemapPipeline } from './pipeline.js';
7
3
  export function withNineapesSourcemaps(options) {
8
4
  let resolvedConfig = null;
9
5
  return {
@@ -17,78 +13,7 @@ export function withNineapesSourcemaps(options) {
17
13
  throw new Error('Vite config is not resolved');
18
14
  }
19
15
  const outDir = path.resolve(resolvedConfig.root, resolvedConfig.build.outDir);
20
- const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
21
- console.log(`[nineapes-sourcemaps] Processing output directory: ${outDir}`);
22
- const resolvedPath = await validateDirectoryPath(outDir);
23
- const [filePairs, commitHash] = await Promise.all([
24
- scanForJavaScriptFiles(resolvedPath),
25
- getGitCommitHash(resolvedPath),
26
- ]);
27
- if (filePairs.length === 0) {
28
- console.log('[nineapes-sourcemaps] No JavaScript files found, skipping sourcemap upload');
29
- return;
30
- }
31
- const totalUploadCount = filePairs.reduce((count, pair) => {
32
- return count + 1 + (pair.mapFile ? 1 : 0);
33
- }, 0);
34
- const injectLimiter = pLimit(15);
35
- const uploadLimiter = pLimit(10);
36
- let injectCompleted = 0;
37
- let injectFailed = 0;
38
- let uploadCompleted = 0;
39
- let uploadFailed = 0;
40
- const uploadPromises = [];
41
- const injectionResults = await Promise.allSettled(filePairs.map((filePair) => injectLimiter(async () => {
42
- const debugId = generateDebugId();
43
- await Promise.all([
44
- injectDebugIdIntoFile(filePair.jsFile, getDebugIdSnippet(debugId), debugId),
45
- injectDebugIdIntoMapFile(filePair.mapFile, debugId),
46
- ]);
47
- injectCompleted++;
48
- const uploadPromise = uploadLimiter(async () => uploadFile(filePair, debugId, { apiKey: options.apiKey }, options.projectId, baseUrl, options.releaseVersion, commitHash, (success) => {
49
- if (success) {
50
- uploadCompleted++;
51
- }
52
- else {
53
- uploadFailed++;
54
- }
55
- }));
56
- uploadPromises.push(uploadPromise);
57
- })));
58
- const injectionErrors = [];
59
- for (const result of injectionResults) {
60
- if (result.status === 'rejected') {
61
- injectFailed++;
62
- const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
63
- injectionErrors.push(message);
64
- }
65
- }
66
- if (injectionErrors.length > 0) {
67
- throw new Error(`[nineapes-sourcemaps] Injection failed for ${injectFailed} file(s): ${injectionErrors.join('\n')}`);
68
- }
69
- const uploadResults = await Promise.allSettled(uploadPromises);
70
- const uploadErrors = [];
71
- for (const result of uploadResults) {
72
- if (result.status === 'fulfilled') {
73
- uploadErrors.push(...result.value);
74
- }
75
- else {
76
- uploadErrors.push({
77
- fileName: 'unknown',
78
- errorMessage: result.reason instanceof Error ? result.reason.message : String(result.reason),
79
- });
80
- }
81
- }
82
- if (uploadErrors.length > 0) {
83
- const formattedErrors = uploadErrors
84
- .map((error) => {
85
- const code = error.statusCode ? ` status=${error.statusCode}` : '';
86
- return `- ${error.fileName}${code}: ${error.errorMessage}`;
87
- })
88
- .join('\n');
89
- throw new Error(`[nineapes-sourcemaps] Upload failed:\n${formattedErrors}`);
90
- }
91
- console.log(`[nineapes-sourcemaps] Done. Injected ${injectCompleted} file pair(s), uploaded ${uploadCompleted}/${totalUploadCount} file(s)`);
16
+ await runSourcemapPipeline(outDir, options);
92
17
  },
93
18
  };
94
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAkC,MAAM,eAAe,CAAC;AAkBrF,MAAM,UAAU,sBAAsB,CAAC,OAAkC;IACvE,IAAI,cAAc,GAA8B,IAAI,CAAC;IAErD,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,KAAK,EAAE,OAAO;QACd,cAAc,CAAC,MAAM;YACnB,cAAc,GAAG,MAAM,CAAC;QAC1B,CAAC;QACD,KAAK,CAAC,WAAW;YACf,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;YACjD,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC9E,MAAM,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@9apes/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
- "description": "A CLI tool built with TypeScript",
5
+ "description": "Connects your production builds to the 9apes error tracking pipeline",
6
6
  "main": "dist/index.js",
7
7
  "exports": {
8
8
  ".": {
@@ -12,6 +12,10 @@
12
12
  "./vite": {
13
13
  "types": "./dist/vite-plugin.d.ts",
14
14
  "import": "./dist/vite-plugin.js"
15
+ },
16
+ "./astro": {
17
+ "types": "./dist/astro-integration.d.ts",
18
+ "import": "./dist/astro-integration.js"
15
19
  }
16
20
  },
17
21
  "bin": {
@@ -0,0 +1,25 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { runSourcemapPipeline, type NineapesSourcemapsOptions } from './pipeline.js';
3
+
4
+ interface AstroBuildDoneOptions {
5
+ dir: URL;
6
+ }
7
+
8
+ interface AstroIntegrationLike {
9
+ name: string;
10
+ hooks: {
11
+ 'astro:build:done': (options: AstroBuildDoneOptions) => Promise<void>;
12
+ };
13
+ }
14
+
15
+ export function withNineapesSourcemaps(options: NineapesSourcemapsOptions): AstroIntegrationLike {
16
+ return {
17
+ name: 'nineapes-sourcemaps',
18
+ hooks: {
19
+ 'astro:build:done': async ({ dir }) => {
20
+ const outDir = fileURLToPath(dir);
21
+ await runSourcemapPipeline(outDir, options);
22
+ },
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,123 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import fg from 'fast-glob';
4
+
5
+ export interface InlineScriptRecord {
6
+ htmlFile: string;
7
+ tempJsFile: string;
8
+ contentStart: number;
9
+ contentEnd: number;
10
+ }
11
+
12
+ const SCRIPT_TAG_REGEX = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
13
+ const SOURCE_MAP_REGEX = /\/\/# sourceMappingURL=([^\s'"]+)/;
14
+
15
+ function resolveTempJsPath(dirPath: string, htmlFile: string, jsRef: string): string {
16
+ const cleanedRef = jsRef.replace(/^\/+/, '');
17
+
18
+ if (jsRef.includes('/')) {
19
+ return path.resolve(dirPath, cleanedRef);
20
+ }
21
+
22
+ const fromHtmlDir = path.resolve(path.dirname(htmlFile), jsRef);
23
+ if (fromHtmlDir.includes(`${path.sep}_astro${path.sep}`)) {
24
+ return fromHtmlDir;
25
+ }
26
+
27
+ return path.resolve(dirPath, '_astro', path.basename(jsRef));
28
+ }
29
+
30
+ export async function extractInlineScripts(dirPath: string): Promise<InlineScriptRecord[]> {
31
+ const htmlFiles = await fg('**/*.html', {
32
+ cwd: dirPath,
33
+ absolute: true,
34
+ ignore: ['**/node_modules/**', '**/.git/**', '**/.*/**'],
35
+ });
36
+
37
+ const records: InlineScriptRecord[] = [];
38
+
39
+ for (const htmlFile of htmlFiles) {
40
+ const htmlContent = await fs.readFile(htmlFile, 'utf-8');
41
+
42
+ for (const match of htmlContent.matchAll(SCRIPT_TAG_REGEX)) {
43
+ const fullTag = match[0];
44
+ const attributes = match[1] ?? '';
45
+ const scriptContent = match[2] ?? '';
46
+ const tagStart = match.index ?? -1;
47
+
48
+ if (tagStart < 0 || /\bsrc\s*=/.test(attributes)) {
49
+ continue;
50
+ }
51
+
52
+ const sourceMapMatch = scriptContent.match(SOURCE_MAP_REGEX);
53
+ if (!sourceMapMatch) {
54
+ continue;
55
+ }
56
+
57
+ const sourceMapRef = sourceMapMatch[1];
58
+ if (sourceMapRef.startsWith('data:') || sourceMapRef.includes('://')) {
59
+ continue;
60
+ }
61
+
62
+ const jsRef = sourceMapRef.replace(/\.map$/, '');
63
+ if (!jsRef.endsWith('.js') && !jsRef.endsWith('.mjs')) {
64
+ continue;
65
+ }
66
+
67
+ const tempJsFile = resolveTempJsPath(dirPath, htmlFile, jsRef);
68
+ await fs.mkdir(path.dirname(tempJsFile), { recursive: true });
69
+ await fs.writeFile(tempJsFile, scriptContent, 'utf-8');
70
+
71
+ const relativeContentStart = fullTag.indexOf(scriptContent);
72
+ const contentStart = tagStart + relativeContentStart;
73
+ const contentEnd = contentStart + scriptContent.length;
74
+
75
+ records.push({
76
+ htmlFile,
77
+ tempJsFile,
78
+ contentStart,
79
+ contentEnd,
80
+ });
81
+ }
82
+ }
83
+
84
+ return records;
85
+ }
86
+
87
+ export async function patchHtmlWithInjectedScripts(records: InlineScriptRecord[]): Promise<void> {
88
+ if (records.length === 0) {
89
+ return;
90
+ }
91
+
92
+ const groupedByHtml = new Map<string, InlineScriptRecord[]>();
93
+ for (const record of records) {
94
+ const existing = groupedByHtml.get(record.htmlFile) ?? [];
95
+ existing.push(record);
96
+ groupedByHtml.set(record.htmlFile, existing);
97
+ }
98
+
99
+ for (const [htmlFile, htmlRecords] of groupedByHtml) {
100
+ let htmlContent = await fs.readFile(htmlFile, 'utf-8');
101
+ const sortedRecords = [...htmlRecords].sort((a, b) => b.contentStart - a.contentStart);
102
+
103
+ for (const record of sortedRecords) {
104
+ const injectedContent = await fs.readFile(record.tempJsFile, 'utf-8');
105
+ htmlContent =
106
+ htmlContent.slice(0, record.contentStart) +
107
+ injectedContent +
108
+ htmlContent.slice(record.contentEnd);
109
+ }
110
+
111
+ await fs.writeFile(htmlFile, htmlContent, 'utf-8');
112
+ }
113
+
114
+ await Promise.all(
115
+ records.map(async (record) => {
116
+ try {
117
+ await fs.unlink(record.tempJsFile);
118
+ } catch {
119
+ // ignore cleanup failures for already-removed temp files
120
+ }
121
+ })
122
+ );
123
+ }
@@ -0,0 +1,151 @@
1
+ import pLimit from 'p-limit';
2
+ import { injectDebugIdIntoFile, injectDebugIdIntoMapFile } from './inject.js';
3
+ import {
4
+ generateDebugId,
5
+ getDebugIdSnippet,
6
+ getGitCommitHash,
7
+ scanForJavaScriptFiles,
8
+ validateDirectoryPath,
9
+ } from './sourcemap.js';
10
+ import { uploadFile, type UploadError } from './upload.js';
11
+ import { extractInlineScripts, patchHtmlWithInjectedScripts } from './inline-scripts.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
+ export async function runSourcemapPipeline(
23
+ outDir: string,
24
+ options: NineapesSourcemapsOptions
25
+ ): Promise<void> {
26
+ const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
27
+
28
+ console.log(`[nineapes-sourcemaps] Processing output directory: ${outDir}`);
29
+
30
+ const resolvedPath = await validateDirectoryPath(outDir);
31
+ const inlineScriptRecords = await extractInlineScripts(resolvedPath);
32
+
33
+ let pipelineError: unknown = null;
34
+ try {
35
+ const [filePairs, commitHash] = await Promise.all([
36
+ scanForJavaScriptFiles(resolvedPath),
37
+ getGitCommitHash(resolvedPath),
38
+ ]);
39
+
40
+ if (filePairs.length === 0) {
41
+ console.log('[nineapes-sourcemaps] No JavaScript files found, skipping sourcemap upload');
42
+ return;
43
+ }
44
+
45
+ const totalUploadCount = filePairs.reduce((count, pair) => {
46
+ return count + 1 + (pair.mapFile ? 1 : 0);
47
+ }, 0);
48
+
49
+ const injectLimiter = pLimit(15);
50
+ const uploadLimiter = pLimit(10);
51
+
52
+ let injectCompleted = 0;
53
+ let injectFailed = 0;
54
+ let uploadCompleted = 0;
55
+ let uploadFailed = 0;
56
+ const uploadPromises: Promise<UploadError[]>[] = [];
57
+
58
+ const injectionResults = await Promise.allSettled(
59
+ filePairs.map((filePair) =>
60
+ injectLimiter(async () => {
61
+ const debugId = generateDebugId();
62
+
63
+ await Promise.all([
64
+ injectDebugIdIntoFile(filePair.jsFile, getDebugIdSnippet(debugId), debugId),
65
+ injectDebugIdIntoMapFile(filePair.mapFile, debugId),
66
+ ]);
67
+
68
+ injectCompleted++;
69
+
70
+ const uploadPromise = uploadLimiter(async () =>
71
+ uploadFile(
72
+ filePair,
73
+ debugId,
74
+ { apiKey: options.apiKey },
75
+ options.projectId,
76
+ baseUrl,
77
+ options.releaseVersion,
78
+ commitHash,
79
+ (success) => {
80
+ if (success) {
81
+ uploadCompleted++;
82
+ } else {
83
+ uploadFailed++;
84
+ }
85
+ }
86
+ )
87
+ );
88
+
89
+ uploadPromises.push(uploadPromise);
90
+ })
91
+ )
92
+ );
93
+
94
+ const injectionErrors: string[] = [];
95
+ for (const result of injectionResults) {
96
+ if (result.status === 'rejected') {
97
+ injectFailed++;
98
+ const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
99
+ injectionErrors.push(message);
100
+ }
101
+ }
102
+
103
+ if (injectionErrors.length > 0) {
104
+ throw new Error(
105
+ `[nineapes-sourcemaps] Injection failed for ${injectFailed} file(s): ${injectionErrors.join('\n')}`
106
+ );
107
+ }
108
+
109
+ const uploadResults = await Promise.allSettled(uploadPromises);
110
+ const uploadErrors: UploadError[] = [];
111
+ for (const result of uploadResults) {
112
+ if (result.status === 'fulfilled') {
113
+ uploadErrors.push(...result.value);
114
+ } else {
115
+ uploadErrors.push({
116
+ fileName: 'unknown',
117
+ errorMessage: result.reason instanceof Error ? result.reason.message : String(result.reason),
118
+ });
119
+ }
120
+ }
121
+
122
+ if (uploadErrors.length > 0) {
123
+ const formattedErrors = uploadErrors
124
+ .map((error) => {
125
+ const code = error.statusCode ? ` status=${error.statusCode}` : '';
126
+ return `- ${error.fileName}${code}: ${error.errorMessage}`;
127
+ })
128
+ .join('\n');
129
+ throw new Error(`[nineapes-sourcemaps] Upload failed:\n${formattedErrors}`);
130
+ }
131
+
132
+ console.log(
133
+ `[nineapes-sourcemaps] Done. Injected ${injectCompleted} file pair(s), uploaded ${uploadCompleted}/${totalUploadCount} file(s)`
134
+ );
135
+ } catch (error) {
136
+ pipelineError = error;
137
+ } finally {
138
+ try {
139
+ await patchHtmlWithInjectedScripts(inlineScriptRecords);
140
+ } catch (patchError) {
141
+ if (!pipelineError) {
142
+ throw patchError;
143
+ }
144
+ console.error('[nineapes-sourcemaps] Failed to patch inline HTML scripts:', patchError);
145
+ }
146
+ }
147
+
148
+ if (pipelineError) {
149
+ throw pipelineError;
150
+ }
151
+ }
@@ -1,23 +1,5 @@
1
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';
2
+ import { runSourcemapPipeline, type NineapesSourcemapsOptions } from './pipeline.js';
21
3
 
22
4
  interface BuildConfigLike {
23
5
  outDir: string;
@@ -50,111 +32,7 @@ export function withNineapesSourcemaps(options: NineapesSourcemapsOptions): Vite
50
32
  }
51
33
 
52
34
  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
- );
35
+ await runSourcemapPipeline(outDir, options);
158
36
  },
159
37
  };
160
38
  }