@bigbertha/sdk 0.1.2 → 0.1.4

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 CHANGED
@@ -55,7 +55,14 @@ import { withBertha } from "@bigbertha/sdk/next/config"
55
55
  export default withBertha({ /* your config */ })
56
56
  ```
57
57
 
58
- `withBertha` options: `release`, `keepMaps` (default false), `disableUpload`.
58
+ `withBertha` options: `release`, `keepMaps` (default false), `disableUpload`. It also
59
+ stamps the environment, so a Vercel preview reports as staging.
60
+
61
+ For your own options (`beforeSend`, `tags`, `sampleRate`), call `init` from
62
+ `@bigbertha/sdk/browser` in `instrumentation-client.ts` instead of importing
63
+ `next/client`, and `init` from `@bigbertha/sdk/node` in `register()`. The build plugin
64
+ works the same either way, and it keeps the debug ids another tool (such as Sentry's
65
+ plugin) already gave the bundles, so both can run side by side.
59
66
 
60
67
  ## Vite (browser)
61
68
 
@@ -118,7 +125,7 @@ await flush()
118
125
  | --- | --- | --- |
119
126
  | `key`, `url` | from env (Node, edge) | Required in the browser. |
120
127
  | `release` | stamped at build time | The plugins stamp it from `BERTHA_RELEASE`, the CI's commit sha or `git HEAD`. |
121
- | `environment` | `BERTHA_ENVIRONMENT` or `NODE_ENV` (Node, edge), else production | Free text is mapped to production, staging or development. |
128
+ | `environment` | `BERTHA_ENVIRONMENT`, `VERCEL_ENV` or `NODE_ENV` (Node, edge; `withBertha` stamps it for the browser), else production | Free text is mapped to production, staging or development. |
122
129
  | `sampleRate` | `1` | Share of events to send. |
123
130
  | `beforeSend` | | Change an event, or return `null` to drop it. |
124
131
  | `scrub` | `true` | Redact emails, bearer tokens and secret query values. |
@@ -32,8 +32,11 @@ export interface Report {
32
32
  export declare function processDirectory(o: ProcessOptions): Promise<Report>;
33
33
  /** A stable UUID from the bundle's content, so rebuilding the same code gives the same id. */
34
34
  export declare function idFor(code: string): string;
35
- /** The line appended to a bundle. Runs once when the bundle loads; never throws. */
36
- export declare function snippet(debugId: string, release?: string): string;
35
+ /**
36
+ * The line appended to a bundle. Runs once when the bundle loads; never throws.
37
+ * `comment: false` leaves out the `//# debugId=` comment, for a bundle that has one.
38
+ */
39
+ export declare function snippet(debugId: string, release?: string, comment?: boolean): string;
37
40
  /** Appends the snippet, before a trailing sourceMappingURL comment if there is one. */
38
- export declare function stamp(code: string, debugId: string, release?: string): string;
41
+ export declare function stamp(code: string, debugId: string, release?: string, comment?: boolean): string;
39
42
  export declare function reason(res: Response): Promise<string>;
@@ -2,7 +2,8 @@
2
2
  // to bertha, and take them out of the output so they aren't served.
3
3
  //
4
4
  // The id goes in a line appended to the bundle. Appending (never prepending) keeps
5
- // every existing mapping valid. At runtime that line records the bundle's own URL
5
+ // every existing mapping valid. A bundle that already has a debug id (from Sentry's
6
+ // plugin, or Rollup's `sourcemapDebugIds`) keeps it, and still gets bertha's line. At runtime that line records the bundle's own URL
6
7
  // from `new Error().stack` against its id, so events can name the map for each frame
7
8
  // wherever the file was served from.
8
9
  import { createHash } from "node:crypto";
@@ -15,6 +16,8 @@ const JS = /\.(?:m|c)?js$/;
15
16
  const ORPHAN_MAP = /\.(?:m|c)?js\.map$/;
16
17
  const MAP_COMMENT = /\n?\/\/[#@] sourceMappingURL=(\S+)[ \t]*\r?\n?$/;
17
18
  const DEBUG_ID_COMMENT = /\/\/# debugId=([0-9a-fA-F-]{36})/;
19
+ /** In every bundle this step has already stamped, so a second run changes nothing. */
20
+ const REGISTERED = "g.__berthaIds=";
18
21
  const CONCURRENCY = 4;
19
22
  const UPLOAD_TIMEOUT_MS = 60_000;
20
23
  export async function processDirectory(o) {
@@ -44,7 +47,7 @@ export async function processDirectory(o) {
44
47
  const json = JSON.parse(map.text);
45
48
  json.debugId = debugId;
46
49
  const mapText = JSON.stringify(json);
47
- let out = DEBUG_ID_COMMENT.test(code) ? code : stamp(code, debugId, o.release);
50
+ let out = code.includes(REGISTERED) ? code : stamp(code, debugId, o.release, !DEBUG_ID_COMMENT.test(code));
48
51
  if (keep && map.path)
49
52
  await writeFile(map.path, mapText);
50
53
  else if (!keep) {
@@ -116,18 +119,22 @@ export function idFor(code) {
116
119
  const variant = ((parseInt(h[16], 16) & 0x3) | 0x8).toString(16);
117
120
  return `${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-${variant}${h.slice(17, 20)}-${h.slice(20, 32)}`;
118
121
  }
119
- /** The line appended to a bundle. Runs once when the bundle loads; never throws. */
120
- export function snippet(debugId, release) {
122
+ /**
123
+ * The line appended to a bundle. Runs once when the bundle loads; never throws.
124
+ * `comment: false` leaves out the `//# debugId=` comment, for a bundle that has one.
125
+ */
126
+ export function snippet(debugId, release, comment = true) {
121
127
  const r = release ? `g.__berthaRelease=${JSON.stringify(release)}` : "";
122
128
  return (`;!function(){try{var g=typeof globalThis<"u"?globalThis:self,s=(new Error).stack;` +
123
- `s&&((g.__berthaIds=g.__berthaIds||{})[s]="${debugId}");${r}}catch(e){}}();\n//# debugId=${debugId}\n`);
129
+ `s&&((${REGISTERED}g.__berthaIds||{})[s]="${debugId}");${r}}catch(e){}}();\n` +
130
+ (comment ? `//# debugId=${debugId}\n` : ""));
124
131
  }
125
132
  /** Appends the snippet, before a trailing sourceMappingURL comment if there is one. */
126
- export function stamp(code, debugId, release) {
133
+ export function stamp(code, debugId, release, comment = true) {
127
134
  const m = MAP_COMMENT.exec(code);
128
135
  const body = m ? code.slice(0, m.index) : code;
129
136
  const tail = m ? m[0].replace(/^\n?/, "") : "";
130
- return `${body.endsWith("\n") ? body : `${body}\n`}${snippet(debugId, release)}${tail}`;
137
+ return `${body.endsWith("\n") ? body : `${body}\n`}${snippet(debugId, release, comment)}${tail}`;
131
138
  }
132
139
  async function upload(o, q, map) {
133
140
  const f = o.fetch ?? fetch;
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.2";
1
+ export declare const VERSION = "0.1.4";
@@ -1 +1 @@
1
- export const VERSION = "0.1.2";
1
+ export const VERSION = "0.1.4";
@@ -18,7 +18,7 @@ export function init(options = {}) {
18
18
  key,
19
19
  url,
20
20
  release: options.release ?? firstEnv("BERTHA_RELEASE"),
21
- environment: options.environment ?? firstEnv("BERTHA_ENVIRONMENT", "NODE_ENV"),
21
+ environment: options.environment ?? firstEnv("BERTHA_ENVIRONMENT", "VERCEL_ENV", "NODE_ENV"),
22
22
  }, fetchTransport({ url, key, gzip: streamGzip }), { name: "bertha.edge", version: VERSION }, { runtime: "Edge" });
23
23
  setClient(client);
24
24
  return client;
@@ -23,7 +23,7 @@ else if (key && url) {
23
23
  key,
24
24
  url,
25
25
  release: read(() => process.env.BERTHA_RELEASE),
26
- environment: read(() => process.env.NODE_ENV),
26
+ environment: read(() => process.env.BERTHA_ENVIRONMENT) ?? read(() => process.env.NODE_ENV),
27
27
  });
28
28
  }
29
29
  else {
@@ -1,6 +1,7 @@
1
1
  // @bigbertha/sdk/next/config, for next.config.ts:
2
2
  // export default withBertha(nextConfig)
3
- // Turns on source maps for production builds, stamps the release, and after each
3
+ // Turns on source maps for production builds, stamps the release and environment
4
+ // (BERTHA_ENVIRONMENT, else Vercel's production or preview), and after each
4
5
  // build gives bundles debug ids, uploads the maps and removes them from `.next`.
5
6
  import { join } from "node:path";
6
7
  import { detectRelease, processDirectory } from "../build/index.js";
@@ -8,11 +9,16 @@ import { firstEnv } from "../core/env.js";
8
9
  export function withBertha(config = {}, options = {}) {
9
10
  const c = config;
10
11
  const release = options.release ?? detectRelease();
12
+ const environment = firstEnv("BERTHA_ENVIRONMENT", "VERCEL_ENV");
11
13
  const previous = c.compiler?.runAfterProductionCompile;
12
14
  const next = {
13
15
  ...c,
14
16
  productionBrowserSourceMaps: true,
15
- env: { ...c.env, ...(release ? { BERTHA_RELEASE: release } : {}) },
17
+ env: {
18
+ ...c.env,
19
+ ...(release ? { BERTHA_RELEASE: release } : {}),
20
+ ...(environment ? { BERTHA_ENVIRONMENT: environment } : {}),
21
+ },
16
22
  experimental: { ...c.experimental, serverSourceMaps: true },
17
23
  compiler: {
18
24
  ...c.compiler,
@@ -25,7 +25,7 @@ export function init(options = {}) {
25
25
  key,
26
26
  url,
27
27
  release: options.release ?? firstEnv("BERTHA_RELEASE"),
28
- environment: options.environment ?? firstEnv("BERTHA_ENVIRONMENT", "NODE_ENV"),
28
+ environment: options.environment ?? firstEnv("BERTHA_ENVIRONMENT", "VERCEL_ENV", "NODE_ENV"),
29
29
  };
30
30
  const client = new Client(opts, fetchTransport({ url, key, gzip: async (body) => new Uint8Array(gzipSync(body)) }), { name: "bertha.node", version: VERSION }, { runtime: `Node ${process.versions.node}`, os: `${platform()} ${arch()}` });
31
31
  setClient(client);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigbertha/sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Error reporting for bertha: browser, Node, edge and Next.js, with source maps by debug id.",
5
5
  "license": "MIT",
6
6
  "repository": {