afterpack 0.0.10 → 0.0.13

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/dist/cli.js CHANGED
@@ -1,46 +1,271 @@
1
1
  #!/usr/bin/env node
2
- import { version } from "@afterpack/core";
2
+ //#region package.json
3
+ var version = "0.0.13";
3
4
 
5
+ //#endregion
6
+ //#region src/utils/format.ts
7
+ const isColorSupported = process.env.FORCE_COLOR !== "0" && !process.env.NO_COLOR && (process.env.FORCE_COLOR !== void 0 || process.stdout.isTTY);
8
+ function ansi(open, close) {
9
+ return (text) => isColorSupported ? `\x1b[${open}m${text}\x1b[${close}m` : text;
10
+ }
11
+ const bold = ansi("1", "22");
12
+ const dim = ansi("2", "22");
13
+ const red = ansi("31", "39");
14
+ const green = ansi("32", "39");
15
+ const yellow = ansi("33", "39");
16
+ const cyan = ansi("36", "39");
17
+ function severityColor(severity) {
18
+ switch (severity.toLowerCase()) {
19
+ case "critical": return red;
20
+ case "high": return red;
21
+ case "medium": return yellow;
22
+ case "low": return cyan;
23
+ default: return dim;
24
+ }
25
+ }
26
+ function header(version$1) {
27
+ return `\n ${bold(`AfterPack v${version$1}`)} — Security Analyzer\n`;
28
+ }
29
+
30
+ //#endregion
31
+ //#region src/utils/sse.ts
32
+ async function* streamSSE(url, options) {
33
+ const response = await fetch(url, options);
34
+ if (!response.ok) throw new Error(`SSE request failed: ${response.status} ${response.statusText}`);
35
+ const body = response.body;
36
+ if (!body) throw new Error("SSE response has no body");
37
+ const decoder = new TextDecoder();
38
+ let buffer = "";
39
+ let currentEvent = "message";
40
+ let currentData = [];
41
+ for await (const chunk of body) {
42
+ buffer += decoder.decode(chunk, { stream: true });
43
+ const lines = buffer.split("\n");
44
+ buffer = lines.pop() ?? "";
45
+ for (const line of lines) if (line.startsWith("event:")) currentEvent = line.slice(6).trim();
46
+ else if (line.startsWith("data:")) currentData.push(line.slice(5).trim());
47
+ else if (line === "") {
48
+ if (currentData.length > 0) {
49
+ yield {
50
+ event: currentEvent,
51
+ data: currentData.join("\n")
52
+ };
53
+ currentEvent = "message";
54
+ currentData = [];
55
+ }
56
+ }
57
+ }
58
+ if (currentData.length > 0) yield {
59
+ event: currentEvent,
60
+ data: currentData.join("\n")
61
+ };
62
+ }
63
+
64
+ //#endregion
65
+ //#region src/commands/audit.ts
66
+ const API_BASE = "https://api.afterpack.dev";
67
+ const WEB_BASE = "https://afterpack.dev";
68
+ function printUsage() {
69
+ console.log(`
70
+ ${bold("Usage:")} afterpack audit ${cyan("<url>")}
71
+
72
+ Example: afterpack audit https://example.com
73
+ `);
74
+ process.exit(1);
75
+ }
76
+ function normalizeUrl(input) {
77
+ let url = input.trim();
78
+ if (!/^https?:\/\//i.test(url)) url = `https://${url}`;
79
+ try {
80
+ new URL(url);
81
+ } catch {
82
+ console.error(`\n ${red("Error:")} Invalid URL: ${input}\n`);
83
+ process.exit(1);
84
+ }
85
+ return url;
86
+ }
87
+ function clearLine() {
88
+ if (process.stdout.isTTY) process.stdout.write("\x1B[1A\x1B[2K");
89
+ }
90
+ function printPhase(label, done) {
91
+ const icon = done ? green("✓") : yellow("●");
92
+ console.log(` ${icon} ${label}`);
93
+ }
94
+ function printSummaryBox(result) {
95
+ const lines = [];
96
+ if (result.score !== void 0) {
97
+ const scoreColor = result.score >= 70 ? green : result.score >= 40 ? yellow : red;
98
+ lines.push(` Score: ${scoreColor(bold(`${result.score}/100`))}`);
99
+ lines.push("");
100
+ }
101
+ if (result.resources) {
102
+ const r = result.resources;
103
+ const parts = [];
104
+ if (r.unprotected) parts.push(`${r.unprotected} unprotected`);
105
+ if (r.sourceExposed) parts.push(`${r.sourceExposed} source exposed`);
106
+ const detail = parts.length > 0 ? ` (${parts.join(", ")})` : "";
107
+ lines.push(` Resources ${r.total ?? 0} found${detail}`);
108
+ }
109
+ if (result.findings && result.findings.length > 0) {
110
+ const counts = {};
111
+ for (const f of result.findings) {
112
+ const s = f.severity.toLowerCase();
113
+ counts[s] = (counts[s] || 0) + 1;
114
+ }
115
+ const parts = Object.entries(counts).map(([sev, n]) => `${n} ${sev}`);
116
+ lines.push(` Findings ${result.findings.length} issues (${parts.join(", ")})`);
117
+ }
118
+ if (result.techStack && result.techStack.length > 0) lines.push(` Tech Stack ${result.techStack.join(", ")}`);
119
+ if (result.readability !== void 0) lines.push(` Readability ${result.readability}/100`);
120
+ const maxLen = Math.max(...lines.map((l) => stripAnsi(l).length), 50);
121
+ const border = "─".repeat(maxLen + 4);
122
+ console.log(`\n ┌─${border}─┐`);
123
+ for (const line of lines) {
124
+ const pad = maxLen + 4 - stripAnsi(line).length;
125
+ console.log(` │ ${line}${" ".repeat(pad)} │`);
126
+ }
127
+ console.log(` └─${border}─┘`);
128
+ }
129
+ function stripAnsi(str) {
130
+ return str.replace(/\x1b\[[0-9;]*m/g, "");
131
+ }
132
+ async function audit(url) {
133
+ if (!url) {
134
+ printUsage();
135
+ return;
136
+ }
137
+ const normalizedUrl = normalizeUrl(url);
138
+ console.log(header(version));
139
+ console.log(` Scanning ${cyan(normalizedUrl)} ...\n`);
140
+ const phases = [
141
+ "Fetching page",
142
+ "Rendering JavaScript",
143
+ "Analyzing resources"
144
+ ];
145
+ let currentPhaseIndex = -1;
146
+ let extraLinesSincePhase = 0;
147
+ const result = {};
148
+ function advancePhase(phaseName) {
149
+ if (currentPhaseIndex >= 0) {
150
+ if (extraLinesSincePhase === 0) {
151
+ if (process.stdout.isTTY) clearLine();
152
+ printPhase(phases[currentPhaseIndex], true);
153
+ }
154
+ }
155
+ if (phaseName) {
156
+ let idx = phases.indexOf(phaseName);
157
+ if (idx === -1) {
158
+ phases.push(phaseName);
159
+ idx = phases.length - 1;
160
+ }
161
+ currentPhaseIndex = idx;
162
+ extraLinesSincePhase = 0;
163
+ printPhase(phaseName, false);
164
+ }
165
+ }
166
+ try {
167
+ const stream = streamSSE(`${API_BASE}/v1/scan`, {
168
+ method: "POST",
169
+ headers: { "Content-Type": "application/json" },
170
+ body: JSON.stringify({ url: normalizedUrl })
171
+ });
172
+ for await (const event of stream) {
173
+ let payload;
174
+ try {
175
+ payload = JSON.parse(event.data);
176
+ } catch {
177
+ payload = event.data;
178
+ }
179
+ switch (event.event) {
180
+ case "progress":
181
+ advancePhase(payload.phase ?? payload.message);
182
+ break;
183
+ case "finding": {
184
+ const finding = {
185
+ severity: payload.severity ?? "info",
186
+ title: payload.title ?? payload.message ?? "",
187
+ detail: payload.detail
188
+ };
189
+ if (!result.findings) result.findings = [];
190
+ result.findings.push(finding);
191
+ const sev = finding.severity.toLowerCase();
192
+ if (sev === "critical" || sev === "high") {
193
+ const color = severityColor(sev);
194
+ console.log(` ${color("⚠")} ${color(finding.severity)}: ${finding.title}`);
195
+ extraLinesSincePhase++;
196
+ }
197
+ break;
198
+ }
199
+ case "score_update":
200
+ result.score = payload.score ?? payload.value;
201
+ break;
202
+ case "complete":
203
+ if (currentPhaseIndex >= 0 && extraLinesSincePhase === 0) {
204
+ if (process.stdout.isTTY) clearLine();
205
+ printPhase(phases[currentPhaseIndex], true);
206
+ }
207
+ if (payload.score !== void 0) result.score = payload.score;
208
+ if (payload.id) result.id = payload.id;
209
+ if (payload.resources) result.resources = payload.resources;
210
+ if (payload.findings) result.findings = payload.findings;
211
+ if (payload.techStack) result.techStack = payload.techStack;
212
+ if (payload.readability !== void 0) result.readability = payload.readability;
213
+ printSummaryBox(result);
214
+ if (result.findings) {
215
+ const critical = result.findings.filter((f) => f.severity.toLowerCase() === "critical");
216
+ if (critical.length > 0) {
217
+ console.log(`\n ${red("⚠")} ${bold(`${critical.length} critical finding${critical.length > 1 ? "s" : ""}:`)}`);
218
+ for (const f of critical) console.log(` ${f.title}${f.detail ? ` (${dim(f.detail)})` : ""}`);
219
+ }
220
+ }
221
+ if (result.id) console.log(`\n Full report: ${cyan(`${WEB_BASE}/security-scanner?id=${result.id}`)}\n`);
222
+ else console.log("");
223
+ process.exit(0);
224
+ case "error":
225
+ console.error(`\n ${red("Error:")} ${payload.message ?? payload.error ?? "Scan failed"}\n`);
226
+ process.exit(1);
227
+ }
228
+ }
229
+ if (result.score !== void 0) printSummaryBox(result);
230
+ console.error(`\n ${yellow("Warning:")} Stream ended without completion event.\n`);
231
+ process.exit(1);
232
+ } catch (err) {
233
+ console.error(`\n ${red("Error:")} ${err.message ?? "Failed to connect to API"}\n`);
234
+ process.exit(1);
235
+ }
236
+ }
237
+
238
+ //#endregion
4
239
  //#region src/cli.ts
5
240
  const args = process.argv.slice(2);
6
241
  if (args.includes("--version") || args.includes("-v")) {
7
- console.log(`AfterPack v${version()}`);
242
+ console.log(`AfterPack v${version}`);
8
243
  process.exit(0);
9
244
  }
10
- if (args[0] === "audit") {
11
- console.log(`
12
- ╔═══════════════════════════════════════════════════════╗
13
- ║ AfterPack v${version().padEnd(42)}║
14
- ╚═══════════════════════════════════════════════════════╝
15
-
16
- Audit command coming soon!
17
-
18
- This will analyze your site and reveal what AI can
19
- extract from your client-side JavaScript.
245
+ const command = args[0];
246
+ switch (command) {
247
+ case "audit":
248
+ await audit(args[1]);
249
+ break;
250
+ default:
251
+ console.log(`
252
+ AfterPack v${version}
20
253
 
21
254
  ┌─────────────────────────────────────────────────────┐
22
- │ Join the waitlist to get notified: │
23
- │ https://www.afterpack.dev │
255
+ │ Launching soon — join the waitlist for day-one │
256
+ │ access: https://www.afterpack.dev │
24
257
  └─────────────────────────────────────────────────────┘
25
- `);
26
- process.exit(0);
27
- }
28
- console.log(`
29
- ╔═══════════════════════════════════════════════════════╗
30
- ║ AfterPack v${version().padEnd(42)}║
31
- ╚═══════════════════════════════════════════════════════╝
32
258
 
33
- Coming soon: High-performance JavaScript protection.
259
+ Usage: afterpack <command> [options]
34
260
 
35
- Secure your client-side logic from AI extraction,
36
- reverse engineering, vulnerability scanning, and
37
- automated scraping.
261
+ Commands:
262
+ audit <url> Audit your frontend for leaked secrets & sensitive data
38
263
 
39
- ┌─────────────────────────────────────────────────────┐
40
- │ Join the waitlist for early access: │
41
- │ https://www.afterpack.dev │
42
- └─────────────────────────────────────────────────────┘
264
+ Options:
265
+ -v, --version Show version
43
266
  `);
267
+ break;
268
+ }
44
269
 
45
270
  //#endregion
46
271
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { version } from \"@afterpack/core\";\n\nconst args = process.argv.slice(2);\n\n// Version flag\nif (args.includes(\"--version\") || args.includes(\"-v\")) {\n console.log(`AfterPack v${version()}`);\n process.exit(0);\n}\n\n// Audit command\nif (args[0] === \"audit\") {\n console.log(`\n ╔═══════════════════════════════════════════════════════╗\n ║ AfterPack v${version().padEnd(42)}║\n ╚═══════════════════════════════════════════════════════╝\n\n Audit command coming soon!\n\n This will analyze your site and reveal what AI can\n extract from your client-side JavaScript.\n\n ┌─────────────────────────────────────────────────────┐\n │ Join the waitlist to get notified: │\n │ https://www.afterpack.dev │\n └─────────────────────────────────────────────────────┘\n`);\n process.exit(0);\n}\n\n// Default: show main waitlist message\nconsole.log(`\n ╔═══════════════════════════════════════════════════════╗\n ║ AfterPack v${version().padEnd(42)}║\n ╚═══════════════════════════════════════════════════════╝\n\n Coming soon: High-performance JavaScript protection.\n\n Secure your client-side logic from AI extraction,\n reverse engineering, vulnerability scanning, and\n automated scraping.\n\n ┌─────────────────────────────────────────────────────┐\n │ Join the waitlist for early access: │\n │ https://www.afterpack.dev │\n └─────────────────────────────────────────────────────┘\n`);\n"],"mappings":";;;;AAIA,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE;AAGlC,IAAI,KAAK,SAAS,YAAY,IAAI,KAAK,SAAS,KAAK,EAAE;AACrD,SAAQ,KAAK,aAAa,SAAS,CAAC,EAAE;AACtC,SAAQ,KAAK,EAAE;AAChB;AAGD,IAAI,KAAK,OAAO,SAAS;AACvB,SAAQ,KAAK;;kBAEG,SAAS,CAAC,OAAO,GAAG,CAAC;;;;;;;;;;;;EAYrC;AACA,SAAQ,KAAK,EAAE;AAChB;AAGD,QAAQ,KAAK;;kBAEK,SAAS,CAAC,OAAO,GAAG,CAAC;;;;;;;;;;;;;EAarC"}
1
+ {"version":3,"file":"cli.js","names":["open: string","close: string","text: string","severity: string","version: string","version","url: string","options?: RequestInit","currentData: string[]","input: string","label: string","done: boolean","result: ScanResult","lines: string[]","parts: string[]","counts: Record<string, number>","str: string","url: string | undefined","phaseName?: string","payload: any","finding: Finding","err: any"],"sources":["../package.json","../src/utils/format.ts","../src/utils/sse.ts","../src/commands/audit.ts","../src/cli.ts"],"sourcesContent":["{\n \"name\": \"afterpack\",\n \"version\": \"0.0.13\",\n \"description\": \"A high-performance post-build JavaScript obfuscator and minifier\",\n \"type\": \"module\",\n \"main\": \"./dist/cli.js\",\n \"bin\": {\n \"afterpack\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsdown src/cli.ts --format esm --dts\",\n \"dev\": \"tsdown src/cli.ts --format esm --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"pnpm build\",\n \"prepack\": \"tsx scripts/prepack.ts\",\n \"postpack\": \"tsx scripts/postpack.ts\",\n \"release\": \"npm version patch && git push && git push --tags\",\n \"release:minor\": \"npm version minor && git push && git push --tags\",\n \"release:major\": \"npm version major && git push && git push --tags\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\"\n },\n \"keywords\": [\n \"obfuscator\",\n \"minifier\",\n \"javascript\",\n \"protection\",\n \"security\",\n \"afterpack\"\n ],\n \"author\": \"AfterPack <hello@afterpack.dev>\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/afterpack-dev/afterpack.git\"\n },\n \"homepage\": \"https://afterpack.dev\",\n \"bugs\": {\n \"url\": \"https://github.com/afterpack-dev/afterpack/issues\"\n },\n \"engines\": {\n \"node\": \">=18\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.4.4\",\n \"@types/node\": \"^25.0.9\",\n \"tsdown\": \"^0.11.0\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.0.0\",\n \"vitest\": \"^2.0.0\"\n }\n}\n","const isColorSupported =\n process.env.FORCE_COLOR !== \"0\" &&\n !process.env.NO_COLOR &&\n (process.env.FORCE_COLOR !== undefined || process.stdout.isTTY);\n\nfunction ansi(open: string, close: string) {\n return (text: string) =>\n isColorSupported ? `\\x1b[${open}m${text}\\x1b[${close}m` : text;\n}\n\nexport const bold = ansi(\"1\", \"22\");\nexport const dim = ansi(\"2\", \"22\");\nexport const red = ansi(\"31\", \"39\");\nexport const green = ansi(\"32\", \"39\");\nexport const yellow = ansi(\"33\", \"39\");\nexport const cyan = ansi(\"36\", \"39\");\n\nexport function severityColor(severity: string): (text: string) => string {\n switch (severity.toLowerCase()) {\n case \"critical\":\n return red;\n case \"high\":\n return red;\n case \"medium\":\n return yellow;\n case \"low\":\n return cyan;\n default:\n return dim;\n }\n}\n\nexport function header(version: string): string {\n return `\\n ${bold(`AfterPack v${version}`)} — Security Analyzer\\n`;\n}\n","export interface SSEEvent {\n event: string;\n data: string;\n}\n\nexport async function* streamSSE(\n url: string,\n options?: RequestInit,\n): AsyncGenerator<SSEEvent> {\n const response = await fetch(url, options);\n\n if (!response.ok) {\n throw new Error(\n `SSE request failed: ${response.status} ${response.statusText}`,\n );\n }\n\n const body = response.body;\n if (!body) {\n throw new Error(\"SSE response has no body\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent = \"message\";\n let currentData: string[] = [];\n\n for await (const chunk of body) {\n buffer += decoder.decode(chunk, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n // Keep the last incomplete line in the buffer\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event:\")) {\n currentEvent = line.slice(6).trim();\n } else if (line.startsWith(\"data:\")) {\n currentData.push(line.slice(5).trim());\n } else if (line === \"\") {\n // Empty line = end of event\n if (currentData.length > 0) {\n yield { event: currentEvent, data: currentData.join(\"\\n\") };\n currentEvent = \"message\";\n currentData = [];\n }\n }\n }\n }\n\n // Flush any remaining event\n if (currentData.length > 0) {\n yield { event: currentEvent, data: currentData.join(\"\\n\") };\n }\n}\n","import { version } from \"../../package.json\";\nimport {\n bold,\n cyan,\n dim,\n green,\n header,\n red,\n severityColor,\n yellow,\n} from \"../utils/format.js\";\nimport { streamSSE } from \"../utils/sse.js\";\n\nconst API_BASE = \"https://api.afterpack.dev\";\nconst WEB_BASE = \"https://afterpack.dev\";\n\ninterface Finding {\n severity: string;\n title: string;\n detail?: string;\n}\n\ninterface ScanResult {\n id?: string;\n score?: number;\n resources?: { total?: number; unprotected?: number; sourceExposed?: number };\n findings?: Finding[];\n techStack?: string[];\n readability?: number;\n}\n\nfunction printUsage(): void {\n console.log(`\n ${bold(\"Usage:\")} afterpack audit ${cyan(\"<url>\")}\n\n Example: afterpack audit https://example.com\n`);\n process.exit(1);\n}\n\nfunction normalizeUrl(input: string): string {\n let url = input.trim();\n if (!/^https?:\\/\\//i.test(url)) {\n url = `https://${url}`;\n }\n // Validate\n try {\n new URL(url);\n } catch {\n console.error(`\\n ${red(\"Error:\")} Invalid URL: ${input}\\n`);\n process.exit(1);\n }\n return url;\n}\n\nfunction clearLine(): void {\n if (process.stdout.isTTY) {\n process.stdout.write(\"\\x1b[1A\\x1b[2K\");\n }\n}\n\nfunction printPhase(label: string, done: boolean): void {\n const icon = done ? green(\"✓\") : yellow(\"●\");\n console.log(` ${icon} ${label}`);\n}\n\nfunction printSummaryBox(result: ScanResult): void {\n const lines: string[] = [];\n\n // Score\n if (result.score !== undefined) {\n const scoreColor =\n result.score >= 70 ? green : result.score >= 40 ? yellow : red;\n lines.push(` Score: ${scoreColor(bold(`${result.score}/100`))}`);\n lines.push(\"\");\n }\n\n // Resources\n if (result.resources) {\n const r = result.resources;\n const parts: string[] = [];\n if (r.unprotected) parts.push(`${r.unprotected} unprotected`);\n if (r.sourceExposed) parts.push(`${r.sourceExposed} source exposed`);\n const detail = parts.length > 0 ? ` (${parts.join(\", \")})` : \"\";\n lines.push(` Resources ${r.total ?? 0} found${detail}`);\n }\n\n // Findings\n if (result.findings && result.findings.length > 0) {\n const counts: Record<string, number> = {};\n for (const f of result.findings) {\n const s = f.severity.toLowerCase();\n counts[s] = (counts[s] || 0) + 1;\n }\n const parts = Object.entries(counts).map(([sev, n]) => `${n} ${sev}`);\n lines.push(\n ` Findings ${result.findings.length} issues (${parts.join(\", \")})`,\n );\n }\n\n // Tech Stack\n if (result.techStack && result.techStack.length > 0) {\n lines.push(` Tech Stack ${result.techStack.join(\", \")}`);\n }\n\n // Readability\n if (result.readability !== undefined) {\n lines.push(` Readability ${result.readability}/100`);\n }\n\n // Box\n const maxLen = Math.max(...lines.map((l) => stripAnsi(l).length), 50);\n const border = \"─\".repeat(maxLen + 4);\n\n console.log(`\\n ┌─${border}─┐`);\n for (const line of lines) {\n const pad = maxLen + 4 - stripAnsi(line).length;\n console.log(` │ ${line}${\" \".repeat(pad)} │`);\n }\n console.log(` └─${border}─┘`);\n}\n\nfunction stripAnsi(str: string): string {\n return str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\nexport async function audit(url: string | undefined): Promise<void> {\n if (!url) {\n printUsage();\n return;\n }\n\n const normalizedUrl = normalizeUrl(url);\n\n console.log(header(version));\n console.log(` Scanning ${cyan(normalizedUrl)} ...\\n`);\n\n const phases = [\n \"Fetching page\",\n \"Rendering JavaScript\",\n \"Analyzing resources\",\n ];\n let currentPhaseIndex = -1;\n let extraLinesSincePhase = 0;\n const result: ScanResult = {};\n\n function advancePhase(phaseName?: string): void {\n // Mark current phase as done\n if (currentPhaseIndex >= 0) {\n if (extraLinesSincePhase === 0) {\n // No findings printed — replace ● with ✓ in place\n if (process.stdout.isTTY) clearLine();\n printPhase(phases[currentPhaseIndex], true);\n }\n // If findings were printed, the ● line stays — no ✓ needed\n }\n\n if (phaseName) {\n // Find phase index or add it\n let idx = phases.indexOf(phaseName);\n if (idx === -1) {\n phases.push(phaseName);\n idx = phases.length - 1;\n }\n currentPhaseIndex = idx;\n extraLinesSincePhase = 0;\n printPhase(phaseName, false);\n }\n }\n\n try {\n const stream = streamSSE(`${API_BASE}/v1/scan`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ url: normalizedUrl }),\n });\n\n for await (const event of stream) {\n let payload: any;\n try {\n payload = JSON.parse(event.data);\n } catch {\n payload = event.data;\n }\n\n switch (event.event) {\n case \"progress\":\n advancePhase(payload.phase ?? payload.message);\n break;\n\n case \"finding\": {\n const finding: Finding = {\n severity: payload.severity ?? \"info\",\n title: payload.title ?? payload.message ?? \"\",\n detail: payload.detail,\n };\n if (!result.findings) result.findings = [];\n result.findings.push(finding);\n\n // Print critical/high findings immediately\n const sev = finding.severity.toLowerCase();\n if (sev === \"critical\" || sev === \"high\") {\n const color = severityColor(sev);\n console.log(\n ` ${color(\"⚠\")} ${color(finding.severity)}: ${finding.title}`,\n );\n extraLinesSincePhase++;\n }\n break;\n }\n\n case \"score_update\":\n result.score = payload.score ?? payload.value;\n break;\n\n case \"complete\":\n // Finalize current phase\n if (currentPhaseIndex >= 0 && extraLinesSincePhase === 0) {\n if (process.stdout.isTTY) clearLine();\n printPhase(phases[currentPhaseIndex], true);\n }\n\n // Merge final data\n if (payload.score !== undefined) result.score = payload.score;\n if (payload.id) result.id = payload.id;\n if (payload.resources) result.resources = payload.resources;\n if (payload.findings) result.findings = payload.findings;\n if (payload.techStack) result.techStack = payload.techStack;\n if (payload.readability !== undefined)\n result.readability = payload.readability;\n\n printSummaryBox(result);\n\n // Critical findings summary\n if (result.findings) {\n const critical = result.findings.filter(\n (f) => f.severity.toLowerCase() === \"critical\",\n );\n if (critical.length > 0) {\n console.log(\n `\\n ${red(\"⚠\")} ${bold(\n `${critical.length} critical finding${\n critical.length > 1 ? \"s\" : \"\"\n }:`,\n )}`,\n );\n for (const f of critical) {\n console.log(\n ` ${f.title}${f.detail ? ` (${dim(f.detail)})` : \"\"}`,\n );\n }\n }\n }\n\n // Report link\n if (result.id) {\n console.log(\n `\\n Full report: ${cyan(\n `${WEB_BASE}/security-scanner?id=${result.id}`,\n )}\\n`,\n );\n } else {\n console.log(\"\");\n }\n\n process.exit(0);\n\n case \"error\":\n console.error(\n `\\n ${red(\"Error:\")} ${\n payload.message ?? payload.error ?? \"Scan failed\"\n }\\n`,\n );\n process.exit(1);\n }\n }\n\n // If stream ends without a complete event, still show what we have\n if (result.score !== undefined) {\n printSummaryBox(result);\n }\n console.error(\n `\\n ${yellow(\"Warning:\")} Stream ended without completion event.\\n`,\n );\n process.exit(1);\n } catch (err: any) {\n console.error(\n `\\n ${red(\"Error:\")} ${err.message ?? \"Failed to connect to API\"}\\n`,\n );\n process.exit(1);\n }\n}\n","#!/usr/bin/env node\n\nimport { version } from \"../package.json\";\nimport { audit } from \"./commands/audit.js\";\n\nconst args = process.argv.slice(2);\n\n// Version flag\nif (args.includes(\"--version\") || args.includes(\"-v\")) {\n console.log(`AfterPack v${version}`);\n process.exit(0);\n}\n\n// Command routing\nconst command = args[0];\n\nswitch (command) {\n case \"audit\":\n await audit(args[1]);\n break;\n\n default:\n console.log(`\n AfterPack v${version}\n\n ┌─────────────────────────────────────────────────────┐\n │ Launching soon — join the waitlist for day-one │\n │ access: https://www.afterpack.dev │\n └─────────────────────────────────────────────────────┘\n\n Usage: afterpack <command> [options]\n\n Commands:\n audit <url> Audit your frontend for leaked secrets & sensitive data\n\n Options:\n -v, --version Show version\n`);\n break;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "afterpack",
3
- "version": "0.0.10",
3
+ "version": "0.0.13",
4
4
  "description": "A high-performance post-build JavaScript obfuscator and minifier",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
@@ -10,13 +10,7 @@
10
10
  "files": [
11
11
  "dist"
12
12
  ],
13
- "scripts": {
14
- "build": "tsdown src/cli.ts --format esm --dts",
15
- "dev": "tsdown src/cli.ts --format esm --watch",
16
- "test": "vitest run",
17
- "test:watch": "vitest",
18
- "prepublishOnly": "pnpm build"
19
- },
13
+ "scripts": {},
20
14
  "keywords": [
21
15
  "obfuscator",
22
16
  "minifier",
@@ -38,12 +32,11 @@
38
32
  "engines": {
39
33
  "node": ">=18"
40
34
  },
41
- "dependencies": {
42
- "@afterpack/core": "^0.0.9"
43
- },
44
35
  "devDependencies": {
36
+ "@biomejs/biome": "^2.4.4",
45
37
  "@types/node": "^25.0.9",
46
38
  "tsdown": "^0.11.0",
39
+ "tsx": "^4.21.0",
47
40
  "typescript": "^5.0.0",
48
41
  "vitest": "^2.0.0"
49
42
  }