@allstak/react 0.3.1 → 0.3.3
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/CHANGELOG.md +57 -0
- package/README.md +101 -162
- package/dist/build/chunk-G6VPGDP6.mjs +214 -0
- package/dist/build/chunk-G6VPGDP6.mjs.map +1 -0
- package/dist/build/chunk-ZY4H5AN4.mjs +32 -0
- package/dist/build/chunk-ZY4H5AN4.mjs.map +1 -0
- package/dist/build/next.d.mts +33 -0
- package/dist/build/next.d.ts +33 -0
- package/dist/build/next.js +274 -0
- package/dist/build/next.js.map +1 -0
- package/dist/build/next.mjs +26 -0
- package/dist/build/next.mjs.map +1 -0
- package/dist/build/sourcemaps.d.mts +160 -0
- package/dist/build/sourcemaps.d.ts +160 -0
- package/dist/build/sourcemaps.js +248 -0
- package/dist/build/sourcemaps.js.map +1 -0
- package/dist/build/sourcemaps.mjs +23 -0
- package/dist/build/sourcemaps.mjs.map +1 -0
- package/dist/build/vite.d.mts +65 -0
- package/dist/build/vite.d.ts +65 -0
- package/dist/build/vite.js +269 -0
- package/dist/build/vite.js.map +1 -0
- package/dist/build/vite.mjs +43 -0
- package/dist/build/vite.mjs.map +1 -0
- package/dist/build/webpack.d.mts +58 -0
- package/dist/build/webpack.d.ts +58 -0
- package/dist/build/webpack.js +256 -0
- package/dist/build/webpack.js.map +1 -0
- package/dist/build/webpack.mjs +8 -0
- package/dist/build/webpack.mjs.map +1 -0
- package/dist/index.d.mts +196 -42
- package/dist/index.d.ts +196 -42
- package/dist/index.js +435 -50
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +435 -50
- package/dist/index.mjs.map +1 -1
- package/package.json +25 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@allstak/react` are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.3.1] — 2026-05-11
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Version constant in `client.ts` now matches `package.json` (was `0.3.0`).
|
|
13
|
+
- Added version consistency test to CI.
|
|
14
|
+
|
|
15
|
+
## [0.3.0] — 2026-05-08
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- Full automatic HTTP instrumentation (fetch and XHR interception).
|
|
20
|
+
- Source map upload pipeline with Vite, Webpack, and Next.js build plugins.
|
|
21
|
+
- Per-frame `debugId` resolution via `globalThis._allstakDebugIds`.
|
|
22
|
+
- `debugMeta.images[]` aggregation in error payloads.
|
|
23
|
+
|
|
24
|
+
## [0.2.0] — 2026-04-25
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Scope management for tags, extras, and context.
|
|
29
|
+
- Distributed tracing with automatic `traceparent` propagation.
|
|
30
|
+
- Web session replay (surrogate events).
|
|
31
|
+
- React Router integration helpers.
|
|
32
|
+
- Debug-ID based source map resolution.
|
|
33
|
+
|
|
34
|
+
## [0.1.4] — 2026-04-10
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Standalone release on public npm as `@allstak/react`.
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- `ErrorBoundary` component with `componentDidCatch` capture.
|
|
43
|
+
- Automatic `window.onerror` and `onunhandledrejection` handlers.
|
|
44
|
+
- `beforeSend` callback, `sampleRate`, `setTags`/`setExtra`/`setContext`.
|
|
45
|
+
- `flush()` with bounded timeout.
|
|
46
|
+
- Web Vitals (LCP, FID, CLS) capture.
|
|
47
|
+
|
|
48
|
+
## [0.1.1] — 2026-03-28
|
|
49
|
+
|
|
50
|
+
Initial professional release.
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- Core error capture with stack trace parsing.
|
|
55
|
+
- Breadcrumb ring buffer (max 50).
|
|
56
|
+
- Fail-open transport with exponential backoff.
|
|
57
|
+
- Circuit breaker on 401 responses.
|
package/README.md
CHANGED
|
@@ -1,226 +1,165 @@
|
|
|
1
1
|
# @allstak/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AllStak React SDK for browser error capture, component error boundaries, breadcrumbs, user context, HTTP metadata, traces, Web Vitals, and source-map release support.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[](https://github.com/AllStak/allstak-react/actions)
|
|
7
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
Stability: stable SDK runtime, beta live certification. Live dashboard proof is not claimed until you run a project with real AllStak credentials.
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
## 1. Automatic Setup With Wizard
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
Recommended flow:
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
```bash
|
|
12
|
+
npx @allstak/wizard setup --integration react
|
|
13
|
+
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
The only value you may need to enter is the AllStak ingest API key. The wizard detects Vite React, CRA-style React, React Router, JavaScript, and TypeScript entry files. If the project is Next.js, use `@allstak/next` instead:
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
```bash
|
|
18
|
+
npx @allstak/wizard setup --integration next
|
|
19
|
+
```
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
- `useAllStak()` hook for in-component capture and context
|
|
21
|
-
- `withAllStakProfiler` HOC for mount/update timing
|
|
22
|
-
- Inherits automatic fetch, console, and window error capture from the core SDK
|
|
23
|
-
- Works with React 17, 18, and 19
|
|
24
|
-
- Full TypeScript types
|
|
21
|
+
## 2. What The Wizard Changes
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
The wizard:
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
- Installs `@allstak/react`.
|
|
26
|
+
- Writes managed env vars to `.env.local` for Vite or `.env` for non-Vite React.
|
|
27
|
+
- Detects `src/main.tsx`, `src/main.jsx`, `src/index.tsx`, or `src/index.jsx`.
|
|
28
|
+
- Wraps the existing render tree with `AllStakProvider`.
|
|
29
|
+
- Preserves `React.StrictMode`, React Router, and existing providers.
|
|
30
|
+
- Enables provider-managed component error boundary capture.
|
|
31
|
+
- Wires Vite source-map upload hooks when a Vite config is present.
|
|
32
|
+
- Prints the changed files.
|
|
33
|
+
- Supports dry-run, repair, idempotent re-runs, and uninstall.
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
- **Logs** — console warnings and errors as structured breadcrumbs
|
|
32
|
-
- **HTTP** — outbound `fetch` timing, status codes, failed calls
|
|
33
|
-
- **Performance** — mount and update timing from the profiler HOC
|
|
34
|
-
- **Alerts** — email and webhook notifications on regressions
|
|
35
|
+
## 3. Verification
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
After setup:
|
|
37
38
|
|
|
38
39
|
```bash
|
|
39
|
-
npm
|
|
40
|
+
npm run build
|
|
41
|
+
npx @allstak/wizard doctor --integration react
|
|
40
42
|
```
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
> Create a project at [app.allstak.sa](https://app.allstak.sa) to get your API key.
|
|
44
|
+
With live credentials, send a test event from your app:
|
|
45
45
|
|
|
46
|
-
```
|
|
47
|
-
import { AllStak
|
|
46
|
+
```ts
|
|
47
|
+
import { AllStak } from '@allstak/react';
|
|
48
48
|
|
|
49
|
-
AllStak.
|
|
50
|
-
|
|
51
|
-
environment: 'production',
|
|
49
|
+
AllStak.captureMessage('AllStak React setup verification', {
|
|
50
|
+
source: 'manual-verification',
|
|
52
51
|
});
|
|
53
|
-
|
|
54
|
-
AllStak.captureException(new Error('test: hello from allstak-react'));
|
|
55
|
-
|
|
56
|
-
export function App() {
|
|
57
|
-
return (
|
|
58
|
-
<AllStakErrorBoundary fallback={<p>Something went wrong.</p>}>
|
|
59
|
-
<Routes />
|
|
60
|
-
</AllStakErrorBoundary>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
52
|
```
|
|
64
53
|
|
|
65
|
-
|
|
54
|
+
Dashboard appearance is not guaranteed by local tests. Confirm the event in AllStak before claiming live certification.
|
|
66
55
|
|
|
67
|
-
##
|
|
56
|
+
## 4. Rollback / Uninstall
|
|
68
57
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
4. Export it as `ALLSTAK_API_KEY` or pass it to `AllStak.init(...)`
|
|
58
|
+
```bash
|
|
59
|
+
npx @allstak/wizard uninstall --integration react
|
|
60
|
+
```
|
|
73
61
|
|
|
74
|
-
|
|
62
|
+
Uninstall removes wizard-managed env blocks, source-map config, imports, and provider wrappers. User-owned code outside wizard markers is preserved.
|
|
75
63
|
|
|
76
|
-
|
|
77
|
-
|---|---|---|---|---|
|
|
78
|
-
| `apiKey` | `string` | yes | — | Project API key (`ask_live_…`) |
|
|
79
|
-
| `environment` | `string` | no | — | Deployment env |
|
|
80
|
-
| `release` | `string` | no | — | Version or git SHA |
|
|
81
|
-
| `host` | `string` | no | `https://api.allstak.sa` | Ingest host override |
|
|
82
|
-
| `user` | `{ id?, email? }` | no | — | Default user context |
|
|
83
|
-
| `tags` | `Record<string,string>` | no | — | Default tags |
|
|
64
|
+
## 5. Manual Setup Fallback
|
|
84
65
|
|
|
85
|
-
|
|
66
|
+
Use manual setup only when the wizard cannot safely patch a custom entry file:
|
|
86
67
|
|
|
87
|
-
|
|
68
|
+
```bash
|
|
69
|
+
npm install @allstak/react
|
|
70
|
+
```
|
|
88
71
|
|
|
89
72
|
```tsx
|
|
90
|
-
import {
|
|
73
|
+
import { AllStakProvider } from '@allstak/react';
|
|
91
74
|
|
|
92
|
-
function
|
|
93
|
-
const allstak = useAllStak();
|
|
75
|
+
export function Root({ children }: { children: React.ReactNode }) {
|
|
94
76
|
return (
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
77
|
+
<AllStakProvider
|
|
78
|
+
apiKey={import.meta.env.VITE_ALLSTAK_API_KEY}
|
|
79
|
+
host={import.meta.env.VITE_ALLSTAK_HOST}
|
|
80
|
+
environment={import.meta.env.VITE_ALLSTAK_ENVIRONMENT ?? 'production'}
|
|
81
|
+
release={import.meta.env.VITE_ALLSTAK_RELEASE}
|
|
82
|
+
enableHttpTracking
|
|
83
|
+
>
|
|
84
|
+
{children}
|
|
85
|
+
</AllStakProvider>
|
|
99
86
|
);
|
|
100
87
|
}
|
|
101
88
|
```
|
|
102
89
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
```tsx
|
|
106
|
-
import { withAllStakProfiler } from '@allstak/react';
|
|
107
|
-
export default withAllStakProfiler(Dashboard, { name: 'Dashboard' });
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Set user context on login:
|
|
111
|
-
|
|
112
|
-
```tsx
|
|
113
|
-
import { AllStak } from '@allstak/react';
|
|
114
|
-
AllStak.setUser({ id: user.id, email: user.email });
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## HTTP tracking
|
|
118
|
-
|
|
119
|
-
Setting `enableHttpTracking: true` (off by default) auto-wraps `fetch`,
|
|
120
|
-
`XMLHttpRequest`, and `axios` (when installed) so every outbound HTTP
|
|
121
|
-
call is recorded as an `http_request` event.
|
|
122
|
-
|
|
123
|
-
**Privacy defaults are aggressive:**
|
|
124
|
-
|
|
125
|
-
- request/response bodies are **not** captured
|
|
126
|
-
- headers are **not** captured
|
|
127
|
-
- `Authorization`, `Cookie`, `Set-Cookie`, `X-API-Key`, `X-Auth-Token`,
|
|
128
|
-
`Proxy-Authorization` are **always** redacted
|
|
129
|
-
- query params named `token`, `password`, `api_key`, `apikey`,
|
|
130
|
-
`authorization`, `auth`, `secret`, `access_token`, `refresh_token`,
|
|
131
|
-
`session`, `sessionid`, `jwt` are **always** redacted in the URL
|
|
132
|
-
|
|
133
|
-
To enable richer capture (only on routes you control):
|
|
90
|
+
For manual capture:
|
|
134
91
|
|
|
135
92
|
```ts
|
|
136
|
-
AllStak
|
|
137
|
-
apiKey: '...',
|
|
138
|
-
enableHttpTracking: true,
|
|
139
|
-
httpTracking: {
|
|
140
|
-
captureRequestBody: true,
|
|
141
|
-
captureResponseBody: true,
|
|
142
|
-
captureHeaders: true, // auth headers still hard-redacted
|
|
143
|
-
redactHeaders: ['x-tenant'],
|
|
144
|
-
redactQueryParams: ['custom_id'],
|
|
145
|
-
ignoredUrls: [/health/i, '/metrics'],
|
|
146
|
-
allowedUrls: [],
|
|
147
|
-
maxBodyBytes: 4096,
|
|
148
|
-
},
|
|
149
|
-
});
|
|
93
|
+
import { AllStak } from '@allstak/react';
|
|
150
94
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
95
|
+
AllStak.captureException(new Error('checkout failed'));
|
|
96
|
+
AllStak.captureMessage('user opened checkout');
|
|
97
|
+
AllStak.setUser({ id: 'user_123' });
|
|
98
|
+
AllStak.addBreadcrumb({ type: 'navigation', message: 'Checkout' });
|
|
154
99
|
```
|
|
155
100
|
|
|
156
|
-
|
|
157
|
-
HTTP requests (last 10) are automatically attached to the error
|
|
158
|
-
metadata under `http.recentFailed` for easy triage.
|
|
101
|
+
## 6. Configuration
|
|
159
102
|
|
|
160
|
-
|
|
103
|
+
Provider props include:
|
|
161
104
|
|
|
162
|
-
|
|
105
|
+
| Option | Default | Notes |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `apiKey` | required | Public browser ingest key. |
|
|
108
|
+
| `host` | `https://api.allstak.sa` | Override for self-hosted ingest. |
|
|
109
|
+
| `environment` | `production` | Release environment tag. |
|
|
110
|
+
| `release` | unset | Use app version or commit SHA. |
|
|
111
|
+
| `debug` | `false` | Enables SDK diagnostic logs. |
|
|
112
|
+
| `enableHttpTracking` | `false` | Captures HTTP metadata with redaction. |
|
|
113
|
+
| `autoCaptureBrowserErrors` | `true` | Captures `window.onerror` and `unhandledrejection`. |
|
|
114
|
+
| `autoBreadcrumbsFetch` | `true` | Adds fetch/XHR breadcrumbs. |
|
|
115
|
+
| `autoBreadcrumbsConsole` | `true` | Captures `warn`/`error`; `log`/`info` stay off by default. |
|
|
116
|
+
| `beforeSend` | unset | Last-chance event scrub/drop hook. |
|
|
163
117
|
|
|
164
|
-
|
|
165
|
-
AllStak.init({ apiKey: '...', host: 'https://allstak.mycorp.com' });
|
|
166
|
-
```
|
|
118
|
+
## 7. Privacy / PII / Redaction
|
|
167
119
|
|
|
168
|
-
|
|
120
|
+
Privacy defaults are conservative:
|
|
169
121
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
122
|
+
- Authorization, cookie, API key, token, and secret headers are always redacted.
|
|
123
|
+
- Sensitive query parameters are redacted.
|
|
124
|
+
- Request/response bodies are disabled unless explicitly enabled.
|
|
125
|
+
- Console `log` and `info` breadcrumbs are off by default.
|
|
126
|
+
- Use `beforeSend` for app-specific PII removal.
|
|
175
127
|
|
|
176
|
-
|
|
128
|
+
Do not send passwords, payment data, national IDs, raw tokens, or raw request/response bodies unless you have verified redaction in your app.
|
|
177
129
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
130
|
+
## 8. Source Maps / Releases
|
|
131
|
+
|
|
132
|
+
For Vite, the wizard wires the AllStak source-map plugin automatically when possible. Manual fallback:
|
|
181
133
|
|
|
182
134
|
```ts
|
|
183
|
-
// vite.config.ts
|
|
184
135
|
import { defineConfig } from 'vite';
|
|
185
|
-
import
|
|
136
|
+
import react from '@vitejs/plugin-react';
|
|
137
|
+
import { allstakSourcemaps } from '@allstak/react/vite';
|
|
186
138
|
|
|
187
139
|
export default defineConfig({
|
|
188
|
-
build: { sourcemap: 'hidden' },
|
|
189
140
|
plugins: [
|
|
141
|
+
react(),
|
|
190
142
|
allstakSourcemaps({
|
|
191
|
-
release: process.env.
|
|
192
|
-
token: process.env.
|
|
143
|
+
release: process.env.VITE_ALLSTAK_RELEASE,
|
|
144
|
+
token: process.env.ALLSTAK_SOURCEMAP_TOKEN,
|
|
193
145
|
}),
|
|
194
146
|
],
|
|
195
147
|
});
|
|
196
148
|
```
|
|
197
149
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
The plugin exists for `@allstak/js/webpack` and `@allstak/js/next`; for
|
|
201
|
-
any other bundler call the underlying API after your build:
|
|
202
|
-
|
|
203
|
-
```ts
|
|
204
|
-
import { processBuildOutput } from '@allstak/js/sourcemaps';
|
|
205
|
-
|
|
206
|
-
await processBuildOutput({
|
|
207
|
-
dir: 'dist',
|
|
208
|
-
release: process.env.RELEASE!,
|
|
209
|
-
token: process.env.ALLSTAK_UPLOAD_TOKEN!,
|
|
210
|
-
});
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
This injects a stable `debugId` into every chunk and uploads the matching
|
|
214
|
-
`.map`. The runtime resolver in `@allstak/react` reads `globalThis._allstakDebugIds`
|
|
215
|
-
to attach the right debug-id to each captured frame, so the symbolicator
|
|
216
|
-
picks the correct map even after long-tail caching of bundles.
|
|
150
|
+
Use the same `release` value in the provider and source-map upload.
|
|
217
151
|
|
|
218
|
-
##
|
|
152
|
+
## 9. Troubleshooting
|
|
219
153
|
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
-
|
|
154
|
+
- No events: verify `VITE_ALLSTAK_API_KEY` exists and the provider is present once.
|
|
155
|
+
- Duplicate events: rerun `npx @allstak/wizard doctor --integration react` and check for multiple providers.
|
|
156
|
+
- Next.js detected: use `@allstak/next`.
|
|
157
|
+
- Build fails after setup: run `npx @allstak/wizard uninstall --integration react`, then rerun with `--dry-run` and inspect the planned diff.
|
|
158
|
+
- Source maps missing: confirm `ALLSTAK_SOURCEMAP_TOKEN` and matching `release`.
|
|
223
159
|
|
|
224
|
-
##
|
|
160
|
+
## 10. Limitations
|
|
225
161
|
|
|
226
|
-
|
|
162
|
+
- Live dashboard delivery is not proven by local tests.
|
|
163
|
+
- Session replay is privacy-first and limited compared with full DOM replay tools.
|
|
164
|
+
- Edge runtimes should use framework-specific packages.
|
|
165
|
+
- Stable production launch requires live certification against your dashboard.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// src/build/walk.ts
|
|
2
|
+
import { readdirSync, statSync } from "fs";
|
|
3
|
+
import { basename, join } from "path";
|
|
4
|
+
function walk(dir, out = []) {
|
|
5
|
+
for (const name of readdirSync(dir)) {
|
|
6
|
+
const full = join(dir, name);
|
|
7
|
+
const st = statSync(full);
|
|
8
|
+
if (st.isDirectory()) walk(full, out);
|
|
9
|
+
else out.push(full);
|
|
10
|
+
}
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
function findPairs(root) {
|
|
14
|
+
const all = walk(root);
|
|
15
|
+
const maps = new Set(all.filter((p) => p.endsWith(".map")));
|
|
16
|
+
const pairs = [];
|
|
17
|
+
for (const js of all) {
|
|
18
|
+
if (!js.endsWith(".js") && !js.endsWith(".mjs") && !js.endsWith(".cjs")) continue;
|
|
19
|
+
const map = js + ".map";
|
|
20
|
+
if (maps.has(map)) {
|
|
21
|
+
pairs.push({ jsPath: js, mapPath: map, bundleName: basename(js) });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return pairs;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/build/inject.ts
|
|
28
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
29
|
+
import { randomUUID } from "crypto";
|
|
30
|
+
var DEBUG_ID_LINE_RE = /^\/\/# debugId=([0-9a-f-]{36})\s*$/m;
|
|
31
|
+
var REGISTRATION_MARKER = "/*!__allstak_debug_id_registration__*/";
|
|
32
|
+
function buildRegistrationSnippet(jsBody, debugId) {
|
|
33
|
+
const isEsm = /\bimport\.meta\b/.test(jsBody) || /^\s*(?:import|export)\b/m.test(jsBody);
|
|
34
|
+
if (isEsm) {
|
|
35
|
+
return `${REGISTRATION_MARKER}try{(globalThis._allstakDebugIds=globalThis._allstakDebugIds||{})[import.meta.url]="${debugId}"}catch(_){}`;
|
|
36
|
+
}
|
|
37
|
+
return `${REGISTRATION_MARKER}(function(){try{var u=(typeof document!=="undefined"&&document.currentScript&&document.currentScript.src)||(typeof location!=="undefined"?location.href:"");(globalThis._allstakDebugIds=globalThis._allstakDebugIds||{})[u]="${debugId}"}catch(_){}})();`;
|
|
38
|
+
}
|
|
39
|
+
function stripRegistration(js) {
|
|
40
|
+
const lineRe = new RegExp(
|
|
41
|
+
"^" + REGISTRATION_MARKER.replace(/[/*!]/g, (c) => "\\" + c) + ".*$",
|
|
42
|
+
"m"
|
|
43
|
+
);
|
|
44
|
+
return js.replace(lineRe, "");
|
|
45
|
+
}
|
|
46
|
+
function injectPair(p) {
|
|
47
|
+
const jsRaw = readFileSync(p.jsPath, "utf8");
|
|
48
|
+
const mapRaw = readFileSync(p.mapPath, "utf8");
|
|
49
|
+
const map = JSON.parse(mapRaw);
|
|
50
|
+
let debugId = typeof map.debugId === "string" ? map.debugId : "";
|
|
51
|
+
const existing = DEBUG_ID_LINE_RE.exec(jsRaw);
|
|
52
|
+
if (existing && existing[1]) debugId = debugId || existing[1];
|
|
53
|
+
const reused = !!debugId;
|
|
54
|
+
if (!debugId) debugId = randomUUID();
|
|
55
|
+
map.debugId = debugId;
|
|
56
|
+
writeFileSync(p.mapPath, JSON.stringify(map));
|
|
57
|
+
let jsOut = stripRegistration(jsRaw.replace(DEBUG_ID_LINE_RE, ""));
|
|
58
|
+
jsOut = jsOut.replace(/\s+$/, "");
|
|
59
|
+
jsOut += `
|
|
60
|
+
${buildRegistrationSnippet(jsOut, debugId)}
|
|
61
|
+
//# debugId=${debugId}
|
|
62
|
+
`;
|
|
63
|
+
writeFileSync(p.jsPath, jsOut);
|
|
64
|
+
return { debugId, reused };
|
|
65
|
+
}
|
|
66
|
+
function injectAll(pairs) {
|
|
67
|
+
return pairs.map((pair) => ({ pair, result: injectPair(pair) }));
|
|
68
|
+
}
|
|
69
|
+
function readDebugIdFromMap(mapPath) {
|
|
70
|
+
const json = JSON.parse(readFileSync(mapPath, "utf8"));
|
|
71
|
+
return typeof json.debugId === "string" ? json.debugId : null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/build/upload.ts
|
|
75
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
76
|
+
import { basename as basename2 } from "path";
|
|
77
|
+
var DEFAULT_HOST = "https://api.allstak.sa";
|
|
78
|
+
async function uploadOne(type, filePath, debugId, opts) {
|
|
79
|
+
let buf = readFileSync2(filePath);
|
|
80
|
+
if (type === "sourcemap" && opts.stripSources) {
|
|
81
|
+
const json = JSON.parse(buf.toString("utf8"));
|
|
82
|
+
if (Array.isArray(json.sourcesContent)) delete json.sourcesContent;
|
|
83
|
+
buf = Buffer.from(JSON.stringify(json));
|
|
84
|
+
}
|
|
85
|
+
const form = new FormData();
|
|
86
|
+
form.append("debugId", debugId);
|
|
87
|
+
form.append("type", type);
|
|
88
|
+
form.append("release", opts.release);
|
|
89
|
+
if (opts.dist) form.append("dist", opts.dist);
|
|
90
|
+
form.append(
|
|
91
|
+
"file",
|
|
92
|
+
new Blob([buf], {
|
|
93
|
+
type: type === "sourcemap" ? "application/json" : "application/javascript"
|
|
94
|
+
}),
|
|
95
|
+
basename2(filePath)
|
|
96
|
+
);
|
|
97
|
+
const res = await fetch(opts.host.replace(/\/$/, "") + "/api/v1/artifacts/upload", {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: { "X-AllStak-Upload-Token": opts.token },
|
|
100
|
+
body: form
|
|
101
|
+
});
|
|
102
|
+
return { status: res.status, body: await res.text(), ok: res.ok };
|
|
103
|
+
}
|
|
104
|
+
function sha8(buf) {
|
|
105
|
+
let hash = 0;
|
|
106
|
+
for (let i = 0; i < buf.length; i++) hash = (hash << 5) - hash + buf[i] | 0;
|
|
107
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
108
|
+
}
|
|
109
|
+
async function uploadPair(p, opts) {
|
|
110
|
+
const debugId = readDebugIdFromMap(p.mapPath);
|
|
111
|
+
if (!debugId) {
|
|
112
|
+
return {
|
|
113
|
+
bundleName: p.bundleName,
|
|
114
|
+
debugId: "",
|
|
115
|
+
ok: false,
|
|
116
|
+
steps: [{
|
|
117
|
+
type: "sourcemap",
|
|
118
|
+
status: 0,
|
|
119
|
+
sha8: "",
|
|
120
|
+
body: `[allstak/upload] no debugId in ${p.mapPath} \u2014 run inject first`
|
|
121
|
+
}]
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const merged = {
|
|
125
|
+
release: opts.release,
|
|
126
|
+
host: opts.host ?? process.env.ALLSTAK_HOST ?? DEFAULT_HOST,
|
|
127
|
+
token: opts.token,
|
|
128
|
+
dist: opts.dist,
|
|
129
|
+
stripSources: opts.stripSources
|
|
130
|
+
};
|
|
131
|
+
const steps = [];
|
|
132
|
+
const mapResult = await uploadOne("sourcemap", p.mapPath, debugId, merged);
|
|
133
|
+
steps.push({
|
|
134
|
+
type: "sourcemap",
|
|
135
|
+
status: mapResult.status,
|
|
136
|
+
sha8: sha8(readFileSync2(p.mapPath)),
|
|
137
|
+
body: mapResult.ok ? void 0 : mapResult.body
|
|
138
|
+
});
|
|
139
|
+
let allOk = mapResult.ok;
|
|
140
|
+
if (opts.uploadBundles) {
|
|
141
|
+
const bundleResult = await uploadOne("bundle", p.jsPath, debugId, merged);
|
|
142
|
+
steps.push({
|
|
143
|
+
type: "bundle",
|
|
144
|
+
status: bundleResult.status,
|
|
145
|
+
sha8: sha8(readFileSync2(p.jsPath)),
|
|
146
|
+
body: bundleResult.ok ? void 0 : bundleResult.body
|
|
147
|
+
});
|
|
148
|
+
allOk = allOk && bundleResult.ok;
|
|
149
|
+
}
|
|
150
|
+
return { bundleName: p.bundleName, debugId, ok: allOk, steps };
|
|
151
|
+
}
|
|
152
|
+
async function uploadAll(pairs, opts) {
|
|
153
|
+
return Promise.all(pairs.map((p) => uploadPair(p, opts)));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/build/sourcemaps.ts
|
|
157
|
+
import { resolve } from "path";
|
|
158
|
+
async function processBuildOutput(opts) {
|
|
159
|
+
const dir = resolve(opts.dir);
|
|
160
|
+
const pairs = findPairs(dir);
|
|
161
|
+
const log = opts.silent ? () => void 0 : (m) => console.log(`[allstak/sourcemaps] ${m}`);
|
|
162
|
+
log(`scanning ${dir} \u2014 ${pairs.length} bundle/map pair(s)`);
|
|
163
|
+
if (pairs.length === 0) {
|
|
164
|
+
return { dir, pairs: 0, injected: [] };
|
|
165
|
+
}
|
|
166
|
+
const injectedRaw = injectAll(pairs);
|
|
167
|
+
const injected = injectedRaw.map(({ pair, result }) => ({
|
|
168
|
+
bundleName: pair.bundleName,
|
|
169
|
+
debugId: result.debugId,
|
|
170
|
+
reused: result.reused
|
|
171
|
+
}));
|
|
172
|
+
for (const i of injected) {
|
|
173
|
+
log(` ${i.bundleName} ${i.debugId} ${i.reused ? "(reused)" : "(new)"}`);
|
|
174
|
+
}
|
|
175
|
+
const token = opts.token ?? process.env.ALLSTAK_UPLOAD_TOKEN;
|
|
176
|
+
const release = opts.release ?? process.env.ALLSTAK_RELEASE;
|
|
177
|
+
if (opts.injectOnly || !token) {
|
|
178
|
+
if (!opts.injectOnly && !token) {
|
|
179
|
+
log("skipping upload \u2014 no token (set ALLSTAK_UPLOAD_TOKEN or pass `token`)");
|
|
180
|
+
}
|
|
181
|
+
return { dir, pairs: pairs.length, injected };
|
|
182
|
+
}
|
|
183
|
+
if (!release) {
|
|
184
|
+
log("skipping upload \u2014 no release (set ALLSTAK_RELEASE or pass `release`)");
|
|
185
|
+
return { dir, pairs: pairs.length, injected };
|
|
186
|
+
}
|
|
187
|
+
const uploaded = await uploadAll(pairs, {
|
|
188
|
+
...opts,
|
|
189
|
+
release,
|
|
190
|
+
token
|
|
191
|
+
});
|
|
192
|
+
for (const u of uploaded) {
|
|
193
|
+
if (u.ok) {
|
|
194
|
+
log(` ${u.bundleName} uploaded debugId=${u.debugId}`);
|
|
195
|
+
} else {
|
|
196
|
+
const last = u.steps[u.steps.length - 1];
|
|
197
|
+
log(` ${u.bundleName} FAIL status=${last?.status ?? "?"} body=${last?.body ?? ""}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { dir, pairs: pairs.length, injected, uploaded };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export {
|
|
204
|
+
walk,
|
|
205
|
+
findPairs,
|
|
206
|
+
injectPair,
|
|
207
|
+
injectAll,
|
|
208
|
+
readDebugIdFromMap,
|
|
209
|
+
DEFAULT_HOST,
|
|
210
|
+
uploadPair,
|
|
211
|
+
uploadAll,
|
|
212
|
+
processBuildOutput
|
|
213
|
+
};
|
|
214
|
+
//# sourceMappingURL=chunk-G6VPGDP6.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/build/walk.ts","../../src/build/inject.ts","../../src/build/upload.ts","../../src/build/sourcemaps.ts"],"sourcesContent":["/**\n * Build-time only. File-system walking for the source-map pipeline.\n * Pure Node 18+ (built-in `node:fs` only). Browser runtime never imports\n * this — it's behind a `./build/*` subpath that's marked Node-platform.\n */\n\nimport { readdirSync, statSync } from 'node:fs';\nimport { basename, join } from 'node:path';\n\n/** A bundle and its companion source map on disk. */\nexport interface BundlePair {\n /** Absolute path to the JS bundle (`.js` / `.mjs` / `.cjs`). */\n jsPath: string;\n /** Absolute path to the matching `.map` file. */\n mapPath: string;\n /** Bare filename of the bundle (no directory), for log lines. */\n bundleName: string;\n}\n\n/** Recursively list every file under `dir`. Symlinks are followed. */\nexport function walk(dir: string, out: string[] = []): string[] {\n for (const name of readdirSync(dir)) {\n const full = join(dir, name);\n const st = statSync(full);\n if (st.isDirectory()) walk(full, out);\n else out.push(full);\n }\n return out;\n}\n\n/**\n * Returns every `(bundle, sourcemap)` pair under `root`.\n *\n * A pair is a `.js` / `.mjs` / `.cjs` file with a sibling file of the\n * same name plus a `.map` suffix — the convention every modern bundler\n * (Vite, Webpack, esbuild, Rollup, tsup) follows.\n */\nexport function findPairs(root: string): BundlePair[] {\n const all = walk(root);\n const maps = new Set(all.filter((p) => p.endsWith('.map')));\n const pairs: BundlePair[] = [];\n for (const js of all) {\n if (!js.endsWith('.js') && !js.endsWith('.mjs') && !js.endsWith('.cjs')) continue;\n const map = js + '.map';\n if (maps.has(map)) {\n pairs.push({ jsPath: js, mapPath: map, bundleName: basename(js) });\n }\n }\n return pairs;\n}\n","/**\n * Debug-ID injection. Build-time only.\n *\n * For each `(bundle.js, bundle.js.map)` pair we:\n * - Generate a stable per-bundle UUID (one already on the bundle is\n * reused so re-running is idempotent).\n * - Append `//# debugId=<uuid>` to the JS so the runtime resolver in\n * `src/debug-id.ts` can read it back.\n * - Write a top-level `debugId` field into the `.map` JSON so the\n * symbolicator on the backend can join `bundle.js` ↔ `bundle.js.map`\n * by ID rather than guessing from filenames.\n *\n * Bundlers re-write hashed filenames on every build, so joining by ID\n * (instead of by URL or path) is what makes resolved stack frames\n * survive across releases.\n */\n\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { randomUUID } from 'node:crypto';\n\nimport type { BundlePair } from './walk';\n\nconst DEBUG_ID_LINE_RE = /^\\/\\/# debugId=([0-9a-f-]{36})\\s*$/m;\nconst REGISTRATION_MARKER = '/*!__allstak_debug_id_registration__*/';\n\nfunction buildRegistrationSnippet(jsBody: string, debugId: string): string {\n const isEsm = /\\bimport\\.meta\\b/.test(jsBody) || /^\\s*(?:import|export)\\b/m.test(jsBody);\n if (isEsm) {\n return `${REGISTRATION_MARKER}try{(globalThis._allstakDebugIds=globalThis._allstakDebugIds||{})[import.meta.url]=\"${debugId}\"}catch(_){}`;\n }\n return `${REGISTRATION_MARKER}(function(){try{var u=(typeof document!==\"undefined\"&&document.currentScript&&document.currentScript.src)||(typeof location!==\"undefined\"?location.href:\"\");(globalThis._allstakDebugIds=globalThis._allstakDebugIds||{})[u]=\"${debugId}\"}catch(_){}})();`;\n}\n\nfunction stripRegistration(js: string): string {\n const lineRe = new RegExp(\n '^' + REGISTRATION_MARKER.replace(/[/*!]/g, (c) => '\\\\' + c) + '.*$',\n 'm',\n );\n return js.replace(lineRe, '');\n}\n\n/** Outcome of injecting one pair. */\nexport interface InjectResult {\n /** UUID injected (or reused) for this bundle. */\n debugId: string;\n /** True if the bundle already had a debugId — we reused it. */\n reused: boolean;\n}\n\n/**\n * Inject (or reuse) the debug ID for a single bundle/sourcemap pair.\n * Mutates both files on disk. Pure synchronous Node — safe to call from\n * a Vite `closeBundle` or Webpack `afterEmit` hook.\n */\nexport function injectPair(p: BundlePair): InjectResult {\n const jsRaw = readFileSync(p.jsPath, 'utf8');\n const mapRaw = readFileSync(p.mapPath, 'utf8');\n const map = JSON.parse(mapRaw) as { debugId?: unknown; [k: string]: unknown };\n\n let debugId = typeof map.debugId === 'string' ? map.debugId : '';\n const existing = DEBUG_ID_LINE_RE.exec(jsRaw);\n if (existing && existing[1]) debugId = debugId || existing[1];\n const reused = !!debugId;\n if (!debugId) debugId = randomUUID();\n\n map.debugId = debugId;\n writeFileSync(p.mapPath, JSON.stringify(map));\n\n let jsOut = stripRegistration(jsRaw.replace(DEBUG_ID_LINE_RE, ''));\n jsOut = jsOut.replace(/\\s+$/, '');\n jsOut += `\\n${buildRegistrationSnippet(jsOut, debugId)}\\n//# debugId=${debugId}\\n`;\n writeFileSync(p.jsPath, jsOut);\n\n return { debugId, reused };\n}\n\n/** Inject every pair under `root`. Returns one record per pair. */\nexport function injectAll(pairs: BundlePair[]): Array<{ pair: BundlePair; result: InjectResult }> {\n return pairs.map((pair) => ({ pair, result: injectPair(pair) }));\n}\n\n/** Read a debug ID back from a `.map` file. */\nexport function readDebugIdFromMap(mapPath: string): string | null {\n const json = JSON.parse(readFileSync(mapPath, 'utf8')) as { debugId?: unknown };\n return typeof json.debugId === 'string' ? json.debugId : null;\n}\n","/**\n * Source-map / bundle upload client. Build-time only.\n *\n * Wraps the AllStak `/api/v1/artifacts/upload` endpoint with multipart\n * form data and best-effort retries. Pure Node 18+ (uses the global\n * `fetch` and `FormData`), no third-party HTTP client required.\n *\n * Auth: `X-AllStak-Upload-Token` header (NOT the runtime API key —\n * uploads have a dedicated, narrower scope. Generate one in the\n * dashboard → Project Settings → Upload Tokens).\n */\n\nimport { readFileSync } from 'node:fs';\nimport { basename } from 'node:path';\n\nimport type { BundlePair } from './walk';\nimport { readDebugIdFromMap } from './inject';\n\n/** Default ingest host — overridden via `host` option or `ALLSTAK_HOST`. */\nexport const DEFAULT_HOST = 'https://api.allstak.sa';\n\n/** Options for {@link uploadAll} / {@link uploadPair}. */\nexport interface UploadOptions {\n /** Release identifier, e.g. `myapp@1.4.2`. Required server-side. */\n release: string;\n /** Optional distribution tag (`web`, `ios-hermes`, `staging`, …). */\n dist?: string;\n /** AllStak ingest host (default `https://api.allstak.sa`). */\n host?: string;\n /** Project upload token (`aspk_…`). May come from `ALLSTAK_UPLOAD_TOKEN`. */\n token: string;\n /**\n * Drop `sourcesContent` from the map before upload (smaller payload).\n * Off by default — sourcesContent enables full-source rendering on the\n * dashboard. Turn on if you don't want source code uploaded.\n */\n stripSources?: boolean;\n /** Also upload the JS bundle alongside the map (off by default). */\n uploadBundles?: boolean;\n}\n\n/** One artifact upload result. */\nexport interface UploadResult {\n bundleName: string;\n debugId: string;\n /** True when both the map (and bundle, if requested) uploaded OK. */\n ok: boolean;\n /** Per-artifact responses, in the order we sent them. */\n steps: Array<{\n type: 'sourcemap' | 'bundle';\n status: number;\n sha8: string;\n body?: string;\n }>;\n}\n\ninterface OneStepResult {\n status: number;\n body: string;\n ok: boolean;\n}\n\nasync function uploadOne(\n type: 'sourcemap' | 'bundle',\n filePath: string,\n debugId: string,\n opts: Required<Pick<UploadOptions, 'release' | 'host' | 'token'>> &\n Pick<UploadOptions, 'dist' | 'stripSources'>,\n): Promise<OneStepResult> {\n let buf = readFileSync(filePath);\n if (type === 'sourcemap' && opts.stripSources) {\n const json = JSON.parse(buf.toString('utf8')) as { sourcesContent?: unknown };\n if (Array.isArray(json.sourcesContent)) delete json.sourcesContent;\n buf = Buffer.from(JSON.stringify(json));\n }\n\n const form = new FormData();\n form.append('debugId', debugId);\n form.append('type', type);\n form.append('release', opts.release);\n if (opts.dist) form.append('dist', opts.dist);\n form.append(\n 'file',\n new Blob([buf], {\n type: type === 'sourcemap' ? 'application/json' : 'application/javascript',\n }),\n basename(filePath),\n );\n\n const res = await fetch(opts.host.replace(/\\/$/, '') + '/api/v1/artifacts/upload', {\n method: 'POST',\n headers: { 'X-AllStak-Upload-Token': opts.token },\n body: form,\n });\n return { status: res.status, body: await res.text(), ok: res.ok };\n}\n\nfunction sha8(buf: Buffer): string {\n // Browser/Node fallback — keeps this file pure-JS (no node:crypto in\n // the public surface). Caller doesn't need cryptographic strength;\n // this is for log identification only.\n let hash = 0;\n for (let i = 0; i < buf.length; i++) hash = ((hash << 5) - hash + buf[i]!) | 0;\n return (hash >>> 0).toString(16).padStart(8, '0');\n}\n\n/** Upload one bundle/map pair. The map is always uploaded; bundle is opt-in. */\nexport async function uploadPair(p: BundlePair, opts: UploadOptions): Promise<UploadResult> {\n const debugId = readDebugIdFromMap(p.mapPath);\n if (!debugId) {\n return {\n bundleName: p.bundleName,\n debugId: '',\n ok: false,\n steps: [{\n type: 'sourcemap',\n status: 0,\n sha8: '',\n body: `[allstak/upload] no debugId in ${p.mapPath} — run inject first`,\n }],\n };\n }\n const merged = {\n release: opts.release,\n host: opts.host ?? process.env.ALLSTAK_HOST ?? DEFAULT_HOST,\n token: opts.token,\n dist: opts.dist,\n stripSources: opts.stripSources,\n };\n\n const steps: UploadResult['steps'] = [];\n const mapResult = await uploadOne('sourcemap', p.mapPath, debugId, merged);\n steps.push({\n type: 'sourcemap',\n status: mapResult.status,\n sha8: sha8(readFileSync(p.mapPath)),\n body: mapResult.ok ? undefined : mapResult.body,\n });\n\n let allOk = mapResult.ok;\n if (opts.uploadBundles) {\n const bundleResult = await uploadOne('bundle', p.jsPath, debugId, merged);\n steps.push({\n type: 'bundle',\n status: bundleResult.status,\n sha8: sha8(readFileSync(p.jsPath)),\n body: bundleResult.ok ? undefined : bundleResult.body,\n });\n allOk = allOk && bundleResult.ok;\n }\n\n return { bundleName: p.bundleName, debugId, ok: allOk, steps };\n}\n\n/** Upload every pair in parallel. */\nexport async function uploadAll(\n pairs: BundlePair[],\n opts: UploadOptions,\n): Promise<UploadResult[]> {\n return Promise.all(pairs.map((p) => uploadPair(p, opts)));\n}\n","/**\n * Programmatic source-map pipeline. Build-time only.\n *\n * The high-level \"do everything\" entry point Vite/Webpack/Next plugins\n * call. Walk the build output → inject debug IDs → upload artifacts.\n *\n * import { processBuildOutput } from '@allstak/react/sourcemaps';\n *\n * await processBuildOutput({\n * dir: 'dist',\n * release: 'web@1.4.2',\n * token: process.env.ALLSTAK_UPLOAD_TOKEN!,\n * });\n */\n\nexport type { BundlePair } from './walk';\nexport { findPairs, walk } from './walk';\nexport type { InjectResult } from './inject';\nexport { injectPair, injectAll, readDebugIdFromMap } from './inject';\nexport type { UploadOptions, UploadResult } from './upload';\nexport { uploadPair, uploadAll, DEFAULT_HOST } from './upload';\n\nimport { resolve } from 'node:path';\nimport { findPairs } from './walk';\nimport { injectAll } from './inject';\nimport { uploadAll, type UploadOptions, type UploadResult } from './upload';\n\n/** Options for {@link processBuildOutput}. */\nexport interface ProcessOptions extends Partial<UploadOptions> {\n /** Build output directory to scan (e.g. `dist`, `.next/static`). */\n dir: string;\n /**\n * If true, only inject debug IDs and skip upload. Useful for sample\n * apps and CI dry-runs.\n */\n injectOnly?: boolean;\n /** Suppress per-pair console output. Default false (you want to see this). */\n silent?: boolean;\n}\n\n/** What `processBuildOutput` reports back. */\nexport interface ProcessReport {\n /** Absolute output dir scanned. */\n dir: string;\n /** Pairs found. */\n pairs: number;\n /** Per-pair injection results (debugId + reused?). */\n injected: Array<{ bundleName: string; debugId: string; reused: boolean }>;\n /** Per-pair upload results, omitted when no token / `injectOnly: true`. */\n uploaded?: UploadResult[];\n}\n\nexport async function processBuildOutput(opts: ProcessOptions): Promise<ProcessReport> {\n const dir = resolve(opts.dir);\n const pairs = findPairs(dir);\n const log = opts.silent ? () => undefined : (m: string) => console.log(`[allstak/sourcemaps] ${m}`);\n\n log(`scanning ${dir} — ${pairs.length} bundle/map pair(s)`);\n if (pairs.length === 0) {\n return { dir, pairs: 0, injected: [] };\n }\n\n const injectedRaw = injectAll(pairs);\n const injected = injectedRaw.map(({ pair, result }) => ({\n bundleName: pair.bundleName,\n debugId: result.debugId,\n reused: result.reused,\n }));\n for (const i of injected) {\n log(` ${i.bundleName} ${i.debugId} ${i.reused ? '(reused)' : '(new)'}`);\n }\n\n const token = opts.token ?? process.env.ALLSTAK_UPLOAD_TOKEN;\n const release = opts.release ?? process.env.ALLSTAK_RELEASE;\n\n if (opts.injectOnly || !token) {\n if (!opts.injectOnly && !token) {\n log('skipping upload — no token (set ALLSTAK_UPLOAD_TOKEN or pass `token`)');\n }\n return { dir, pairs: pairs.length, injected };\n }\n if (!release) {\n log('skipping upload — no release (set ALLSTAK_RELEASE or pass `release`)');\n return { dir, pairs: pairs.length, injected };\n }\n\n const uploaded = await uploadAll(pairs, {\n ...opts,\n release,\n token,\n });\n for (const u of uploaded) {\n if (u.ok) {\n log(` ${u.bundleName} uploaded debugId=${u.debugId}`);\n } else {\n const last = u.steps[u.steps.length - 1];\n log(` ${u.bundleName} FAIL status=${last?.status ?? '?'} body=${last?.body ?? ''}`);\n }\n }\n return { dir, pairs: pairs.length, injected, uploaded };\n}\n"],"mappings":"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","names":["readFileSync","basename","readFileSync","basename"]}
|