@autter/runtime-cli 1.1.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 +127 -26
- package/dist/{build-CPXR9Xd3.d.cts → build-DGeoDkTh.d.cts} +57 -7
- package/dist/{build-CPXR9Xd3.d.ts → build-DGeoDkTh.d.ts} +57 -7
- package/dist/{chunk-DQLLM2NT.js → chunk-Q23FB5X3.js} +81 -16
- package/dist/{chunk-I2743JCN.js → chunk-V66S2YHN.js} +13 -3
- package/dist/cli.js +1949 -48
- package/dist/index.cjs +348 -22
- package/dist/index.d.cts +68 -5
- package/dist/index.d.ts +68 -5
- package/dist/index.js +267 -2
- package/dist/next.cjs +120 -19
- package/dist/next.d.cts +6 -3
- package/dist/next.d.ts +6 -3
- package/dist/next.js +41 -4
- package/dist/vite.cjs +92 -18
- package/dist/vite.d.cts +5 -1
- package/dist/vite.d.ts +5 -1
- package/dist/vite.js +22 -4
- package/dist/webpack.cjs +86 -19
- package/dist/webpack.d.cts +18 -4
- package/dist/webpack.d.ts +18 -4
- package/dist/webpack.js +5 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,27 +2,95 @@
|
|
|
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
|
|
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
|
-
- `autter-runtime sourcemaps
|
|
9
|
-
-
|
|
10
|
-
|
|
9
|
+
- `autter-runtime sourcemaps setup` wires it into a project; `verify` checks a build
|
|
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)
|
|
11
15
|
- Next.js wrapper: `withAutterSourceMaps` from `@autter/runtime-cli/next`
|
|
16
|
+
(client and `.next/server` output)
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
[docs/SOURCE-MAPS.md](../../docs/SOURCE-MAPS.md).
|
|
18
|
+
Bundlers are optional peer dependencies. Debug ids need ingester 1.6.0+ and
|
|
19
|
+
`@autter/runtime-browser` 1.6.0+ (older ingesters still match `.js` maps by
|
|
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
|
|
|
20
25
|
```bash
|
|
21
|
-
|
|
26
|
+
npx @autter/runtime-cli@latest sourcemaps setup # in the app's directory
|
|
27
|
+
npm run build # with AUTTER_RUNTIME_KEY set
|
|
28
|
+
npx autter-runtime sourcemaps verify dist # before deploying
|
|
22
29
|
```
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
**`setup [dir]`** is non-interactive and safe to re-run. It detects the build
|
|
32
|
+
tool (Vite and the frameworks built on it: SvelteKit, Nuxt, Astro, Remix,
|
|
33
|
+
React Router, Solid, Qwik; Next.js; webpack and Vue CLI; Rollup; Create React
|
|
34
|
+
App; Angular CLI; Parcel; esbuild), the package manager, the CI (GitHub
|
|
35
|
+
Actions, Vercel, Netlify, Cloudflare Pages, GitLab CI), the output directory,
|
|
36
|
+
existing source map settings, the installed browser SDK, and whether
|
|
37
|
+
`initAutterBrowser` gets a `release`. Then it:
|
|
38
|
+
|
|
39
|
+
- adds `@autter/runtime-cli` to `devDependencies` and prints the install
|
|
40
|
+
command (`--install` runs it);
|
|
41
|
+
- adds the Vite/Rollup or webpack plugin, or wraps the Next.js config in
|
|
42
|
+
`withAutterSourceMaps` (outermost), when the config has a shape it can edit
|
|
43
|
+
safely: `export default defineConfig({...})`, `module.exports = {...}`, a
|
|
44
|
+
config variable, a function returning an object, `withX(nextConfig)`
|
|
45
|
+
wrappers. It parses the file (JavaScript or TypeScript) and changes only
|
|
46
|
+
what it adds; anything else becomes a `manual` action with the exact snippet;
|
|
47
|
+
- for builds without a plugin (CRA, Angular, Parcel, esbuild), adds a
|
|
48
|
+
`postbuild` step (appended to `build` for pnpm, Yarn 2+ and Bun, which skip
|
|
49
|
+
`postbuild`) that runs `inject`, then `upload --delete-after-upload
|
|
50
|
+
--best-effort`, and turns on hidden maps where the tool can
|
|
51
|
+
(`GENERATE_SOURCEMAP=true`, Angular `"sourceMap": { "scripts": true, "hidden": true }`);
|
|
52
|
+
- reports, never edits, app code: a missing `release` gets a `manual` action
|
|
53
|
+
showing how to pass the commit SHA through the tool's public env or define
|
|
54
|
+
mechanism;
|
|
55
|
+
- never writes a key. It says where to store `AUTTER_RUNTIME_KEY` for each CI
|
|
56
|
+
it found, with commands that read the value from a prompt or stdin, never
|
|
57
|
+
from argv: `gh secret set AUTTER_RUNTIME_KEY --repo <owner/repo>`,
|
|
58
|
+
`vercel env add AUTTER_RUNTIME_KEY production`, `netlify env:set …
|
|
59
|
+
"$(cat)"`, and dashboard notes for Cloudflare Pages and GitLab.
|
|
60
|
+
|
|
61
|
+
`--dry-run` writes nothing; `--json` prints `{ detected, actions: [{ type:
|
|
62
|
+
"edit"|"add-dependency"|"add-script"|"manual"|"set-secret", file?,
|
|
63
|
+
description, snippet?, command? }], secret: { name, key, where }, next }`.
|
|
64
|
+
Exit codes: `0` everything applied, `2` manual actions remain, `1` error.
|
|
65
|
+
|
|
66
|
+
**`verify <dir>`** checks a production build before deploy: every JS bundle
|
|
67
|
+
has a debug id (the comment and the registration snippet), no `.map` file is
|
|
68
|
+
left and no `sourceMappingURL` points at a missing map (both deploy risks),
|
|
69
|
+
and, when `AUTTER_RUNTIME_KEY` is set, every bundle's map is on the ingester
|
|
70
|
+
(`/v1/sourcemaps/check` by debug id, ingester 1.7.0+). Without a key the
|
|
71
|
+
local checks still run and the upload is reported as unverified. Files copied
|
|
72
|
+
unchanged from `public/`, Next.js build manifests and polyfills, and service workers are
|
|
73
|
+
skipped; `--ignore <glob>` skips more. `--allow-maps` reports leftover maps
|
|
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.
|
|
78
|
+
|
|
79
|
+
## Keys
|
|
80
|
+
|
|
81
|
+
Set `AUTTER_RUNTIME_KEY` where your build runs. The recommended key for CI is
|
|
82
|
+
an **upload-only key** (`autter_rtu_…`): it can upload and check source maps
|
|
83
|
+
and nothing else, so a leaked CI secret can't send telemetry. The full server
|
|
84
|
+
key (`autter_rt_…`) also works. A publishable client key (`autter_rtc_…`) is
|
|
85
|
+
refused. Upload keys need ingester 1.7.0+. Without a key, the plugins turn no
|
|
86
|
+
source maps on and change nothing, and `upload --best-effort` uploads nothing
|
|
87
|
+
and exits 0.
|
|
88
|
+
|
|
89
|
+
## By hand
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm install -D @autter/runtime-cli
|
|
93
|
+
```
|
|
26
94
|
|
|
27
95
|
## Vite / Rollup
|
|
28
96
|
|
|
@@ -53,10 +121,20 @@ All three turn on hidden source maps when you haven't configured maps, inject
|
|
|
53
121
|
debug ids after the build, upload when a key is present, and then delete the
|
|
54
122
|
maps they created whether or not the upload succeeded (a rejected upload, an
|
|
55
123
|
unreachable ingester, a dry run), printing one line that says why; without a
|
|
56
|
-
key they turn none on. Maps you configured yourself are left alone.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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).
|
|
60
138
|
|
|
61
139
|
## CLI
|
|
62
140
|
|
|
@@ -64,35 +142,58 @@ never fails the build), `silent`.
|
|
|
64
142
|
npx autter-runtime sourcemaps inject dist
|
|
65
143
|
npx autter-runtime sourcemaps upload dist --url-prefix / --release "$GIT_SHA"
|
|
66
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
|
|
67
150
|
```
|
|
68
151
|
|
|
69
152
|
```
|
|
70
|
-
autter-runtime sourcemaps
|
|
153
|
+
autter-runtime sourcemaps setup [dir] [--dry-run] [--install] [--json]
|
|
154
|
+
autter-runtime sourcemaps verify <dir> [--endpoint <url>] [--url-prefix <prefix>]
|
|
155
|
+
[--ignore <glob>]... [--allow-maps] [--server] [--json]
|
|
156
|
+
autter-runtime sourcemaps inject <dir> [--dry-run] [--json]
|
|
71
157
|
autter-runtime sourcemaps upload <dir> [--release <r>] [--url-prefix <prefix>]
|
|
72
|
-
[--endpoint <url>] [--concurrency <n>] [--delete-after-upload]
|
|
158
|
+
[--endpoint <url>] [--concurrency <n>] [--delete-after-upload]
|
|
159
|
+
[--no-sources-content] [--best-effort] [--dry-run] [--json]
|
|
73
160
|
```
|
|
74
161
|
|
|
75
162
|
- **inject** writes `//# debugId=<id>`, a one-line snippet that registers
|
|
76
163
|
`globalThis._autterDebugIds[new Error().stack] = "<id>"`, and `"debugId"` in
|
|
77
164
|
the `.map`, for each `.js`/`.mjs`/`.cjs` with a map. The id is derived from
|
|
78
|
-
sha256 of the bundle bytes; running it again changes nothing.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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`),
|
|
82
171
|
parsed from the file as served, so frames are named after the function they
|
|
83
172
|
are in rather than their call site; a bundle that does not parse is uploaded
|
|
84
|
-
without them, with a warning.
|
|
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`,
|
|
85
179
|
`VERCEL_GIT_COMMIT_SHA`, `CF_PAGES_COMMIT_SHA`, `GIT_SHA`, then
|
|
86
180
|
`git rev-parse HEAD`.
|
|
181
|
+
- **upload --best-effort** is for build scripts that also run locally and in
|
|
182
|
+
fork PRs: without a key, or when the upload fails, it says so and exits 0;
|
|
183
|
+
with `--delete-after-upload`, every `.map` under `<dir>` is then deleted
|
|
184
|
+
anyway, so the output is the same with or without a key and never ships
|
|
185
|
+
maps.
|
|
87
186
|
|
|
88
187
|
Exit codes: `0` ok, `1` some maps failed, `2` no key / no release / key
|
|
89
|
-
rejected, `64` usage error.
|
|
188
|
+
rejected, `64` usage error (`--best-effort`: always `0`). `setup` and `verify`
|
|
189
|
+
have their own, above.
|
|
90
190
|
|
|
91
191
|
## Programmatic API
|
|
92
192
|
|
|
93
193
|
```ts
|
|
94
|
-
import { injectDir, uploadDir } from "@autter/runtime-cli";
|
|
194
|
+
import { injectDir, uploadDir, verifyDir } from "@autter/runtime-cli";
|
|
95
195
|
|
|
96
196
|
await injectDir("dist");
|
|
97
|
-
const summary = await uploadDir("dist", { urlPrefix: "/", release: process.env.GIT_SHA });
|
|
197
|
+
const summary = await uploadDir("dist", { urlPrefix: "/", release: process.env.GIT_SHA, deleteAfterUpload: true });
|
|
198
|
+
const check = await verifyDir("dist"); // check.ok, check.failures, check.upload.status
|
|
98
199
|
```
|
|
@@ -69,12 +69,18 @@ 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
|
|
73
|
-
* `sourcesContent
|
|
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. */
|
|
77
78
|
declare const MAX_UPLOAD_BYTES: number;
|
|
79
|
+
/** Keys `upload` accepts, and the one it refuses before sending anything. */
|
|
80
|
+
declare const UPLOAD_KEY_PREFIX = "autter_rtu_";
|
|
81
|
+
declare const SERVER_KEY_PREFIX = "autter_rt_";
|
|
82
|
+
declare const CLIENT_KEY_PREFIX = "autter_rtc_";
|
|
83
|
+
declare const KEY_ADVICE: string;
|
|
78
84
|
interface UploadOptions {
|
|
79
85
|
/** Only these bundles (absolute, or relative to the directory). */
|
|
80
86
|
files?: string[];
|
|
@@ -86,7 +92,8 @@ interface UploadOptions {
|
|
|
86
92
|
/** Ingester base URL. Default: $AUTTER_ENDPOINT, $AUTTER_RUNTIME_ENDPOINT,
|
|
87
93
|
* $OTEL_EXPORTER_OTLP_ENDPOINT, else https://otlp.autter.dev. */
|
|
88
94
|
endpoint?: string;
|
|
89
|
-
/**
|
|
95
|
+
/** Upload key (`autter_rtu_…`, recommended for CI) or server key
|
|
96
|
+
* (`autter_rt_…`). Default: $AUTTER_RUNTIME_KEY. */
|
|
90
97
|
key?: string;
|
|
91
98
|
/** List what would be uploaded; no network, no key needed. */
|
|
92
99
|
dryRun?: boolean;
|
|
@@ -95,6 +102,11 @@ interface UploadOptions {
|
|
|
95
102
|
/** Delete each `.map` (and its `sourceMappingURL` comment) once the
|
|
96
103
|
* ingester has it, so maps are never deployed. */
|
|
97
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;
|
|
98
110
|
log?: (line: string) => void;
|
|
99
111
|
env?: NodeJS.ProcessEnv;
|
|
100
112
|
cwd?: string;
|
|
@@ -132,8 +144,23 @@ interface UploadSummary {
|
|
|
132
144
|
declare function resolveEndpoint(flag?: string, env?: NodeJS.ProcessEnv): string;
|
|
133
145
|
/** prefix + path, with exactly one "/" between. "~/" (Sentry's spelling) means "/". */
|
|
134
146
|
declare function servedFilename(urlPrefix: string | undefined, path: string): string;
|
|
135
|
-
/**
|
|
136
|
-
declare function
|
|
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;
|
|
137
164
|
declare class FatalUploadError extends Error {
|
|
138
165
|
}
|
|
139
166
|
/** Upload every source map under `dir` to the ingester's `/v1/sourcemaps`. */
|
|
@@ -149,7 +176,8 @@ interface AutterSourceMapsOptions {
|
|
|
149
176
|
/** Ingester base URL. Default: $AUTTER_ENDPOINT, $AUTTER_RUNTIME_ENDPOINT,
|
|
150
177
|
* $OTEL_EXPORTER_OTLP_ENDPOINT, else https://otlp.autter.dev. */
|
|
151
178
|
endpoint?: string;
|
|
152
|
-
/**
|
|
179
|
+
/** Upload-only key (`autter_rtu_…`, recommended for CI) or server key
|
|
180
|
+
* (`autter_rt_…`). Default: $AUTTER_RUNTIME_KEY. Without one (and
|
|
153
181
|
* without `upload: false` or `dryRun`), the plugin generates no source
|
|
154
182
|
* maps it would have to delete again: the build is left as it was. */
|
|
155
183
|
key?: string;
|
|
@@ -170,7 +198,26 @@ interface AutterSourceMapsOptions {
|
|
|
170
198
|
failOnError?: boolean;
|
|
171
199
|
/** No log output. */
|
|
172
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;
|
|
173
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;
|
|
174
221
|
interface BuildOutputResult {
|
|
175
222
|
injected: InjectResult[];
|
|
176
223
|
upload: UploadSummary | null;
|
|
@@ -201,6 +248,9 @@ declare function processBuildOutput(input: {
|
|
|
201
248
|
/** Map files the build emitted (absolute), when the bundler lists them;
|
|
202
249
|
* the maps of `files` (or of every bundle under `dir`) are added. */
|
|
203
250
|
maps?: string[];
|
|
251
|
+
/** Server output (RF-05): `urlPrefix` is serverUrlPrefix(), which the
|
|
252
|
+
* public `options.urlPrefix` never overrides. */
|
|
253
|
+
server?: boolean;
|
|
204
254
|
}): Promise<BuildOutputResult>;
|
|
205
255
|
|
|
206
|
-
export { type AutterSourceMapsOptions as A, BUNDLE_RE as B, DEFAULT_ENDPOINT as D, FatalUploadError as F, type InjectOptions as I, MAX_MAP_CHARS as M,
|
|
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,12 +69,18 @@ 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
|
|
73
|
-
* `sourcesContent
|
|
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. */
|
|
77
78
|
declare const MAX_UPLOAD_BYTES: number;
|
|
79
|
+
/** Keys `upload` accepts, and the one it refuses before sending anything. */
|
|
80
|
+
declare const UPLOAD_KEY_PREFIX = "autter_rtu_";
|
|
81
|
+
declare const SERVER_KEY_PREFIX = "autter_rt_";
|
|
82
|
+
declare const CLIENT_KEY_PREFIX = "autter_rtc_";
|
|
83
|
+
declare const KEY_ADVICE: string;
|
|
78
84
|
interface UploadOptions {
|
|
79
85
|
/** Only these bundles (absolute, or relative to the directory). */
|
|
80
86
|
files?: string[];
|
|
@@ -86,7 +92,8 @@ interface UploadOptions {
|
|
|
86
92
|
/** Ingester base URL. Default: $AUTTER_ENDPOINT, $AUTTER_RUNTIME_ENDPOINT,
|
|
87
93
|
* $OTEL_EXPORTER_OTLP_ENDPOINT, else https://otlp.autter.dev. */
|
|
88
94
|
endpoint?: string;
|
|
89
|
-
/**
|
|
95
|
+
/** Upload key (`autter_rtu_…`, recommended for CI) or server key
|
|
96
|
+
* (`autter_rt_…`). Default: $AUTTER_RUNTIME_KEY. */
|
|
90
97
|
key?: string;
|
|
91
98
|
/** List what would be uploaded; no network, no key needed. */
|
|
92
99
|
dryRun?: boolean;
|
|
@@ -95,6 +102,11 @@ interface UploadOptions {
|
|
|
95
102
|
/** Delete each `.map` (and its `sourceMappingURL` comment) once the
|
|
96
103
|
* ingester has it, so maps are never deployed. */
|
|
97
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;
|
|
98
110
|
log?: (line: string) => void;
|
|
99
111
|
env?: NodeJS.ProcessEnv;
|
|
100
112
|
cwd?: string;
|
|
@@ -132,8 +144,23 @@ interface UploadSummary {
|
|
|
132
144
|
declare function resolveEndpoint(flag?: string, env?: NodeJS.ProcessEnv): string;
|
|
133
145
|
/** prefix + path, with exactly one "/" between. "~/" (Sentry's spelling) means "/". */
|
|
134
146
|
declare function servedFilename(urlPrefix: string | undefined, path: string): string;
|
|
135
|
-
/**
|
|
136
|
-
declare function
|
|
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;
|
|
137
164
|
declare class FatalUploadError extends Error {
|
|
138
165
|
}
|
|
139
166
|
/** Upload every source map under `dir` to the ingester's `/v1/sourcemaps`. */
|
|
@@ -149,7 +176,8 @@ interface AutterSourceMapsOptions {
|
|
|
149
176
|
/** Ingester base URL. Default: $AUTTER_ENDPOINT, $AUTTER_RUNTIME_ENDPOINT,
|
|
150
177
|
* $OTEL_EXPORTER_OTLP_ENDPOINT, else https://otlp.autter.dev. */
|
|
151
178
|
endpoint?: string;
|
|
152
|
-
/**
|
|
179
|
+
/** Upload-only key (`autter_rtu_…`, recommended for CI) or server key
|
|
180
|
+
* (`autter_rt_…`). Default: $AUTTER_RUNTIME_KEY. Without one (and
|
|
153
181
|
* without `upload: false` or `dryRun`), the plugin generates no source
|
|
154
182
|
* maps it would have to delete again: the build is left as it was. */
|
|
155
183
|
key?: string;
|
|
@@ -170,7 +198,26 @@ interface AutterSourceMapsOptions {
|
|
|
170
198
|
failOnError?: boolean;
|
|
171
199
|
/** No log output. */
|
|
172
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;
|
|
173
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;
|
|
174
221
|
interface BuildOutputResult {
|
|
175
222
|
injected: InjectResult[];
|
|
176
223
|
upload: UploadSummary | null;
|
|
@@ -201,6 +248,9 @@ declare function processBuildOutput(input: {
|
|
|
201
248
|
/** Map files the build emitted (absolute), when the bundler lists them;
|
|
202
249
|
* the maps of `files` (or of every bundle under `dir`) are added. */
|
|
203
250
|
maps?: string[];
|
|
251
|
+
/** Server output (RF-05): `urlPrefix` is serverUrlPrefix(), which the
|
|
252
|
+
* public `options.urlPrefix` never overrides. */
|
|
253
|
+
server?: boolean;
|
|
204
254
|
}): Promise<BuildOutputResult>;
|
|
205
255
|
|
|
206
|
-
export { type AutterSourceMapsOptions as A, BUNDLE_RE as B, DEFAULT_ENDPOINT as D, FatalUploadError as F, type InjectOptions as I, MAX_MAP_CHARS as M,
|
|
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 };
|
|
@@ -667,6 +667,10 @@ var DEFAULT_ENDPOINT = "https://otlp.autter.dev";
|
|
|
667
667
|
var MAX_MAP_CHARS = 32 * 1024 * 1024;
|
|
668
668
|
var MAX_UPLOAD_BYTES = MAX_MAP_CHARS + 1024 * 1024;
|
|
669
669
|
var LEGACY_MAX_BYTES = 5 * 1024 * 1024;
|
|
670
|
+
var UPLOAD_KEY_PREFIX = "autter_rtu_";
|
|
671
|
+
var SERVER_KEY_PREFIX = "autter_rt_";
|
|
672
|
+
var CLIENT_KEY_PREFIX = "autter_rtc_";
|
|
673
|
+
var KEY_ADVICE = "set AUTTER_RUNTIME_KEY to an upload-only key (autter_rtu_\u2026, recommended for CI: it can only upload source maps) or the full server key (autter_rt_\u2026, also works)";
|
|
670
674
|
var gzip = promisify(gzipCallback);
|
|
671
675
|
var CHECK_BATCH = 500;
|
|
672
676
|
var MAX_ATTEMPTS = 3;
|
|
@@ -680,9 +684,56 @@ function servedFilename(urlPrefix, path) {
|
|
|
680
684
|
if (!/^[a-z][\w+.-]*:\/\//i.test(prefix) && !prefix.startsWith("/")) prefix = `/${prefix}`;
|
|
681
685
|
return `${prefix.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
682
686
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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;
|
|
686
737
|
}
|
|
687
738
|
function bytesOf(buffer) {
|
|
688
739
|
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -693,7 +744,7 @@ function fits(json, body) {
|
|
|
693
744
|
function uploadBody(release, filename, debugId, json) {
|
|
694
745
|
return `{"release":${JSON.stringify(release)},"filename":${JSON.stringify(filename)},${debugId ? `"debugId":${JSON.stringify(debugId)},` : ""}"map":${json}}`;
|
|
695
746
|
}
|
|
696
|
-
async function prepare(bundle, urlPrefix, release, log, compress) {
|
|
747
|
+
async function prepare(bundle, urlPrefix, release, log, compress, sourcesContent) {
|
|
697
748
|
let map;
|
|
698
749
|
try {
|
|
699
750
|
map = JSON.parse(await readFile3(bundle.map, "utf8"));
|
|
@@ -708,9 +759,10 @@ async function prepare(bundle, urlPrefix, release, log, compress) {
|
|
|
708
759
|
const filename = servedFilename(urlPrefix, bundle.path);
|
|
709
760
|
const { [SCOPES_KEY]: _stale, ...plain } = map;
|
|
710
761
|
const scopes = computeScopes(code, map);
|
|
711
|
-
|
|
762
|
+
const stored = { sourcesContent };
|
|
763
|
+
let json = storedMapJson(plain, stored);
|
|
712
764
|
if ("scopes" in scopes) {
|
|
713
|
-
const scoped = storedMapJson({ ...plain, [SCOPES_KEY]: scopes.scopes });
|
|
765
|
+
const scoped = storedMapJson({ ...plain, [SCOPES_KEY]: scopes.scopes }, stored);
|
|
714
766
|
if (fits(scoped, uploadBody(release, filename, debugId, scoped))) json = scoped;
|
|
715
767
|
else if (fits(json, uploadBody(release, filename, debugId, json))) {
|
|
716
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)`);
|
|
@@ -757,7 +809,7 @@ async function uploadDir(dir, options = {}) {
|
|
|
757
809
|
};
|
|
758
810
|
const items = [];
|
|
759
811
|
for (const bundle of await findBundles(dir, options.files)) {
|
|
760
|
-
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);
|
|
761
813
|
if ("error" in prepared) {
|
|
762
814
|
summary.failed.push({ path: bundle.path, filename: servedFilename(options.urlPrefix, bundle.path), error: prepared.error });
|
|
763
815
|
} else if (prepared.mapChars > MAX_MAP_CHARS || prepared.bytes > MAX_UPLOAD_BYTES) {
|
|
@@ -775,10 +827,13 @@ async function uploadDir(dir, options = {}) {
|
|
|
775
827
|
return summary;
|
|
776
828
|
}
|
|
777
829
|
const key = (options.key ?? env.AUTTER_RUNTIME_KEY ?? "").trim();
|
|
778
|
-
if (!key) throw new FatalUploadError(
|
|
779
|
-
if (key.startsWith(
|
|
780
|
-
throw new FatalUploadError(
|
|
830
|
+
if (!key) throw new FatalUploadError(`no ingest key: ${KEY_ADVICE}`);
|
|
831
|
+
if (key.startsWith(CLIENT_KEY_PREFIX)) {
|
|
832
|
+
throw new FatalUploadError(`AUTTER_RUNTIME_KEY is a publishable client key (autter_rtc_\u2026), which cannot upload source maps: ${KEY_ADVICE}`);
|
|
781
833
|
}
|
|
834
|
+
const rejected = (status, detail) => new FatalUploadError(
|
|
835
|
+
`the ingester rejected the key (${status}): ${detail}` + (key.startsWith(UPLOAD_KEY_PREFIX) ? " (upload keys, autter_rtu_\u2026, need ingester 1.7.0 or later; the server key works with any)" : "")
|
|
836
|
+
);
|
|
782
837
|
const headers = { "content-type": "application/json", authorization: `Bearer ${key}` };
|
|
783
838
|
const uploadHeaders = { ...headers, "content-encoding": "gzip" };
|
|
784
839
|
const skipped = /* @__PURE__ */ new Set();
|
|
@@ -800,9 +855,7 @@ async function uploadDir(dir, options = {}) {
|
|
|
800
855
|
);
|
|
801
856
|
break;
|
|
802
857
|
}
|
|
803
|
-
if (res.status === 401 || res.status === 403)
|
|
804
|
-
throw new FatalUploadError(`the ingester rejected the key (${res.status}): ${(await res.text()).slice(0, 300)}`);
|
|
805
|
-
}
|
|
858
|
+
if (res.status === 401 || res.status === 403) throw rejected(res.status, (await res.text()).slice(0, 300));
|
|
806
859
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
807
860
|
const body = await res.json();
|
|
808
861
|
const present = new Set(Array.isArray(body.present) ? body.present : []);
|
|
@@ -833,7 +886,7 @@ async function uploadDir(dir, options = {}) {
|
|
|
833
886
|
detail = err.message;
|
|
834
887
|
}
|
|
835
888
|
if (status === 401 || status === 403) {
|
|
836
|
-
fatal =
|
|
889
|
+
fatal = rejected(status, detail);
|
|
837
890
|
return;
|
|
838
891
|
}
|
|
839
892
|
const retryable = status === 0 || status === 429 || status >= 500;
|
|
@@ -876,7 +929,11 @@ function strip(item) {
|
|
|
876
929
|
|
|
877
930
|
// src/build.ts
|
|
878
931
|
import { existsSync as existsSync2 } from "fs";
|
|
879
|
-
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
|
+
}
|
|
880
937
|
function buildKey(options) {
|
|
881
938
|
const key = (options.key ?? process.env.AUTTER_RUNTIME_KEY ?? "").trim();
|
|
882
939
|
return key || null;
|
|
@@ -939,12 +996,13 @@ async function processBuildOutput(input) {
|
|
|
939
996
|
result.upload = await uploadDir(input.dir, {
|
|
940
997
|
files: input.files,
|
|
941
998
|
release: options.release,
|
|
942
|
-
urlPrefix: options.urlPrefix ?? input.urlPrefix,
|
|
999
|
+
urlPrefix: input.server ? input.urlPrefix : options.urlPrefix ?? input.urlPrefix,
|
|
943
1000
|
endpoint: options.endpoint,
|
|
944
1001
|
key: key ?? void 0,
|
|
945
1002
|
dryRun: options.dryRun,
|
|
946
1003
|
concurrency: options.concurrency,
|
|
947
1004
|
deleteAfterUpload: options.deleteAfterUpload ?? input.deleteAfterUpload,
|
|
1005
|
+
...options.sourcesContent === false ? { sourcesContent: false } : {},
|
|
948
1006
|
log
|
|
949
1007
|
});
|
|
950
1008
|
for (const line of formatUploadSummary(result.upload)) log(line);
|
|
@@ -1023,11 +1081,18 @@ export {
|
|
|
1023
1081
|
DEFAULT_ENDPOINT,
|
|
1024
1082
|
MAX_MAP_CHARS,
|
|
1025
1083
|
MAX_UPLOAD_BYTES,
|
|
1084
|
+
UPLOAD_KEY_PREFIX,
|
|
1085
|
+
SERVER_KEY_PREFIX,
|
|
1086
|
+
CLIENT_KEY_PREFIX,
|
|
1087
|
+
KEY_ADVICE,
|
|
1026
1088
|
resolveEndpoint,
|
|
1027
1089
|
servedFilename,
|
|
1090
|
+
isInAppSource,
|
|
1091
|
+
storedSourcesContent,
|
|
1028
1092
|
storedMapJson,
|
|
1029
1093
|
FatalUploadError,
|
|
1030
1094
|
uploadDir,
|
|
1095
|
+
serverUrlPrefix,
|
|
1031
1096
|
shouldGenerateMaps,
|
|
1032
1097
|
logMapsNotGenerated,
|
|
1033
1098
|
formatUploadSummary,
|