@getargus/build-tools 0.0.0-stage → 0.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/LICENSE +21 -0
- package/README.md +218 -2
- package/bin/argus-build.cjs +8 -0
- package/dist/cli-C6Vb06ix.d.ts +317 -0
- package/dist/cli-Poqz5E1p.d.cts +317 -0
- package/dist/cli.cjs +1283 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1254 -0
- package/dist/cli.js.map +1 -0
- package/dist/ids-T7ddcOxQ.d.cts +41 -0
- package/dist/ids-T7ddcOxQ.d.ts +41 -0
- package/dist/index.cjs +1548 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +1486 -0
- package/dist/index.js.map +1 -0
- package/dist/metro.cjs +117 -0
- package/dist/metro.cjs.map +1 -0
- package/dist/metro.d.cts +40 -0
- package/dist/metro.d.ts +40 -0
- package/dist/metro.js +92 -0
- package/dist/metro.js.map +1 -0
- package/dist/next.cjs +609 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +60 -0
- package/dist/next.d.ts +60 -0
- package/dist/next.js +582 -0
- package/dist/next.js.map +1 -0
- package/package.json +67 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Argus contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,219 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @getargus/build-tools
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Argus' own build integration — it replaces vendor Metro / Next wrappers. Node only, no
|
|
4
|
+
dependencies. Four jobs:
|
|
5
|
+
|
|
6
|
+
0. **Setup**: `npx @getargus/build-tools init` detects Next.js / Node / Expo and writes the
|
|
7
|
+
instrumentation files, the config wrapper and the env names ([Setup](#setup-argus-build-init)).
|
|
8
|
+
1. **Identity**: generate an artifact set id at build start (`artifacts-v1.md` §1).
|
|
9
|
+
2. **Injection**: get that id into the bundle as *source* that goes through the bundler, so the
|
|
10
|
+
bundle and its source map stay consistent. Built output, `.map` files and Hermes bytecode are
|
|
11
|
+
never patched.
|
|
12
|
+
3. **Upload**: find the artifacts a build produced, decide which artifact set each belongs to,
|
|
13
|
+
and upload them — for Next.js at the end of `next build`, with no extra CI step.
|
|
14
|
+
|
|
15
|
+
> **Status.** The Next.js path is verified against real `next build` runs of Next 15.5 (webpack
|
|
16
|
+
> and Turbopack, also `output: 'standalone'`) with a local artifacts server: both sets uploaded
|
|
17
|
+
> once, browser maps gone afterwards. `init` was run in a fresh Next app that was then built for
|
|
18
|
+
> real. No real Metro, EAS, Xcode or Gradle build has been run through this package. See
|
|
19
|
+
> [Verification status](#verification-status).
|
|
20
|
+
|
|
21
|
+
## Setup: `argus-build init`
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
npx @getargus/build-tools init # show what would be written (asks before writing on a terminal)
|
|
25
|
+
npx @getargus/build-tools init --yes # write
|
|
26
|
+
npx @getargus/build-tools init --yes --dsn 'https://<ingest key>@<host>'
|
|
27
|
+
npx @getargus/build-tools init --dry-run # never writes, whatever else is given
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Reads `package.json` in the current directory (in a monorepo: run it inside the app's package;
|
|
31
|
+
`--dir <path>` points elsewhere) and prints every file it writes, in full.
|
|
32
|
+
|
|
33
|
+
| Project | Written | Printed for you to do |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| Next.js (app or pages router, `src/` or not, TS or JS) | `instrumentation.ts` (`register` + `onRequestError`), `instrumentation-client.ts`, `withArgus()` around the export of `next.config.*` (or a new `next.config.mjs`), the variable names in `.env.example` | install command, where to set the DSN and the upload variables |
|
|
36
|
+
| Node (ESM or CommonJS, TS or JS) | `argus.ts` / `.js` / `.mjs` / `.cjs` with `init()` (in `src/` when it exists), `ARGUS_DSN=` in `.env.example` | install command, the `import './argus.js'` line for the entry file |
|
|
37
|
+
| Expo / React Native | nothing | packages, the `app.json` plugin entry, the `metro.config.js` lines, the upload command |
|
|
38
|
+
|
|
39
|
+
What it will not do:
|
|
40
|
+
|
|
41
|
+
* **Overwrite a file.** An existing `instrumentation.*` or `argus.*` is left alone and the snippet
|
|
42
|
+
to merge is printed. `next.config.*` is rewritten only when its export is a single
|
|
43
|
+
`export default <identifier>` / `module.exports = <identifier>`; any other shape (an object
|
|
44
|
+
literal, another wrapper, a function) gets the two-line diff printed instead.
|
|
45
|
+
* **Put a secret in a tracked file.** `.env.example` gets names without values. `--dsn` writes to
|
|
46
|
+
`.env.local` (Next) or `.env` (Node) only if git ignores that file; otherwise the lines are
|
|
47
|
+
printed.
|
|
48
|
+
* **Install anything.** The command for the detected package manager (`packageManager` field,
|
|
49
|
+
else the nearest lockfile: pnpm, yarn, bun, npm) is printed.
|
|
50
|
+
* **Change anything on a second run.** Files that already use Argus are reported as `ok`.
|
|
51
|
+
|
|
52
|
+
Without `--yes` and without a terminal (CI, a pipe) it only prints the plan.
|
|
53
|
+
|
|
54
|
+
## Programmatic API
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import {
|
|
58
|
+
newArtifactSetId, isValidArtifactSetId, resolveArtifactSetId,
|
|
59
|
+
artifactSetPrelude, writeArtifactSetModule /* alias: injectArtifactSetId */,
|
|
60
|
+
planNext, planExpoExport, planNative, planFiles, uploadArgs, formatCommand, runUploads,
|
|
61
|
+
uploadNextBuild, // what withArgus runs at the end of `next build`
|
|
62
|
+
planInit, applyInit, runInit, // `argus-build init` as functions
|
|
63
|
+
} from '@getargus/build-tools';
|
|
64
|
+
import { withArgus } from '@getargus/build-tools/metro'; // Metro config helper
|
|
65
|
+
import { withArgus } from '@getargus/build-tools/next'; // Next.js config helper
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
* `newArtifactSetId(prefix)` → `<prefix>-<uuidv7 hex>`, same format as `argus artifacts new-id`.
|
|
69
|
+
* `resolveArtifactSetId(option, platform, env)` — `ARGUS_ARTIFACT_SET_ID_<PLATFORM>` →
|
|
70
|
+
`ARGUS_ARTIFACT_SET_ID` → option. Environment wins so CI can inject the id; the
|
|
71
|
+
`@getargus/expo` config plugin follows the same rule for the native side.
|
|
72
|
+
* `writeArtifactSetModule({ file, artifactSetId })` writes a generated source file defining
|
|
73
|
+
`globalThis.__ARGUS_ARTIFACT_SET_ID__`. It is idempotent and refuses to overwrite any file it
|
|
74
|
+
did not generate.
|
|
75
|
+
* `plan*()` return `UploadPlan`s (one artifact set, one platform, explicit file list, warnings);
|
|
76
|
+
`uploadArgs(plan)` turns one into CLI arguments; `runUploads(plans)` executes them in order and
|
|
77
|
+
stops at the first failure.
|
|
78
|
+
|
|
79
|
+
### Metro (React Native / Expo)
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
// metro.config.js
|
|
83
|
+
const { getDefaultConfig } = require('expo/metro-config');
|
|
84
|
+
const { withArgus } = require('@getargus/build-tools/metro');
|
|
85
|
+
module.exports = withArgus(getDefaultConfig(__dirname), { /* artifactSetId: { ios, android } */ });
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Writes `<projectRoot>/.argus/argus-artifact-set.<platform>.js` when the config is evaluated and
|
|
89
|
+
appends the matching file to `serializer.getPolyfills({ platform })`, after the existing
|
|
90
|
+
polyfills. A polyfill is part of the bundle Metro serialises and of the map it composes, and it
|
|
91
|
+
runs before any module — before Hermes compiles anything. Add `.argus/` to `.gitignore`.
|
|
92
|
+
|
|
93
|
+
An artifact set has one platform. A single-platform build (EAS build, `expo run:*`) can use
|
|
94
|
+
`ARGUS_ARTIFACT_SET_ID`; a process that bundles several platforms (`expo export`) needs
|
|
95
|
+
`ARGUS_ARTIFACT_SET_ID_IOS` and `ARGUS_ARTIFACT_SET_ID_ANDROID`. A platform without an id is
|
|
96
|
+
bundled unchanged and a warning is printed once.
|
|
97
|
+
|
|
98
|
+
### Next.js
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
// next.config.mjs
|
|
102
|
+
import { withArgus } from '@getargus/build-tools/next';
|
|
103
|
+
export default withArgus({ /* your config */ }, { /* options */ });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
With `ARGUS_URL` and `ARGUS_UPLOAD_KEY` in the environment of `next build`, that is all: the
|
|
107
|
+
build gets an artifact set id, compiles it in, uploads the browser and the server source maps to
|
|
108
|
+
their two sets and removes the browser maps from the output. Without those variables nothing is
|
|
109
|
+
uploaded and nothing is printed.
|
|
110
|
+
|
|
111
|
+
* **Id.** Adds `env.NEXT_PUBLIC_ARGUS_ARTIFACT_SET_ID` (browser set) and
|
|
112
|
+
`env.ARGUS_SERVER_ARTIFACT_SET_ID` (server set, default `<id>-node`). Next inlines `env` values
|
|
113
|
+
at build time; no DefinePlugin and no post-processing. Pass them to the SDKs:
|
|
114
|
+
`initClient({ artifactSetId: process.env.NEXT_PUBLIC_ARGUS_ARTIFACT_SET_ID })` in the browser,
|
|
115
|
+
`register({ artifactSetId: process.env.ARGUS_SERVER_ARTIFACT_SET_ID })` on the server
|
|
116
|
+
(`argus-build init` writes both). The id comes from `ARGUS_ARTIFACT_SET_ID[_WEB]`, else the
|
|
117
|
+
`artifactSetId` option; a build that uploads and has neither generates one
|
|
118
|
+
(`next-<uuidv7>`) and keeps it in `ARGUS_ARTIFACT_SET_ID_WEB`, so Next's build workers compile
|
|
119
|
+
in the same id the upload uses. Do not hard-code an id: a set is one build.
|
|
120
|
+
* **Upload** — `upload: 'auto' | true | false` (default `'auto'`: when both variables are set).
|
|
121
|
+
It runs in Next's `compiler.runAfterProductionCompile` hook: once per `next build`, in the main
|
|
122
|
+
process, after the server, edge and client compilers (webpack or Turbopack) have written their
|
|
123
|
+
output, before type checking and static generation. `ARGUS_RELEASE` and `ARGUS_GIT_SHA` are
|
|
124
|
+
put on the manifest. **Needs Next 15.4+**; on an older Next a warning says so once and you run
|
|
125
|
+
`argus-build upload next --delete-maps` after the build.
|
|
126
|
+
* **A failed upload** — `onUploadError: 'warn' | 'fail'` (default `'warn'`): the reason is
|
|
127
|
+
printed and the build continues, because a monitoring outage must not block a deploy. `'fail'`
|
|
128
|
+
fails the build.
|
|
129
|
+
* **Browser maps** — `productionBrowserSourceMaps` is set unless you set it yourself; Next then
|
|
130
|
+
writes `.next/static/**/*.js.map` **and would serve them**. `deleteBrowserMapsAfterUpload`
|
|
131
|
+
(default: true when `withArgus` turned the maps on, false when your config did) removes them
|
|
132
|
+
when the upload step is over — also after a failed upload that only warned, since that build
|
|
133
|
+
goes on to be deployed. With `onUploadError: 'fail'` they are left for a retry. **A build
|
|
134
|
+
without upload credentials emits no browser maps**; when you upload in a separate step, pass
|
|
135
|
+
`browserSourceMaps: true` and run `argus-build upload next --delete-maps` before deploying.
|
|
136
|
+
* **Server maps** — `serverSourceMaps` sets `devtool: 'source-map'` for the production server
|
|
137
|
+
compilation (your `webpack` hook is chained). Default: on when the build uploads, off
|
|
138
|
+
otherwise. Turbopack writes server maps on its own. If you run Node with
|
|
139
|
+
`--enable-source-maps`, stacks are already mapped and marked `premapped` by `@getargus/node`;
|
|
140
|
+
do not rely on both.
|
|
141
|
+
* **Names** — browser chunks are named `<urlPrefix>static/...`; `urlPrefix` defaults to
|
|
142
|
+
`/_next/` below the path of `assetPrefix`, else `basePath`.
|
|
143
|
+
|
|
144
|
+
### Expo / React Native: upload after the build
|
|
145
|
+
|
|
146
|
+
There is no Metro upload hook yet; run one command after the build, with `ARGUS_URL` and
|
|
147
|
+
`ARGUS_UPLOAD_KEY` set:
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
# after `expo export` (OTA update); ids are the ones the bundles were built with
|
|
151
|
+
npx argus-build upload expo-export --ios-set-id "$ARGUS_ARTIFACT_SET_ID_IOS" --android-set-id "$ARGUS_ARTIFACT_SET_ID_ANDROID"
|
|
152
|
+
|
|
153
|
+
# after an EAS / native build (needs the argus binary for dSYMs and mapping.txt)
|
|
154
|
+
npx argus-build upload ios --set-id "$ARGUS_ARTIFACT_SET_ID" --dir <xcarchive or dSYMs folder>
|
|
155
|
+
npx argus-build upload android --set-id "$ARGUS_ARTIFACT_SET_ID" --dir android/app/build
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
On EAS, the `eas-build-on-success` npm script is the place for it.
|
|
159
|
+
|
|
160
|
+
## CLI
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
argus-build init [--dsn <dsn>] [--yes] [--dry-run] [--dir <project>] # see Setup
|
|
164
|
+
argus-build new-id [--prefix <p>]
|
|
165
|
+
argus-build prelude --set-id <id> --out <file>
|
|
166
|
+
argus-build collect <target> [options] # print the per-set file list and the argus commands; no network
|
|
167
|
+
argus-build upload <target> [options] # upload each set (--dry-run, --print, --transport)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The upload key is read from `ARGUS_UPLOAD_KEY`; it is never put on a command line. `ARGUS_URL`
|
|
171
|
+
selects the server.
|
|
172
|
+
|
|
173
|
+
**No binary needed for JavaScript.** Sets that contain only source maps and bundles (`next`,
|
|
174
|
+
`expo-export`, `files` with JS) are uploaded from Node over HTTP: manifest, missing blobs,
|
|
175
|
+
finalize. Re-running uploads nothing that is already there, and the command fails unless the set
|
|
176
|
+
ends up `ready`. Sets with native symbols (dSYM, ELF, R8 mapping) go through the `argus` binary,
|
|
177
|
+
which validates them with the server's own parser; `ARGUS_BIN` / `--argus-bin` selects it and
|
|
178
|
+
also forces it for JS sets. `--transport http|cli|auto` (env `ARGUS_UPLOAD_TRANSPORT`) overrides
|
|
179
|
+
the choice.
|
|
180
|
+
|
|
181
|
+
| Target | Artifact sets | Files | Naming (`--strip-prefix` / `--url-prefix`) |
|
|
182
|
+
|---|---|---|---|
|
|
183
|
+
| `next` | `next:browser` → `--set-id`, platform `web` | `.next/static/**/*.js` + `.js.map` (pairs only) | `/_next/static/chunks/app-123.js` (`.next` / `/_next/`; change with `--url-prefix` for a basePath) |
|
|
184
|
+
| | `next:server` → `--server-set-id` (default `<id>-node`), platform `node` | `.next/server/**/*.js` + `.js.map` | `.next/server/app/page.js` (project dir / none) |
|
|
185
|
+
| `expo-export` | one per platform: `--ios-set-id`, `--android-set-id`; `--ota`, `--update-id`, `--runtime-version`, `--parent-set-id` | the bundle named in `dist/metadata.json` + its `.map` | `entry-<hash>.hbc` (file name) |
|
|
186
|
+
| `ios` | `--set-id`, platform `ios` | every `*.dSYM` under `--dir` (passed as directories), `*.jsbundle.map` / `*.bundle.map` and the bundle next to it | `main.jsbundle` (file name) |
|
|
187
|
+
| `android` | `--set-id`, platform `android` | `mapping.txt`, `*.bundle.map`, the bundle (`--bundle`), `.so` with `--native-libs` | `index.android.bundle` (file name) |
|
|
188
|
+
| `files` | `--set-id`, `--platform` | `*.map` + generated file under `--dir` | as given |
|
|
189
|
+
|
|
190
|
+
Browser and server chunks of a Next build never share a set: same file names can exist in both,
|
|
191
|
+
and the server resolves a frame by artifact set + name. `next --delete-maps` removes the browser
|
|
192
|
+
`.map` files from `.next/static` after **all** uploads succeeded.
|
|
193
|
+
|
|
194
|
+
Turbopack names a browser chunk's map after the map's own hash (`21…80.js` →
|
|
195
|
+
`4b…ca.js.map`); such a map is paired with its chunk through the chunk's `sourceMappingURL`
|
|
196
|
+
comment and uploaded under the chunk's name. Only the HTTP transport can say that; the `argus`
|
|
197
|
+
binary names a map after its file, so do not force `ARGUS_BIN` for a Turbopack build.
|
|
198
|
+
|
|
199
|
+
Update id for `expo-export`: taken from `--update-id` / `--ios-update-id` / `--android-update-id`,
|
|
200
|
+
else from `metadata.json` if it has `fileMetadata.<platform>.updateId|id` or a top-level
|
|
201
|
+
`updateId|id`. A stock `expo export` `metadata.json` is not known to contain one, so pass the id
|
|
202
|
+
your update server assigned; without it the plan still works (matching is by artifact set id) and
|
|
203
|
+
a warning is printed.
|
|
204
|
+
|
|
205
|
+
## Verification status
|
|
206
|
+
|
|
207
|
+
| Integration point | State |
|
|
208
|
+
|---|---|
|
|
209
|
+
| Id format, validation, env precedence | unit-tested |
|
|
210
|
+
| Generated prelude defines the global; generated file is idempotent; foreign files are never overwritten | unit-tested (evaluated in a `vm` context) |
|
|
211
|
+
| Metro `withArgus`: polyfill list composition per platform, chaining, missing id | unit-tested against a structural config. **Not run in Metro**: that Metro (as shipped with Expo SDK 57) transforms a file under `<projectRoot>/.argus/`, includes it in the bundle and its source map, that Expo's serializer honours `getPolyfills` on every platform, and that the global is readable from Hermes bytecode |
|
|
212
|
+
| Next `withArgus`: `env`, source map flags, webpack hook chaining, config functions | unit-tested against plain objects, and run in `next build` (Next 15.5.29, `examples/next-trpc`): the generated id was found in both the browser and the server bundles and equals the uploaded set ids |
|
|
213
|
+
| Next upload at the end of the build | **run for real** against a local artifacts-v1 server, webpack: 2 manifests, 2 finalizes, each blob PUT once, 0 `.map` left under `.next/static`, server maps kept. Same with `--turbopack` and with `output: 'standalone'`. A 503 server: build passes with a warning by default and fails with `onUploadError: 'fail'`. No credentials: no output, no hook. **Not verified**: Next 15.4 itself (the hook was checked to exist in its package, not run), Next 16, rspack, `experimental-build-mode`, and symbolication of a Turbopack frame on the real server |
|
|
214
|
+
| `argus-build init` | unit-tested in temp directories (detection, file contents, no overwrite, dry run, idempotency, DSN only into git-ignored files); run in a fresh TypeScript `src/` app-router project whose result passed a real `next build` including type checking, and in a minimal ESM Node project. **Not verified**: that the first event reaches a server from those projects (no server was run), the Expo steps, the pages router beyond detection |
|
|
215
|
+
| File discovery and set separation for Next / Expo export / iOS / Android | unit-tested on fixture trees shaped like the documented outputs; the Next layout also on real `.next` output of 15.5 (webpack and Turbopack). **Not verified**: `expo export` of SDK 57, Xcode / Gradle / EAS artifact locations |
|
|
216
|
+
| CLI arguments | unit-tested; additionally accepted by the real `argus artifacts upload --dry-run`, whose manifest showed the expected `platform`, `is_embedded`, `update_id`, `parent_artifact_set_id`, `name` and `bundle_name` for web, Next and Expo-export fixtures |
|
|
217
|
+
| dSYM / `mapping.txt` / `.so` handling by the Rust CLI | not exercised here (fixtures are placeholders, not real symbol files) |
|
|
218
|
+
| Whether the names produced here equal the file names in real runtime stack frames (OTA bundles loaded by expo-updates from its own storage path in particular) | **not verified**; if they differ, symbolication reports `missing_artifact` rather than a wrong line |
|
|
219
|
+
| A real upload followed by symbolication of a known frame | not done |
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { B as BuildPlatform } from './ids-T7ddcOxQ.js';
|
|
2
|
+
|
|
3
|
+
type ArtifactFileKind = 'sourcemap' | 'js_bundle' | 'dsym' | 'proguard' | 'elf';
|
|
4
|
+
/** Mirrors `classify` in crates/cli/src/upload.rs (hermes maps are detected by the CLI from content). */
|
|
5
|
+
declare function classifyArtifact(path: string): ArtifactFileKind | undefined;
|
|
6
|
+
/** Recursive file list, sorted, symlinks not followed. */
|
|
7
|
+
declare function walkFiles(dir: string, opts?: {
|
|
8
|
+
skipDirs?: ReadonlySet<string>;
|
|
9
|
+
}): string[];
|
|
10
|
+
interface UploadMeta {
|
|
11
|
+
release?: string | undefined;
|
|
12
|
+
dist?: string | undefined;
|
|
13
|
+
nativeRelease?: string | undefined;
|
|
14
|
+
nativeBuild?: string | undefined;
|
|
15
|
+
runtimeVersion?: string | undefined;
|
|
16
|
+
gitSha?: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
interface UploadPlan extends UploadMeta {
|
|
19
|
+
/** e.g. `next:browser`, `expo-export:ios`, `native:android`. */
|
|
20
|
+
label: string;
|
|
21
|
+
artifactSetId: string;
|
|
22
|
+
platform: BuildPlatform;
|
|
23
|
+
/** Absolute paths handed to the CLI. A `.dSYM` bundle is passed as its directory. */
|
|
24
|
+
paths: string[];
|
|
25
|
+
/** Every file that will be part of the set, with its kind — what `collect` prints. */
|
|
26
|
+
files: {
|
|
27
|
+
path: string;
|
|
28
|
+
kind: ArtifactFileKind;
|
|
29
|
+
size: number;
|
|
30
|
+
}[];
|
|
31
|
+
/** `--strip-prefix`: removed from JS paths to form the name frames are matched against. */
|
|
32
|
+
stripPrefix?: string | undefined;
|
|
33
|
+
/** `--url-prefix`: prepended to that name. */
|
|
34
|
+
urlPrefix?: string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Source map → generated file, for maps not named `<generated file>.map` (Turbopack chunks).
|
|
37
|
+
* Only the HTTP transport honours it; the argus CLI names a map after its own file name.
|
|
38
|
+
*/
|
|
39
|
+
generatedFor?: Record<string, string> | undefined;
|
|
40
|
+
/** OTA update (not embedded in a native binary). */
|
|
41
|
+
ota?: boolean | undefined;
|
|
42
|
+
updateId?: string | undefined;
|
|
43
|
+
/** Artifact set of the native build this OTA update runs on (native symbols are resolved there). */
|
|
44
|
+
parentSetId?: string | undefined;
|
|
45
|
+
warnings: string[];
|
|
46
|
+
}
|
|
47
|
+
interface NextPlanOptions extends UploadMeta {
|
|
48
|
+
/** Project directory containing the build output. Default `process.cwd()`. */
|
|
49
|
+
projectDir?: string;
|
|
50
|
+
/** Build output directory relative to the project. Default `.next`. */
|
|
51
|
+
distDir?: string;
|
|
52
|
+
/** Artifact set of the browser chunks. */
|
|
53
|
+
artifactSetId: string;
|
|
54
|
+
/** Artifact set of the server chunks. Default `<artifactSetId>-node`. */
|
|
55
|
+
serverArtifactSetId?: string;
|
|
56
|
+
/** Public path the browser loads chunks from. Default `/_next/` (add your basePath / assetPrefix path). */
|
|
57
|
+
urlPrefix?: string;
|
|
58
|
+
/** Upload the generated JS next to each map. Default true. */
|
|
59
|
+
includeBundles?: boolean;
|
|
60
|
+
}
|
|
61
|
+
declare function planNext(options: NextPlanOptions): UploadPlan[];
|
|
62
|
+
/** Deletes browser `.map` files from the build output so they are never deployed or served. Returns the removed paths. */
|
|
63
|
+
declare function browserSourceMaps(projectDir: string, distDir?: string): string[];
|
|
64
|
+
type MobilePlatform = 'ios' | 'android';
|
|
65
|
+
interface ExpoExportPlanOptions extends UploadMeta {
|
|
66
|
+
/** Output directory of `expo export`. Default `dist`. */
|
|
67
|
+
exportDir?: string;
|
|
68
|
+
/** One artifact set per platform (an artifact set has exactly one platform). */
|
|
69
|
+
artifactSetIds: Partial<Record<MobilePlatform, string>>;
|
|
70
|
+
/** Limit to these platforms. Default: every platform in `metadata.json` that has an id. */
|
|
71
|
+
platforms?: MobilePlatform[];
|
|
72
|
+
/** OTA update id per platform (or one string for a single platform). Falls back to `metadata.json` when it carries one. */
|
|
73
|
+
updateId?: string | Partial<Record<MobilePlatform, string>>;
|
|
74
|
+
/** Artifact set of the native build the update targets, per platform. */
|
|
75
|
+
parentSetIds?: Partial<Record<MobilePlatform, string>>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* One plan per platform from `expo export` output: the bundle named by
|
|
79
|
+
* `metadata.json → fileMetadata.<platform>.bundle` and its `.map` (export with
|
|
80
|
+
* `--dump-sourcemap` / `--source-maps`). Marked `--ota` with update id, runtime version and the
|
|
81
|
+
* parent (native build) set.
|
|
82
|
+
*/
|
|
83
|
+
declare function planExpoExport(options: ExpoExportPlanOptions): UploadPlan[];
|
|
84
|
+
interface NativePlanOptions extends UploadMeta {
|
|
85
|
+
platform: MobilePlatform;
|
|
86
|
+
artifactSetId: string;
|
|
87
|
+
/**
|
|
88
|
+
* Directories / files to search: an `.xcarchive`, a `dSYMs` folder, `ios/build`,
|
|
89
|
+
* `android/app/build`, an EAS artifacts folder, or explicit files.
|
|
90
|
+
*/
|
|
91
|
+
searchPaths: string[];
|
|
92
|
+
/** Embedded JS bundle and its (Hermes-composed) source map, when not found by the search. */
|
|
93
|
+
bundle?: string;
|
|
94
|
+
sourcemap?: string;
|
|
95
|
+
/** Android: also upload unstripped `.so` files found under the search paths. Default false. */
|
|
96
|
+
includeNativeLibs?: boolean;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The embedded build of one platform — one artifact set holding
|
|
100
|
+
* - iOS: every `.dSYM` bundle + `main.jsbundle(.map)`;
|
|
101
|
+
* - Android: R8 `mapping.txt` + `index.android.bundle(.map)` (+ unstripped `.so` on request).
|
|
102
|
+
* JS files keep their file name (`index.android.bundle`), which is what Hermes frames report.
|
|
103
|
+
*/
|
|
104
|
+
declare function planNative(options: NativePlanOptions): UploadPlan;
|
|
105
|
+
interface FilesPlanOptions extends UploadMeta {
|
|
106
|
+
artifactSetId: string;
|
|
107
|
+
platform: BuildPlatform;
|
|
108
|
+
/** Files or directories; directories are searched for `*.map` with their generated file. */
|
|
109
|
+
paths: string[];
|
|
110
|
+
stripPrefix?: string;
|
|
111
|
+
urlPrefix?: string;
|
|
112
|
+
includeBundles?: boolean;
|
|
113
|
+
}
|
|
114
|
+
/** Any bundler output (esbuild, Vite, a worker build…): source maps plus their generated files. */
|
|
115
|
+
declare function planFiles(options: FilesPlanOptions): UploadPlan;
|
|
116
|
+
/**
|
|
117
|
+
* Arguments for the Rust CLI: `argus artifacts upload …` (flags as defined in
|
|
118
|
+
* crates/cli/src/upload.rs). The upload key is never put on the command line; the CLI reads
|
|
119
|
+
* `ARGUS_UPLOAD_KEY` (and `ARGUS_URL`) from the environment.
|
|
120
|
+
*/
|
|
121
|
+
declare function uploadArgs(plan: UploadPlan, opts?: {
|
|
122
|
+
dryRun?: boolean;
|
|
123
|
+
url?: string;
|
|
124
|
+
}): string[];
|
|
125
|
+
/** The name a JS file gets in the manifest (`bundle_name`), mirroring the CLI's rule. For display and tests. */
|
|
126
|
+
declare function manifestName(plan: Pick<UploadPlan, 'stripPrefix' | 'urlPrefix' | 'generatedFor'>, file: string): string;
|
|
127
|
+
/** A copy-pasteable shell command for a plan (paths relative to `cwd` when inside it). */
|
|
128
|
+
declare function formatCommand(bin: string, args: string[], cwd?: string): string;
|
|
129
|
+
|
|
130
|
+
type ProjectKind = 'next' | 'expo' | 'react-native' | 'node';
|
|
131
|
+
type PackageManager = 'pnpm' | 'yarn' | 'npm' | 'bun';
|
|
132
|
+
interface ProjectInfo {
|
|
133
|
+
kind: ProjectKind;
|
|
134
|
+
dir: string;
|
|
135
|
+
/** Package name from `package.json`, for messages only. */
|
|
136
|
+
name: string | undefined;
|
|
137
|
+
typescript: boolean;
|
|
138
|
+
/** `"type": "module"` in package.json. */
|
|
139
|
+
esm: boolean;
|
|
140
|
+
packageManager: PackageManager;
|
|
141
|
+
/** All dependency names (dependencies + devDependencies). */
|
|
142
|
+
deps: Set<string>;
|
|
143
|
+
/** Next: `src` when the app lives in `src/`, else ``. Node: `src` when that directory exists. */
|
|
144
|
+
sourceDir: string;
|
|
145
|
+
/** Next only. */
|
|
146
|
+
router?: 'app' | 'pages' | undefined;
|
|
147
|
+
nextConfig?: string | undefined;
|
|
148
|
+
/** Next only: `[major, minor]` of the installed (else declared) version. */
|
|
149
|
+
nextVersion?: [number, number] | undefined;
|
|
150
|
+
/** Node only: `main` of package.json when it names a file. */
|
|
151
|
+
main?: string | undefined;
|
|
152
|
+
}
|
|
153
|
+
interface PackageJson {
|
|
154
|
+
name?: unknown;
|
|
155
|
+
type?: unknown;
|
|
156
|
+
main?: unknown;
|
|
157
|
+
packageManager?: unknown;
|
|
158
|
+
dependencies?: Record<string, string>;
|
|
159
|
+
devDependencies?: Record<string, string>;
|
|
160
|
+
}
|
|
161
|
+
/** Lockfile next to package.json, else in a parent (a workspace keeps one lockfile at its root). */
|
|
162
|
+
declare function detectPackageManager(dir: string, pkg?: PackageJson): PackageManager;
|
|
163
|
+
/** Reads `package.json` in `dir` (only there: in a monorepo, run it inside the app's package). */
|
|
164
|
+
declare function detectProject(dir: string): ProjectInfo;
|
|
165
|
+
interface InitAction {
|
|
166
|
+
/** `create`/`append`/`modify` write; `ok` is already in place; `manual` is printed for the user to do. */
|
|
167
|
+
type: 'create' | 'append' | 'modify' | 'ok' | 'manual';
|
|
168
|
+
/** Path relative to the project directory. */
|
|
169
|
+
file: string;
|
|
170
|
+
/** One line: what and why. */
|
|
171
|
+
summary: string;
|
|
172
|
+
/** create: the whole file. append: the text added at the end. modify: the whole new file. */
|
|
173
|
+
content?: string;
|
|
174
|
+
/** What is shown for the action: the new file, the appended lines, a diff or a snippet. */
|
|
175
|
+
preview?: string;
|
|
176
|
+
}
|
|
177
|
+
interface InitPlan {
|
|
178
|
+
project: ProjectInfo;
|
|
179
|
+
actions: InitAction[];
|
|
180
|
+
/** Missing packages, as the command for the project's package manager. */
|
|
181
|
+
install: string | undefined;
|
|
182
|
+
/** What is left for the user, in order. */
|
|
183
|
+
nextSteps: string[];
|
|
184
|
+
}
|
|
185
|
+
interface InitOptions {
|
|
186
|
+
/** Project directory. Default `process.cwd()`. */
|
|
187
|
+
dir?: string;
|
|
188
|
+
/** Written to a git-ignored local env file (`.env.local` for Next, `.env` for Node); never to a tracked file. */
|
|
189
|
+
dsn?: string;
|
|
190
|
+
}
|
|
191
|
+
/** Decides what to do. Reads the project, writes nothing. */
|
|
192
|
+
declare function planInit(options?: InitOptions): InitPlan;
|
|
193
|
+
/** Applies the writing actions of a plan. Refuses to replace a file that appeared since planning. */
|
|
194
|
+
declare function applyInit(plan: InitPlan): string[];
|
|
195
|
+
type RenderMode = 'applied' | 'dry-run' | 'preview';
|
|
196
|
+
/** The full report of a plan. */
|
|
197
|
+
declare function renderInit(plan: InitPlan, mode: RenderMode): string;
|
|
198
|
+
interface RunInitOptions extends InitOptions {
|
|
199
|
+
/** Write without asking. */
|
|
200
|
+
yes?: boolean;
|
|
201
|
+
/** Never write. Wins over `yes`. */
|
|
202
|
+
dryRun?: boolean;
|
|
203
|
+
/** Asked once when neither flag is given and there is something to write. Absent: do not ask, do not write. */
|
|
204
|
+
confirm?: (question: string) => Promise<boolean>;
|
|
205
|
+
out?: (text: string) => void;
|
|
206
|
+
}
|
|
207
|
+
/** Plan, show, and write when allowed. Returns the plan and the files written. */
|
|
208
|
+
declare function runInit(options?: RunInitOptions): Promise<{
|
|
209
|
+
plan: InitPlan;
|
|
210
|
+
written: string[];
|
|
211
|
+
}>;
|
|
212
|
+
|
|
213
|
+
type HttpFetch = (url: string, init: {
|
|
214
|
+
method: string;
|
|
215
|
+
headers: Record<string, string>;
|
|
216
|
+
body?: string | Uint8Array;
|
|
217
|
+
}) => Promise<{
|
|
218
|
+
status: number;
|
|
219
|
+
text(): Promise<string>;
|
|
220
|
+
}>;
|
|
221
|
+
interface HttpUploadOptions {
|
|
222
|
+
/** Server base URL, e.g. `https://argus.example.com`. */
|
|
223
|
+
url: string;
|
|
224
|
+
/** Upload key (kind `upload`). */
|
|
225
|
+
uploadKey: string;
|
|
226
|
+
dryRun?: boolean;
|
|
227
|
+
fetch?: HttpFetch;
|
|
228
|
+
log?: (line: string) => void;
|
|
229
|
+
}
|
|
230
|
+
interface ManifestFileEntry {
|
|
231
|
+
kind: 'sourcemap' | 'hermes_sourcemap' | 'js_bundle';
|
|
232
|
+
name: string;
|
|
233
|
+
bundle_name?: string;
|
|
234
|
+
sha256: string;
|
|
235
|
+
size: number;
|
|
236
|
+
}
|
|
237
|
+
interface Found {
|
|
238
|
+
path: string;
|
|
239
|
+
file: ManifestFileEntry;
|
|
240
|
+
}
|
|
241
|
+
/** True when every file of the plan can be uploaded without the CLI. */
|
|
242
|
+
declare function isJsOnlyPlan(plan: UploadPlan): boolean;
|
|
243
|
+
/** Builds the manifest for a JS-only plan. Exported for tests and `--dry-run`. */
|
|
244
|
+
declare function buildManifest(plan: UploadPlan): Promise<{
|
|
245
|
+
manifest: Record<string, unknown>;
|
|
246
|
+
found: Found[];
|
|
247
|
+
}>;
|
|
248
|
+
/**
|
|
249
|
+
* Creates the set, uploads the blobs the server does not have yet and finalizes. Idempotent:
|
|
250
|
+
* re-running with identical files uploads nothing. Throws unless the set ends up `ready`.
|
|
251
|
+
*/
|
|
252
|
+
declare function uploadPlanOverHttp(plan: UploadPlan, options: HttpUploadOptions): Promise<{
|
|
253
|
+
status: string;
|
|
254
|
+
uploaded: number;
|
|
255
|
+
files: number;
|
|
256
|
+
}>;
|
|
257
|
+
|
|
258
|
+
interface RunResult {
|
|
259
|
+
plan: UploadPlan;
|
|
260
|
+
command: string;
|
|
261
|
+
code: number;
|
|
262
|
+
}
|
|
263
|
+
interface RunOptions {
|
|
264
|
+
/** The `argus` binary. Default: `$ARGUS_BIN`, else `argus` on PATH. */
|
|
265
|
+
bin?: string;
|
|
266
|
+
/** Pass `--dry-run`: the CLI prints the manifest and uploads nothing. */
|
|
267
|
+
dryRun?: boolean;
|
|
268
|
+
/** Server base URL (`--url`). Default: the CLI's own `ARGUS_URL` handling. */
|
|
269
|
+
url?: string;
|
|
270
|
+
env?: Record<string, string | undefined>;
|
|
271
|
+
cwd?: string;
|
|
272
|
+
/**
|
|
273
|
+
* `http`: upload from Node (JS artifacts only). `cli`: run the `argus` binary. Default `auto`
|
|
274
|
+
* (env `ARGUS_UPLOAD_TRANSPORT`): HTTP for JS-only sets unless `ARGUS_BIN` names a binary,
|
|
275
|
+
* the CLI for sets with native symbols.
|
|
276
|
+
*/
|
|
277
|
+
transport?: 'auto' | 'http' | 'cli';
|
|
278
|
+
/** HTTP client for the `http` transport, replaceable in tests. */
|
|
279
|
+
fetch?: HttpFetch;
|
|
280
|
+
/** Process launcher, replaceable in tests. Resolves with the exit code. */
|
|
281
|
+
exec?: (bin: string, args: string[], opts: {
|
|
282
|
+
env: Record<string, string | undefined>;
|
|
283
|
+
cwd: string;
|
|
284
|
+
}) => Promise<number>;
|
|
285
|
+
log?: (line: string) => void;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Uploads the plans one after another and stops at the first failure (a build must not be
|
|
289
|
+
* promoted with a half-uploaded artifact set). Credentials come from the environment
|
|
290
|
+
* (`ARGUS_UPLOAD_KEY`); they are never placed on the command line.
|
|
291
|
+
*/
|
|
292
|
+
declare function runUploads(plans: UploadPlan[], options?: RunOptions): Promise<RunResult[]>;
|
|
293
|
+
|
|
294
|
+
declare const USAGE = "argus-build <command>\n\n init [--dsn <dsn>] [--yes] [--dry-run] [--dir <project>]\n set a Next.js / Node / Expo project up for Argus: prints every file it\n would write; writes with --yes (or after asking, on a terminal); never\n overwrites a file, never installs anything\n new-id [--prefix <p>] print a new artifact set id (same format as `argus artifacts new-id`)\n prelude --set-id <id> --out <file> write the generated module that defines globalThis.__ARGUS_ARTIFACT_SET_ID__\n collect <target> [options] print what would be uploaded, per artifact set (no network)\n upload <target> [options] upload each artifact set (JS sets over HTTP, native symbols via the argus CLI)\n\nTargets\n next --set-id <web id> [--server-set-id <id>] [--dir <project>] [--dist-dir .next] [--url-prefix /_next/]\n browser chunks and server chunks go to two different sets (default server id: <web id>-node)\n expo-export --ios-set-id <id> --android-set-id <id> [--dir dist] [--platform ios|android]\n [--update-id <id> | --ios-update-id <id> --android-update-id <id>]\n [--parent-set-id <id> | --ios-parent-set-id <id> --android-parent-set-id <id>] [--runtime-version <v>]\n ios --set-id <id> --dir <xcarchive | dSYMs | build dir>... [--bundle <main.jsbundle>] [--sourcemap <map>]\n android --set-id <id> --dir <android/app/build>... [--bundle <f>] [--sourcemap <map>] [--native-libs]\n files --set-id <id> --platform <ios|android|web|node> --dir <dir or file>... [--strip-prefix <p>] [--url-prefix <p>]\n\nCommon options\n --release <r> --dist <d> --native-release <v> --native-build <b> --runtime-version <v> --git-sha <sha>\n --no-bundles upload source maps only (next, files)\n --json collect: machine readable output\n --dry-run upload: print the manifests and upload nothing\n --print upload: only print the commands\n --transport <t> upload: http (from Node, JS artifacts only), cli (the argus binary) or auto (default:\n http for JS-only sets, cli for native symbols)\n --delete-maps upload next: delete browser .map files from the build output after a successful upload\n --argus-bin <path> the argus binary (default $ARGUS_BIN, else \"argus\")\n --url <base url> server (default: $ARGUS_URL as read by the argus CLI)\n\nEnvironment: ARGUS_UPLOAD_KEY (upload key; never passed on the command line), ARGUS_URL, ARGUS_BIN, ARGUS_GIT_SHA.\n";
|
|
295
|
+
interface ParsedArgs {
|
|
296
|
+
positionals: string[];
|
|
297
|
+
flags: Record<string, string | boolean | undefined>;
|
|
298
|
+
multi: Record<string, string[]>;
|
|
299
|
+
}
|
|
300
|
+
declare function parseArgs(argv: string[]): ParsedArgs;
|
|
301
|
+
/** Builds the upload plans for a target from CLI arguments. Exported for tests. */
|
|
302
|
+
declare function plansFor(target: string, args: ParsedArgs, env?: Record<string, string | undefined>, cwd?: string): UploadPlan[];
|
|
303
|
+
/** Human readable listing of the plans: what `collect` prints. */
|
|
304
|
+
declare function renderPlans(plans: UploadPlan[], bin: string, cwd: string): string;
|
|
305
|
+
interface CliIo {
|
|
306
|
+
out?: (text: string) => void;
|
|
307
|
+
err?: (text: string) => void;
|
|
308
|
+
env?: Record<string, string | undefined>;
|
|
309
|
+
cwd?: string;
|
|
310
|
+
exec?: RunOptions['exec'];
|
|
311
|
+
/** `init`: asks before writing. Default: a y/N prompt when stdin and stdout are terminals, else no prompt. */
|
|
312
|
+
confirm?: RunInitOptions['confirm'];
|
|
313
|
+
}
|
|
314
|
+
/** Runs the CLI. Returns the process exit code; never throws. */
|
|
315
|
+
declare function main(argv: string[], io?: CliIo): Promise<number>;
|
|
316
|
+
|
|
317
|
+
export { type ArtifactFileKind as A, runUploads as B, uploadArgs as C, uploadPlanOverHttp as D, type ExpoExportPlanOptions as E, type FilesPlanOptions as F, walkFiles as G, type HttpFetch as H, type InitAction as I, type CliIo as J, type ParsedArgs as K, USAGE as L, type MobilePlatform as M, type NativePlanOptions as N, parseArgs as O, type PackageManager as P, plansFor as Q, type RunOptions as R, renderPlans as S, type UploadMeta as U, type HttpUploadOptions as a, type InitOptions as b, type InitPlan as c, type NextPlanOptions as d, type ProjectInfo as e, type ProjectKind as f, type RunInitOptions as g, type RunResult as h, type UploadPlan as i, applyInit as j, browserSourceMaps as k, buildManifest as l, classifyArtifact as m, detectPackageManager as n, detectProject as o, formatCommand as p, isJsOnlyPlan as q, manifestName as r, planExpoExport as s, planFiles as t, planInit as u, planNative as v, planNext as w, renderInit as x, main as y, runInit as z };
|