snapfail 0.0.16 → 0.0.18

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/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # snapfail
2
+
3
+ CLI for [snapfail](https://snapfail.com) — automatic error capture and AI diagnostics for your web app.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g snapfail
9
+ # or
10
+ bun add -g snapfail
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ # Set up snapfail in your project (one time)
17
+ snapfail init
18
+
19
+ # View unresolved incidents
20
+ snapfail incidents
21
+
22
+ # View incident detail
23
+ snapfail incident <id>
24
+
25
+ # Get AI diagnosis
26
+ snapfail explain <id>
27
+ ```
28
+
29
+ `snapfail init` detects your framework, authenticates, lets you pick or create a project, writes `SNAPFAIL_PROJECT_KEY` to `.env`, and injects the browser SDK into your config automatically.
30
+
31
+ ## Documentation
32
+
33
+ Full docs at [snapfail.com](https://snapfail.com)
package/dist/index.js CHANGED
@@ -164,6 +164,14 @@ async function fetchIncident(config, id) {
164
164
  });
165
165
  return data;
166
166
  }
167
+ async function deleteGroup(config, id) {
168
+ const url = `${config.endpoint}/api/incidents/${id}`;
169
+ const data = await apiFetch(url, {
170
+ method: "DELETE",
171
+ headers: { "X-Project-Key": config.projectKey }
172
+ });
173
+ return data !== null;
174
+ }
167
175
  async function fetchSample(config, groupId, sampleIndex) {
168
176
  const url = `${config.endpoint}/api/incidents/${groupId}/samples/${sampleIndex}`;
169
177
  const data = await apiFetch(url, {
@@ -287,6 +295,20 @@ async function runIncidents(opts) {
287
295
  // src/commands/incident.ts
288
296
  async function runIncident(opts) {
289
297
  const config = loadConfig();
298
+ if (opts.delete) {
299
+ const ok = await deleteGroup(config, opts.id);
300
+ if (!ok) {
301
+ console.error(`Incident group ${opts.id} not found.`);
302
+ process.exit(1);
303
+ }
304
+ if (opts.json) {
305
+ process.stdout.write(JSON.stringify({ ok: true, deleted: opts.id }) + `
306
+ `);
307
+ } else {
308
+ console.log(`Deleted incident group ${opts.id} and all related data.`);
309
+ }
310
+ return;
311
+ }
290
312
  if (opts.sample != null) {
291
313
  const s = await fetchSample(config, opts.id, opts.sample);
292
314
  if (!s) {
@@ -2056,6 +2078,34 @@ function injectAstroAdapter(configPath) {
2056
2078
  }
2057
2079
  writeFileSync3(configPath, src, "utf-8");
2058
2080
  }
2081
+ function injectNextConfig(cwd) {
2082
+ const candidates = ["next.config.ts", "next.config.mjs", "next.config.js"];
2083
+ const cfgFile = candidates.find((n2) => existsSync3(resolve2(cwd, n2)));
2084
+ if (!cfgFile)
2085
+ return;
2086
+ const cfgPath = resolve2(cwd, cfgFile);
2087
+ let src = readFileSync3(cfgPath, "utf-8");
2088
+ if (src.includes("withSnapfail"))
2089
+ return;
2090
+ const isEsm = cfgFile.endsWith(".mjs") || cfgFile.endsWith(".ts");
2091
+ const projectKeyExpr = isEsm ? `import.meta.env?.NEXT_PUBLIC_SNAPFAIL_PROJECT_KEY ?? process.env.NEXT_PUBLIC_SNAPFAIL_PROJECT_KEY ?? ""` : `process.env.NEXT_PUBLIC_SNAPFAIL_PROJECT_KEY ?? ""`;
2092
+ if (isEsm) {
2093
+ src = `import { withSnapfail } from "@snapfail/next";
2094
+ ` + src;
2095
+ src = src.replace(/export\s+default\s+(\w+);/, `export default withSnapfail(${projectKeyExpr})($1);`);
2096
+ if (!src.includes("withSnapfail(")) {
2097
+ src = src.replace(/export\s+default\s+(\{)/, `export default withSnapfail(${projectKeyExpr})($1`);
2098
+ }
2099
+ } else {
2100
+ src = `const { withSnapfail } = require("@snapfail/next");
2101
+ ` + src;
2102
+ src = src.replace(/module\.exports\s*=\s*(\w+);/, `module.exports = withSnapfail(${projectKeyExpr})($1);`);
2103
+ if (!src.includes("withSnapfail(")) {
2104
+ src = src.replace(/module\.exports\s*=\s*(\{)/, `module.exports = withSnapfail(${projectKeyExpr})($1`);
2105
+ }
2106
+ }
2107
+ writeFileSync3(cfgPath, src, "utf-8");
2108
+ }
2059
2109
  function injectNextProvider(cwd) {
2060
2110
  const providerSrc = `"use client";
2061
2111
  import { useEffect } from "react";
@@ -2065,7 +2115,9 @@ export function SnapfailProvider() {
2065
2115
  useEffect(() => {
2066
2116
  initSnapfail({
2067
2117
  projectKey: process.env.NEXT_PUBLIC_SNAPFAIL_PROJECT_KEY ?? "",
2118
+ endpoint: process.env.NEXT_PUBLIC_SNAPFAIL_ENDPOINT ?? "https://app.snapfail.com",
2068
2119
  environment: process.env.NODE_ENV === "production" ? "prod" : "dev",
2120
+ buildId: process.env.NEXT_PUBLIC_SNAPFAIL_BUILD_ID,
2069
2121
  });
2070
2122
  }, []);
2071
2123
  return null;
@@ -2210,6 +2262,8 @@ async function runInit(cwd = process.cwd()) {
2210
2262
  } catch {
2211
2263
  spinner2.stop("Skipping install (not yet on npm).");
2212
2264
  }
2265
+ injectNextConfig(cwd);
2266
+ log.success("Injected withSnapfail into next.config");
2213
2267
  injectNextProvider(cwd);
2214
2268
  log.success("Created snapfail-provider.tsx");
2215
2269
  log.warn("Add <SnapfailProvider /> to your root layout.");
@@ -2242,7 +2296,8 @@ async function fetchDiagnosis(config, id, force) {
2242
2296
  try {
2243
2297
  res = await fetch(url.toString(), {
2244
2298
  method: "POST",
2245
- headers: { "X-Project-Key": config.projectKey }
2299
+ headers: { "X-Project-Key": config.projectKey, "Content-Type": "application/json" },
2300
+ body: "{}"
2246
2301
  });
2247
2302
  } catch (err) {
2248
2303
  throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);
@@ -2318,7 +2373,7 @@ function parseArgs(argv) {
2318
2373
  }
2319
2374
  return { command, args, flags };
2320
2375
  }
2321
- var VERSION = "0.0.16";
2376
+ var VERSION = "0.0.18";
2322
2377
  async function main() {
2323
2378
  const { command, args, flags } = parseArgs(process.argv);
2324
2379
  const json = flags["json"] === true;
@@ -2348,7 +2403,7 @@ async function main() {
2348
2403
  }
2349
2404
  const sampleFlag = flags["sample"];
2350
2405
  const sample = typeof sampleFlag === "string" ? parseInt(sampleFlag) : undefined;
2351
- await runIncident({ id, sample, json });
2406
+ await runIncident({ id, sample, json, delete: flags["delete"] === true });
2352
2407
  return;
2353
2408
  }
2354
2409
  if (command === "explain") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snapfail",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "type": "module",
5
5
  "description": "CLI for snapfail — project setup, incident inspection and AI diagnostics",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -30,7 +30,7 @@ function parseArgs(argv: string[]): {
30
30
  return { command, args, flags };
31
31
  }
32
32
 
33
- const VERSION = "0.0.16";
33
+ const VERSION = "0.0.18";
34
34
 
35
35
  async function main(): Promise<void> {
36
36
  const { command, args, flags } = parseArgs(process.argv);
@@ -66,7 +66,7 @@ async function main(): Promise<void> {
66
66
  const sampleFlag = flags["sample"];
67
67
  const sample =
68
68
  typeof sampleFlag === "string" ? parseInt(sampleFlag) : undefined;
69
- await runIncident({ id, sample, json });
69
+ await runIncident({ id, sample, json, delete: flags["delete"] === true });
70
70
  return;
71
71
  }
72
72