@autter/runtime-cli 1.2.0 → 1.3.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.
package/README.md CHANGED
@@ -2,18 +2,23 @@
2
2
 
3
3
  Readable stack traces for Autter Runtime. Gives every JS bundle a
4
4
  content-addressed **debug id** and uploads its **source map** to the ingester,
5
- so minified browser frames resolve to your original source, even when the
5
+ so minified browser frames and bundled Node and edge frames resolve to your
6
+ original source, with the lines around each frame of your code, even when the
6
7
  release your SDK reports doesn't match the upload.
7
8
 
8
9
  - `autter-runtime sourcemaps setup` wires it into a project; `verify` checks a build
9
- - `autter-runtime sourcemaps inject <dir>` / `upload <dir>` CLI
10
- - Vite/Rollup plugin: `@autter/runtime-cli/vite`
11
- - webpack 5 plugin: `@autter/runtime-cli/webpack`
10
+ - `autter-runtime sourcemaps inject <dir>` / `upload <dir>` CLI, for browser
11
+ and server output alike (tsup, esbuild, a Worker)
12
+ - Vite/Rollup plugin: `@autter/runtime-cli/vite` (SSR builds and server
13
+ environments too)
14
+ - webpack 5 plugin: `@autter/runtime-cli/webpack` (Node targets too)
12
15
  - Next.js wrapper: `withAutterSourceMaps` from `@autter/runtime-cli/next`
16
+ (client and `.next/server` output)
13
17
 
14
18
  Bundlers are optional peer dependencies. Debug ids need ingester 1.6.0+ and
15
19
  `@autter/runtime-browser` 1.6.0+ (older ingesters still match `.js` maps by
16
- release + path). Full guide: [docs/SOURCE-MAPS.md](../../docs/SOURCE-MAPS.md).
20
+ release + path). Server bundles and source contents need the current
21
+ ingester. Full guide: [docs/SOURCE-MAPS.md](../../docs/SOURCE-MAPS.md).
17
22
 
18
23
  ## Quick start (people and coding agents)
19
24
 
@@ -66,8 +71,10 @@ and, when `AUTTER_RUNTIME_KEY` is set, every bundle's map is on the ingester
66
71
  local checks still run and the upload is reported as unverified. Files copied
67
72
  unchanged from `public/`, Next.js build manifests and polyfills, and service workers are
68
73
  skipped; `--ignore <glob>` skips more. `--allow-maps` reports leftover maps
69
- without failing. `--json` for machines; exit `1` on any failure, with the
70
- reason per file.
74
+ without failing. `--server` checks server output (a tsup or esbuild `dist/`,
75
+ `.next/server`), where leftover maps are a note, not a failure, and Next.js
76
+ server manifests are skipped. `--json` for machines; exit `1` on any failure,
77
+ with the reason per file.
71
78
 
72
79
  ## Keys
73
80
 
@@ -114,10 +121,20 @@ All three turn on hidden source maps when you haven't configured maps, inject
114
121
  debug ids after the build, upload when a key is present, and then delete the
115
122
  maps they created whether or not the upload succeeded (a rejected upload, an
116
123
  unreachable ingester, a dry run), printing one line that says why; without a
117
- key they turn none on. Maps you configured yourself are left alone. Options (all optional): `release`, `urlPrefix`,
118
- `endpoint`, `key`, `upload: false`, `deleteAfterUpload`, `dryRun`,
119
- `concurrency`, `failOnError` (default `false`: a failed upload is logged and
120
- never fails the build), `silent`.
124
+ key they turn none on. Maps you configured yourself are left alone.
125
+
126
+ Server bundles get the same: Vite SSR builds and Vite 6 server environments,
127
+ webpack builds for a Node target, and Next.js `.next/server` (Node and edge,
128
+ webpack and Turbopack). Their maps are stored under the output directory's
129
+ path in the project (`/dist/server/`, `/.next/server/`), which the ingester
130
+ matches against the path the server runs them from, never under the public
131
+ URL prefix.
132
+
133
+ Options (all optional): `release`, `urlPrefix`, `endpoint`, `key`,
134
+ `upload: false`, `deleteAfterUpload`, `dryRun`, `concurrency`, `failOnError`
135
+ (default `false`: a failed upload is logged and never fails the build),
136
+ `silent`, `sourcesContent: false` (upload maps without your files' source),
137
+ `serverBundles: false` (leave server output exactly as the bundler wrote it).
121
138
 
122
139
  ## CLI
123
140
 
@@ -125,28 +142,40 @@ never fails the build), `silent`.
125
142
  npx autter-runtime sourcemaps inject dist
126
143
  npx autter-runtime sourcemaps upload dist --url-prefix / --release "$GIT_SHA"
127
144
  npx autter-runtime sourcemaps upload dist --dry-run
145
+
146
+ # A bundled Node server (tsup, esbuild): the same commands
147
+ npx autter-runtime sourcemaps inject dist
148
+ npx autter-runtime sourcemaps upload dist --url-prefix /dist/
149
+ npx autter-runtime sourcemaps verify dist --server
128
150
  ```
129
151
 
130
152
  ```
131
153
  autter-runtime sourcemaps setup [dir] [--dry-run] [--install] [--json]
132
154
  autter-runtime sourcemaps verify <dir> [--endpoint <url>] [--url-prefix <prefix>]
133
- [--ignore <glob>]... [--allow-maps] [--json]
155
+ [--ignore <glob>]... [--allow-maps] [--server] [--json]
134
156
  autter-runtime sourcemaps inject <dir> [--dry-run] [--json]
135
157
  autter-runtime sourcemaps upload <dir> [--release <r>] [--url-prefix <prefix>]
136
158
  [--endpoint <url>] [--concurrency <n>] [--delete-after-upload]
137
- [--best-effort] [--dry-run] [--json]
159
+ [--no-sources-content] [--best-effort] [--dry-run] [--json]
138
160
  ```
139
161
 
140
162
  - **inject** writes `//# debugId=<id>`, a one-line snippet that registers
141
163
  `globalThis._autterDebugIds[new Error().stack] = "<id>"`, and `"debugId"` in
142
164
  the `.map`, for each `.js`/`.mjs`/`.cjs` with a map. The id is derived from
143
- sha256 of the bundle bytes; running it again changes nothing.
144
- - **upload** posts each map (without `sourcesContent`, gzipped, up to 32 MB)
145
- to `<endpoint>/v1/sourcemaps`, in parallel, skipping maps the ingester
146
- already has. Each map carries the bundle's function scopes (`x_autter_scopes`),
165
+ sha256 of the bundle bytes; running it again changes nothing. The browser
166
+ SDK and the Node and edge SDKs read the snippet's registry and send the ids
167
+ of the bundles in each exception's stack.
168
+ - **upload** posts each map (gzipped, up to 32 MB) to
169
+ `<endpoint>/v1/sourcemaps`, in parallel, skipping maps the ingester already
170
+ has. Each map carries the bundle's function scopes (`x_autter_scopes`),
147
171
  parsed from the file as served, so frames are named after the function they
148
172
  are in rather than their call site; a bundle that does not parse is uploaded
149
- without them, with a warning. Release: `--release`, else `GITHUB_SHA`,
173
+ without them, with a warning. It also carries the original source of your own
174
+ files (`sourcesContent`; never your dependencies', whole files only, as many
175
+ as fit the 32 MB limit), so issues show the lines around each frame;
176
+ `--no-sources-content` leaves it out. For server output, `--url-prefix` is
177
+ any tail of the path the server runs the files from (`/dist/` for
178
+ `/app/dist/server.js`), or `/`. Release: `--release`, else `GITHUB_SHA`,
150
179
  `VERCEL_GIT_COMMIT_SHA`, `CF_PAGES_COMMIT_SHA`, `GIT_SHA`, then
151
180
  `git rev-parse HEAD`.
152
181
  - **upload --best-effort** is for build scripts that also run locally and in
@@ -69,8 +69,9 @@ declare function injectBundle(bundle: BundleWithMap, options?: InjectOptions): P
69
69
  declare function injectDir(dir: string, options?: InjectOptions): Promise<InjectResult[]>;
70
70
 
71
71
  declare const DEFAULT_ENDPOINT = "https://otlp.autter.dev";
72
- /** Largest map the ingester stores: characters of JSON without
73
- * `sourcesContent` (otlp-ingester's SOURCE_MAP_MAX_CHARS). */
72
+ /** Largest map the ingester stores (otlp-ingester's SOURCE_MAP_MAX_CHARS):
73
+ * characters of JSON without `sourcesContent`; the in-app sources' content
74
+ * that is sent fits in what is left of it, in UTF-8 bytes (storedMapJson). */
74
75
  declare const MAX_MAP_CHARS: number;
75
76
  /** The ingester's `/v1/sourcemaps` body limit, counted after the gzip body
76
77
  * is inflated: the map plus room for the rest of the request. */
@@ -101,6 +102,11 @@ interface UploadOptions {
101
102
  /** Delete each `.map` (and its `sourceMappingURL` comment) once the
102
103
  * ingester has it, so maps are never deployed. */
103
104
  deleteAfterUpload?: boolean;
105
+ /** Send the original source of your own files (`sourcesContent`, when the
106
+ * bundler wrote it), so issues show the lines around each frame of your
107
+ * code. Dependencies' sources are never sent. Default true; false sends
108
+ * maps without it (`--no-sources-content`). */
109
+ sourcesContent?: boolean;
104
110
  log?: (line: string) => void;
105
111
  env?: NodeJS.ProcessEnv;
106
112
  cwd?: string;
@@ -138,8 +144,23 @@ interface UploadSummary {
138
144
  declare function resolveEndpoint(flag?: string, env?: NodeJS.ProcessEnv): string;
139
145
  /** prefix + path, with exactly one "/" between. "~/" (Sentry's spelling) means "/". */
140
146
  declare function servedFilename(urlPrefix: string | undefined, path: string): string;
141
- /** The map as the ingester stores it: `sourcesContent` removed, compact JSON. */
142
- declare function storedMapJson(map: Record<string, unknown>): string;
147
+ /** Whether a map's `sources` entry is the app's own code (the ingester's in-app path rule). */
148
+ declare function isInAppSource(path: string): boolean;
149
+ /**
150
+ * The `sourcesContent` the ingester keeps, or null to leave it out: in-app
151
+ * sources only (not ignore-listed, not under node_modules/ etc.), whole
152
+ * files, smallest first, while the JSON fits `limit` UTF-8 bytes given the
153
+ * `baseBytes` of the map without it.
154
+ */
155
+ declare function storedSourcesContent(fields: Record<string, unknown>, baseBytes: number, limit?: number): Array<string | null> | null;
156
+ /**
157
+ * The map as the ingester stores it, compact JSON: every field as given,
158
+ * then `sourcesContent` bounded by storedSourcesContent (left out when
159
+ * nothing is kept, or with `sourcesContent: false`).
160
+ */
161
+ declare function storedMapJson(map: Record<string, unknown>, options?: {
162
+ sourcesContent?: boolean;
163
+ }): string;
143
164
  declare class FatalUploadError extends Error {
144
165
  }
145
166
  /** Upload every source map under `dir` to the ingester's `/v1/sourcemaps`. */
@@ -177,7 +198,26 @@ interface AutterSourceMapsOptions {
177
198
  failOnError?: boolean;
178
199
  /** No log output. */
179
200
  silent?: boolean;
201
+ /** Send your own files' original source with each map (`sourcesContent`,
202
+ * when the bundler wrote it), so issues show the lines around each frame
203
+ * of your code. Dependencies' sources are never sent. Default true. */
204
+ sourcesContent?: boolean;
205
+ /** Server bundles too (default true): Vite SSR builds and server
206
+ * environments, webpack builds for a Node target, and Next.js server
207
+ * output get debug ids and their maps uploaded, so errors in your backend
208
+ * resolve to its original files and lines. False leaves server output
209
+ * exactly as the bundler wrote it. */
210
+ serverBundles?: boolean;
180
211
  }
212
+ /**
213
+ * Where a server bundle directory's maps are stored: its path relative to
214
+ * the project (`/dist/server/`, `/.next/server/`), never the public URL
215
+ * prefix. The ingester matches a stored path as a suffix of the path the
216
+ * process runs the bundle from (`/app/dist/server/entry.js`,
217
+ * `/var/task/.next/server/app/page.js`), and the directory keeps server and
218
+ * browser maps of one release apart. "/" for a directory outside the project.
219
+ */
220
+ declare function serverUrlPrefix(projectRoot: string, dir: string): string;
181
221
  interface BuildOutputResult {
182
222
  injected: InjectResult[];
183
223
  upload: UploadSummary | null;
@@ -208,6 +248,9 @@ declare function processBuildOutput(input: {
208
248
  /** Map files the build emitted (absolute), when the bundler lists them;
209
249
  * the maps of `files` (or of every bundle under `dir`) are added. */
210
250
  maps?: string[];
251
+ /** Server output (RF-05): `urlPrefix` is serverUrlPrefix(), which the
252
+ * public `options.urlPrefix` never overrides. */
253
+ server?: boolean;
211
254
  }): Promise<BuildOutputResult>;
212
255
 
213
- export { type AutterSourceMapsOptions as A, BUNDLE_RE as B, CLIENT_KEY_PREFIX as C, DEFAULT_ENDPOINT as D, FatalUploadError as F, type InjectOptions as I, KEY_ADVICE as K, MAX_MAP_CHARS as M, SERVER_KEY_PREFIX as S, UPLOAD_KEY_PREFIX as U, type BuildOutputResult as a, type BundleWithMap as b, type InjectResult as c, MAX_UPLOAD_BYTES as d, SNIPPET_LINE_KEY as e, type UploadItem as f, type UploadOptions as g, type UploadSummary as h, findBundles as i, formatUploadSummary as j, injectBundle as k, injectDir as l, mapPathFor as m, resolveEndpoint as n, shiftMappings as o, processBuildOutput as p, snippetLine as q, readSourceMappingUrl as r, servedFilename as s, storedMapJson as t, stripSourceMappingUrl as u, uploadDir as v };
256
+ export { type AutterSourceMapsOptions as A, BUNDLE_RE as B, CLIENT_KEY_PREFIX as C, DEFAULT_ENDPOINT as D, FatalUploadError as F, type InjectOptions as I, KEY_ADVICE as K, MAX_MAP_CHARS as M, SERVER_KEY_PREFIX as S, UPLOAD_KEY_PREFIX as U, type BuildOutputResult as a, type BundleWithMap as b, type InjectResult as c, MAX_UPLOAD_BYTES as d, SNIPPET_LINE_KEY as e, type UploadItem as f, type UploadOptions as g, type UploadSummary as h, findBundles as i, formatUploadSummary as j, injectBundle as k, injectDir as l, isInAppSource as m, mapPathFor as n, resolveEndpoint as o, processBuildOutput as p, serverUrlPrefix as q, readSourceMappingUrl as r, servedFilename as s, shiftMappings as t, snippetLine as u, storedMapJson as v, storedSourcesContent as w, stripSourceMappingUrl as x, uploadDir as y };
@@ -69,8 +69,9 @@ declare function injectBundle(bundle: BundleWithMap, options?: InjectOptions): P
69
69
  declare function injectDir(dir: string, options?: InjectOptions): Promise<InjectResult[]>;
70
70
 
71
71
  declare const DEFAULT_ENDPOINT = "https://otlp.autter.dev";
72
- /** Largest map the ingester stores: characters of JSON without
73
- * `sourcesContent` (otlp-ingester's SOURCE_MAP_MAX_CHARS). */
72
+ /** Largest map the ingester stores (otlp-ingester's SOURCE_MAP_MAX_CHARS):
73
+ * characters of JSON without `sourcesContent`; the in-app sources' content
74
+ * that is sent fits in what is left of it, in UTF-8 bytes (storedMapJson). */
74
75
  declare const MAX_MAP_CHARS: number;
75
76
  /** The ingester's `/v1/sourcemaps` body limit, counted after the gzip body
76
77
  * is inflated: the map plus room for the rest of the request. */
@@ -101,6 +102,11 @@ interface UploadOptions {
101
102
  /** Delete each `.map` (and its `sourceMappingURL` comment) once the
102
103
  * ingester has it, so maps are never deployed. */
103
104
  deleteAfterUpload?: boolean;
105
+ /** Send the original source of your own files (`sourcesContent`, when the
106
+ * bundler wrote it), so issues show the lines around each frame of your
107
+ * code. Dependencies' sources are never sent. Default true; false sends
108
+ * maps without it (`--no-sources-content`). */
109
+ sourcesContent?: boolean;
104
110
  log?: (line: string) => void;
105
111
  env?: NodeJS.ProcessEnv;
106
112
  cwd?: string;
@@ -138,8 +144,23 @@ interface UploadSummary {
138
144
  declare function resolveEndpoint(flag?: string, env?: NodeJS.ProcessEnv): string;
139
145
  /** prefix + path, with exactly one "/" between. "~/" (Sentry's spelling) means "/". */
140
146
  declare function servedFilename(urlPrefix: string | undefined, path: string): string;
141
- /** The map as the ingester stores it: `sourcesContent` removed, compact JSON. */
142
- declare function storedMapJson(map: Record<string, unknown>): string;
147
+ /** Whether a map's `sources` entry is the app's own code (the ingester's in-app path rule). */
148
+ declare function isInAppSource(path: string): boolean;
149
+ /**
150
+ * The `sourcesContent` the ingester keeps, or null to leave it out: in-app
151
+ * sources only (not ignore-listed, not under node_modules/ etc.), whole
152
+ * files, smallest first, while the JSON fits `limit` UTF-8 bytes given the
153
+ * `baseBytes` of the map without it.
154
+ */
155
+ declare function storedSourcesContent(fields: Record<string, unknown>, baseBytes: number, limit?: number): Array<string | null> | null;
156
+ /**
157
+ * The map as the ingester stores it, compact JSON: every field as given,
158
+ * then `sourcesContent` bounded by storedSourcesContent (left out when
159
+ * nothing is kept, or with `sourcesContent: false`).
160
+ */
161
+ declare function storedMapJson(map: Record<string, unknown>, options?: {
162
+ sourcesContent?: boolean;
163
+ }): string;
143
164
  declare class FatalUploadError extends Error {
144
165
  }
145
166
  /** Upload every source map under `dir` to the ingester's `/v1/sourcemaps`. */
@@ -177,7 +198,26 @@ interface AutterSourceMapsOptions {
177
198
  failOnError?: boolean;
178
199
  /** No log output. */
179
200
  silent?: boolean;
201
+ /** Send your own files' original source with each map (`sourcesContent`,
202
+ * when the bundler wrote it), so issues show the lines around each frame
203
+ * of your code. Dependencies' sources are never sent. Default true. */
204
+ sourcesContent?: boolean;
205
+ /** Server bundles too (default true): Vite SSR builds and server
206
+ * environments, webpack builds for a Node target, and Next.js server
207
+ * output get debug ids and their maps uploaded, so errors in your backend
208
+ * resolve to its original files and lines. False leaves server output
209
+ * exactly as the bundler wrote it. */
210
+ serverBundles?: boolean;
180
211
  }
212
+ /**
213
+ * Where a server bundle directory's maps are stored: its path relative to
214
+ * the project (`/dist/server/`, `/.next/server/`), never the public URL
215
+ * prefix. The ingester matches a stored path as a suffix of the path the
216
+ * process runs the bundle from (`/app/dist/server/entry.js`,
217
+ * `/var/task/.next/server/app/page.js`), and the directory keeps server and
218
+ * browser maps of one release apart. "/" for a directory outside the project.
219
+ */
220
+ declare function serverUrlPrefix(projectRoot: string, dir: string): string;
181
221
  interface BuildOutputResult {
182
222
  injected: InjectResult[];
183
223
  upload: UploadSummary | null;
@@ -208,6 +248,9 @@ declare function processBuildOutput(input: {
208
248
  /** Map files the build emitted (absolute), when the bundler lists them;
209
249
  * the maps of `files` (or of every bundle under `dir`) are added. */
210
250
  maps?: string[];
251
+ /** Server output (RF-05): `urlPrefix` is serverUrlPrefix(), which the
252
+ * public `options.urlPrefix` never overrides. */
253
+ server?: boolean;
211
254
  }): Promise<BuildOutputResult>;
212
255
 
213
- export { type AutterSourceMapsOptions as A, BUNDLE_RE as B, CLIENT_KEY_PREFIX as C, DEFAULT_ENDPOINT as D, FatalUploadError as F, type InjectOptions as I, KEY_ADVICE as K, MAX_MAP_CHARS as M, SERVER_KEY_PREFIX as S, UPLOAD_KEY_PREFIX as U, type BuildOutputResult as a, type BundleWithMap as b, type InjectResult as c, MAX_UPLOAD_BYTES as d, SNIPPET_LINE_KEY as e, type UploadItem as f, type UploadOptions as g, type UploadSummary as h, findBundles as i, formatUploadSummary as j, injectBundle as k, injectDir as l, mapPathFor as m, resolveEndpoint as n, shiftMappings as o, processBuildOutput as p, snippetLine as q, readSourceMappingUrl as r, servedFilename as s, storedMapJson as t, stripSourceMappingUrl as u, uploadDir as v };
256
+ export { type AutterSourceMapsOptions as A, BUNDLE_RE as B, CLIENT_KEY_PREFIX as C, DEFAULT_ENDPOINT as D, FatalUploadError as F, type InjectOptions as I, KEY_ADVICE as K, MAX_MAP_CHARS as M, SERVER_KEY_PREFIX as S, UPLOAD_KEY_PREFIX as U, type BuildOutputResult as a, type BundleWithMap as b, type InjectResult as c, MAX_UPLOAD_BYTES as d, SNIPPET_LINE_KEY as e, type UploadItem as f, type UploadOptions as g, type UploadSummary as h, findBundles as i, formatUploadSummary as j, injectBundle as k, injectDir as l, isInAppSource as m, mapPathFor as n, resolveEndpoint as o, processBuildOutput as p, serverUrlPrefix as q, readSourceMappingUrl as r, servedFilename as s, shiftMappings as t, snippetLine as u, storedMapJson as v, storedSourcesContent as w, stripSourceMappingUrl as x, uploadDir as y };
@@ -684,9 +684,56 @@ function servedFilename(urlPrefix, path) {
684
684
  if (!/^[a-z][\w+.-]*:\/\//i.test(prefix) && !prefix.startsWith("/")) prefix = `/${prefix}`;
685
685
  return `${prefix.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
686
686
  }
687
- function storedMapJson(map) {
688
- const { sourcesContent: _omitted, ...safe } = map;
689
- return JSON.stringify(safe);
687
+ var NOT_IN_APP_PATH_PARTS = ["node_modules/", "/site-packages/", "/dist-packages/", "/vendor/", "/.pnpm/"];
688
+ var NOT_IN_APP_PATH_PREFIXES = ["node:", "internal/"];
689
+ var EXTENSION_SCHEMES = ["chrome-extension:", "moz-extension:", "safari-extension:", "safari-web-extension:", "webkit-masked-url:"];
690
+ function isInAppSource(path) {
691
+ if (!path) return true;
692
+ const normalized = path.replace(/\\/g, "/");
693
+ if (NOT_IN_APP_PATH_PARTS.some((part) => normalized.includes(part))) return false;
694
+ if (NOT_IN_APP_PATH_PREFIXES.some((prefix) => normalized.startsWith(prefix))) return false;
695
+ const lower = normalized.toLowerCase();
696
+ return !EXTENSION_SCHEMES.some((scheme) => lower.startsWith(scheme));
697
+ }
698
+ var SOURCES_CONTENT_WRAPPER_BYTES = Buffer.byteLength(',"sourcesContent":[]');
699
+ var NULL_ENTRY_BYTES = 4;
700
+ function storedSourcesContent(fields, baseBytes, limit = MAX_MAP_CHARS) {
701
+ const { sources, sourcesContent } = fields;
702
+ if (!Array.isArray(sources) || !Array.isArray(sourcesContent) || sources.length === 0) return null;
703
+ const ignored = /* @__PURE__ */ new Set();
704
+ for (const list of [fields.ignoreList, fields.x_google_ignoreList]) {
705
+ if (!Array.isArray(list)) continue;
706
+ for (const index of list) if (Number.isInteger(index)) ignored.add(index);
707
+ }
708
+ const count = sources.length;
709
+ const candidates = [];
710
+ for (let index = 0; index < count; index++) {
711
+ const source = sources[index];
712
+ const content = sourcesContent[index];
713
+ if (typeof source !== "string" || typeof content !== "string") continue;
714
+ if (ignored.has(index) || !isInAppSource(source)) continue;
715
+ candidates.push({ index, content, bytes: Buffer.byteLength(JSON.stringify(content)) });
716
+ }
717
+ if (candidates.length === 0) return null;
718
+ candidates.sort((a, b) => a.bytes - b.bytes || a.index - b.index);
719
+ let remaining = limit - baseBytes - SOURCES_CONTENT_WRAPPER_BYTES - NULL_ENTRY_BYTES * count - (count - 1);
720
+ const kept = new Array(count).fill(null);
721
+ let any = false;
722
+ for (const candidate of candidates) {
723
+ const extra = candidate.bytes - NULL_ENTRY_BYTES;
724
+ if (extra > remaining) break;
725
+ remaining -= extra;
726
+ kept[candidate.index] = candidate.content;
727
+ any = true;
728
+ }
729
+ return any ? kept : null;
730
+ }
731
+ function storedMapJson(map, options = {}) {
732
+ const { sourcesContent: _content, ...rest } = map;
733
+ const base = JSON.stringify(rest);
734
+ if (options.sourcesContent === false) return base;
735
+ const content = storedSourcesContent(map, Buffer.byteLength(base));
736
+ return content ? JSON.stringify({ ...rest, sourcesContent: content }) : base;
690
737
  }
691
738
  function bytesOf(buffer) {
692
739
  return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
@@ -697,7 +744,7 @@ function fits(json, body) {
697
744
  function uploadBody(release, filename, debugId, json) {
698
745
  return `{"release":${JSON.stringify(release)},"filename":${JSON.stringify(filename)},${debugId ? `"debugId":${JSON.stringify(debugId)},` : ""}"map":${json}}`;
699
746
  }
700
- async function prepare(bundle, urlPrefix, release, log, compress) {
747
+ async function prepare(bundle, urlPrefix, release, log, compress, sourcesContent) {
701
748
  let map;
702
749
  try {
703
750
  map = JSON.parse(await readFile3(bundle.map, "utf8"));
@@ -712,9 +759,10 @@ async function prepare(bundle, urlPrefix, release, log, compress) {
712
759
  const filename = servedFilename(urlPrefix, bundle.path);
713
760
  const { [SCOPES_KEY]: _stale, ...plain } = map;
714
761
  const scopes = computeScopes(code, map);
715
- let json = storedMapJson(plain);
762
+ const stored = { sourcesContent };
763
+ let json = storedMapJson(plain, stored);
716
764
  if ("scopes" in scopes) {
717
- const scoped = storedMapJson({ ...plain, [SCOPES_KEY]: scopes.scopes });
765
+ const scoped = storedMapJson({ ...plain, [SCOPES_KEY]: scopes.scopes }, stored);
718
766
  if (fits(scoped, uploadBody(release, filename, debugId, scoped))) json = scoped;
719
767
  else if (fits(json, uploadBody(release, filename, debugId, json))) {
720
768
  log(`warning: ${bundle.path}: its map is over 32 MB with function scopes, so it is uploaded without them (frames keep call-site names)`);
@@ -761,7 +809,7 @@ async function uploadDir(dir, options = {}) {
761
809
  };
762
810
  const items = [];
763
811
  for (const bundle of await findBundles(dir, options.files)) {
764
- const prepared = await prepare(bundle, options.urlPrefix, release, log, !options.dryRun);
812
+ const prepared = await prepare(bundle, options.urlPrefix, release, log, !options.dryRun, options.sourcesContent !== false);
765
813
  if ("error" in prepared) {
766
814
  summary.failed.push({ path: bundle.path, filename: servedFilename(options.urlPrefix, bundle.path), error: prepared.error });
767
815
  } else if (prepared.mapChars > MAX_MAP_CHARS || prepared.bytes > MAX_UPLOAD_BYTES) {
@@ -881,7 +929,11 @@ function strip(item) {
881
929
 
882
930
  // src/build.ts
883
931
  import { existsSync as existsSync2 } from "fs";
884
- import { relative as relative2, sep as sep2 } from "path";
932
+ import { isAbsolute, relative as relative2, sep as sep2 } from "path";
933
+ function serverUrlPrefix(projectRoot, dir) {
934
+ const rel = relative2(projectRoot, dir).split(sep2).join("/");
935
+ return !rel || rel.startsWith("..") || isAbsolute(rel) ? "/" : `/${rel.replace(/\/+$/, "")}/`;
936
+ }
885
937
  function buildKey(options) {
886
938
  const key = (options.key ?? process.env.AUTTER_RUNTIME_KEY ?? "").trim();
887
939
  return key || null;
@@ -944,12 +996,13 @@ async function processBuildOutput(input) {
944
996
  result.upload = await uploadDir(input.dir, {
945
997
  files: input.files,
946
998
  release: options.release,
947
- urlPrefix: options.urlPrefix ?? input.urlPrefix,
999
+ urlPrefix: input.server ? input.urlPrefix : options.urlPrefix ?? input.urlPrefix,
948
1000
  endpoint: options.endpoint,
949
1001
  key: key ?? void 0,
950
1002
  dryRun: options.dryRun,
951
1003
  concurrency: options.concurrency,
952
1004
  deleteAfterUpload: options.deleteAfterUpload ?? input.deleteAfterUpload,
1005
+ ...options.sourcesContent === false ? { sourcesContent: false } : {},
953
1006
  log
954
1007
  });
955
1008
  for (const line of formatUploadSummary(result.upload)) log(line);
@@ -1034,9 +1087,12 @@ export {
1034
1087
  KEY_ADVICE,
1035
1088
  resolveEndpoint,
1036
1089
  servedFilename,
1090
+ isInAppSource,
1091
+ storedSourcesContent,
1037
1092
  storedMapJson,
1038
1093
  FatalUploadError,
1039
1094
  uploadDir,
1095
+ serverUrlPrefix,
1040
1096
  shouldGenerateMaps,
1041
1097
  logMapsNotGenerated,
1042
1098
  formatUploadSummary,
@@ -2,12 +2,18 @@ import {
2
2
  BUNDLE_RE,
3
3
  logMapsNotGenerated,
4
4
  processBuildOutput,
5
+ serverUrlPrefix,
5
6
  shouldGenerateMaps
6
- } from "./chunk-KIQRQY3X.js";
7
+ } from "./chunk-Q23FB5X3.js";
7
8
 
8
9
  // src/webpack.ts
9
10
  import { resolve } from "path";
10
11
  var PLUGIN_NAME = "AutterSourceMapsWebpackPlugin";
12
+ var SERVER_TARGET_RE = /^(?:async-)?node|^electron-(?:main|preload)/;
13
+ function isServerTarget(target) {
14
+ const targets = Array.isArray(target) ? target : [target];
15
+ return targets.some((value) => typeof value === "string" && SERVER_TARGET_RE.test(value));
16
+ }
11
17
  var AutterSourceMapsWebpackPlugin = class {
12
18
  constructor(options = {}, internal = {}) {
13
19
  this.options = options;
@@ -18,6 +24,8 @@ var AutterSourceMapsWebpackPlugin = class {
18
24
  apply(compiler) {
19
25
  const mode = compiler.options.mode ?? (process.env.NODE_ENV === "development" ? "development" : void 0);
20
26
  if (mode === "development") return;
27
+ const server = this.internal.server ?? isServerTarget(compiler.options.target);
28
+ if (server && this.options.serverBundles === false) return;
21
29
  let enabledMaps = this.internal.ownMaps ?? false;
22
30
  if (!compiler.options.devtool) {
23
31
  if (shouldGenerateMaps(this.options)) {
@@ -31,7 +39,7 @@ var AutterSourceMapsWebpackPlugin = class {
31
39
  const dir = compilation.outputOptions.path ?? compiler.outputPath;
32
40
  if (!dir) return;
33
41
  const publicPath = compilation.outputOptions.publicPath;
34
- const urlPrefix = typeof publicPath === "string" && publicPath !== "auto" && publicPath !== "" ? publicPath : "/";
42
+ const urlPrefix = server ? serverUrlPrefix(compiler.context ?? process.cwd(), dir) : typeof publicPath === "string" && publicPath !== "auto" && publicPath !== "" ? publicPath : "/";
35
43
  const assets = compilation.getAssets().map((asset) => asset.name);
36
44
  const files = assets.filter((name) => BUNDLE_RE.test(name)).map((name) => resolve(dir, name));
37
45
  const maps = assets.filter((name) => name.endsWith(".map")).map((name) => resolve(dir, name));
@@ -42,7 +50,8 @@ var AutterSourceMapsWebpackPlugin = class {
42
50
  urlPrefix,
43
51
  options: this.options,
44
52
  deleteAfterUpload: enabledMaps,
45
- ownMaps: enabledMaps
53
+ ownMaps: enabledMaps,
54
+ ...server ? { server: true } : {}
46
55
  });
47
56
  });
48
57
  }
@@ -53,6 +62,7 @@ function autterSourceMapsWebpack(options = {}) {
53
62
  var webpack_default = AutterSourceMapsWebpackPlugin;
54
63
 
55
64
  export {
65
+ isServerTarget,
56
66
  AutterSourceMapsWebpackPlugin,
57
67
  autterSourceMapsWebpack,
58
68
  webpack_default