@allstak/react 0.3.1 → 0.3.2
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 +211 -45
- 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
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/build/webpack.ts
|
|
21
|
+
var webpack_exports = {};
|
|
22
|
+
__export(webpack_exports, {
|
|
23
|
+
AllStakWebpackPlugin: () => AllStakWebpackPlugin
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(webpack_exports);
|
|
26
|
+
|
|
27
|
+
// src/build/walk.ts
|
|
28
|
+
var import_node_fs = require("fs");
|
|
29
|
+
var import_node_path = require("path");
|
|
30
|
+
function walk(dir, out = []) {
|
|
31
|
+
for (const name of (0, import_node_fs.readdirSync)(dir)) {
|
|
32
|
+
const full = (0, import_node_path.join)(dir, name);
|
|
33
|
+
const st = (0, import_node_fs.statSync)(full);
|
|
34
|
+
if (st.isDirectory()) walk(full, out);
|
|
35
|
+
else out.push(full);
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function findPairs(root) {
|
|
40
|
+
const all = walk(root);
|
|
41
|
+
const maps = new Set(all.filter((p) => p.endsWith(".map")));
|
|
42
|
+
const pairs = [];
|
|
43
|
+
for (const js of all) {
|
|
44
|
+
if (!js.endsWith(".js") && !js.endsWith(".mjs") && !js.endsWith(".cjs")) continue;
|
|
45
|
+
const map = js + ".map";
|
|
46
|
+
if (maps.has(map)) {
|
|
47
|
+
pairs.push({ jsPath: js, mapPath: map, bundleName: (0, import_node_path.basename)(js) });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return pairs;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/build/inject.ts
|
|
54
|
+
var import_node_fs2 = require("fs");
|
|
55
|
+
var import_node_crypto = require("crypto");
|
|
56
|
+
var DEBUG_ID_LINE_RE = /^\/\/# debugId=([0-9a-f-]{36})\s*$/m;
|
|
57
|
+
var REGISTRATION_MARKER = "/*!__allstak_debug_id_registration__*/";
|
|
58
|
+
function buildRegistrationSnippet(jsBody, debugId) {
|
|
59
|
+
const isEsm = /\bimport\.meta\b/.test(jsBody) || /^\s*(?:import|export)\b/m.test(jsBody);
|
|
60
|
+
if (isEsm) {
|
|
61
|
+
return `${REGISTRATION_MARKER}try{(globalThis._allstakDebugIds=globalThis._allstakDebugIds||{})[import.meta.url]="${debugId}"}catch(_){}`;
|
|
62
|
+
}
|
|
63
|
+
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(_){}})();`;
|
|
64
|
+
}
|
|
65
|
+
function stripRegistration(js) {
|
|
66
|
+
const lineRe = new RegExp(
|
|
67
|
+
"^" + REGISTRATION_MARKER.replace(/[/*!]/g, (c) => "\\" + c) + ".*$",
|
|
68
|
+
"m"
|
|
69
|
+
);
|
|
70
|
+
return js.replace(lineRe, "");
|
|
71
|
+
}
|
|
72
|
+
function injectPair(p) {
|
|
73
|
+
const jsRaw = (0, import_node_fs2.readFileSync)(p.jsPath, "utf8");
|
|
74
|
+
const mapRaw = (0, import_node_fs2.readFileSync)(p.mapPath, "utf8");
|
|
75
|
+
const map = JSON.parse(mapRaw);
|
|
76
|
+
let debugId = typeof map.debugId === "string" ? map.debugId : "";
|
|
77
|
+
const existing = DEBUG_ID_LINE_RE.exec(jsRaw);
|
|
78
|
+
if (existing && existing[1]) debugId = debugId || existing[1];
|
|
79
|
+
const reused = !!debugId;
|
|
80
|
+
if (!debugId) debugId = (0, import_node_crypto.randomUUID)();
|
|
81
|
+
map.debugId = debugId;
|
|
82
|
+
(0, import_node_fs2.writeFileSync)(p.mapPath, JSON.stringify(map));
|
|
83
|
+
let jsOut = stripRegistration(jsRaw.replace(DEBUG_ID_LINE_RE, ""));
|
|
84
|
+
jsOut = jsOut.replace(/\s+$/, "");
|
|
85
|
+
jsOut += `
|
|
86
|
+
${buildRegistrationSnippet(jsOut, debugId)}
|
|
87
|
+
//# debugId=${debugId}
|
|
88
|
+
`;
|
|
89
|
+
(0, import_node_fs2.writeFileSync)(p.jsPath, jsOut);
|
|
90
|
+
return { debugId, reused };
|
|
91
|
+
}
|
|
92
|
+
function injectAll(pairs) {
|
|
93
|
+
return pairs.map((pair) => ({ pair, result: injectPair(pair) }));
|
|
94
|
+
}
|
|
95
|
+
function readDebugIdFromMap(mapPath) {
|
|
96
|
+
const json = JSON.parse((0, import_node_fs2.readFileSync)(mapPath, "utf8"));
|
|
97
|
+
return typeof json.debugId === "string" ? json.debugId : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/build/upload.ts
|
|
101
|
+
var import_node_fs3 = require("fs");
|
|
102
|
+
var import_node_path2 = require("path");
|
|
103
|
+
var DEFAULT_HOST = "https://api.allstak.sa";
|
|
104
|
+
async function uploadOne(type, filePath, debugId, opts) {
|
|
105
|
+
let buf = (0, import_node_fs3.readFileSync)(filePath);
|
|
106
|
+
if (type === "sourcemap" && opts.stripSources) {
|
|
107
|
+
const json = JSON.parse(buf.toString("utf8"));
|
|
108
|
+
if (Array.isArray(json.sourcesContent)) delete json.sourcesContent;
|
|
109
|
+
buf = Buffer.from(JSON.stringify(json));
|
|
110
|
+
}
|
|
111
|
+
const form = new FormData();
|
|
112
|
+
form.append("debugId", debugId);
|
|
113
|
+
form.append("type", type);
|
|
114
|
+
form.append("release", opts.release);
|
|
115
|
+
if (opts.dist) form.append("dist", opts.dist);
|
|
116
|
+
form.append(
|
|
117
|
+
"file",
|
|
118
|
+
new Blob([buf], {
|
|
119
|
+
type: type === "sourcemap" ? "application/json" : "application/javascript"
|
|
120
|
+
}),
|
|
121
|
+
(0, import_node_path2.basename)(filePath)
|
|
122
|
+
);
|
|
123
|
+
const res = await fetch(opts.host.replace(/\/$/, "") + "/api/v1/artifacts/upload", {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: { "X-AllStak-Upload-Token": opts.token },
|
|
126
|
+
body: form
|
|
127
|
+
});
|
|
128
|
+
return { status: res.status, body: await res.text(), ok: res.ok };
|
|
129
|
+
}
|
|
130
|
+
function sha8(buf) {
|
|
131
|
+
let hash = 0;
|
|
132
|
+
for (let i = 0; i < buf.length; i++) hash = (hash << 5) - hash + buf[i] | 0;
|
|
133
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
134
|
+
}
|
|
135
|
+
async function uploadPair(p, opts) {
|
|
136
|
+
const debugId = readDebugIdFromMap(p.mapPath);
|
|
137
|
+
if (!debugId) {
|
|
138
|
+
return {
|
|
139
|
+
bundleName: p.bundleName,
|
|
140
|
+
debugId: "",
|
|
141
|
+
ok: false,
|
|
142
|
+
steps: [{
|
|
143
|
+
type: "sourcemap",
|
|
144
|
+
status: 0,
|
|
145
|
+
sha8: "",
|
|
146
|
+
body: `[allstak/upload] no debugId in ${p.mapPath} \u2014 run inject first`
|
|
147
|
+
}]
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const merged = {
|
|
151
|
+
release: opts.release,
|
|
152
|
+
host: opts.host ?? process.env.ALLSTAK_HOST ?? DEFAULT_HOST,
|
|
153
|
+
token: opts.token,
|
|
154
|
+
dist: opts.dist,
|
|
155
|
+
stripSources: opts.stripSources
|
|
156
|
+
};
|
|
157
|
+
const steps = [];
|
|
158
|
+
const mapResult = await uploadOne("sourcemap", p.mapPath, debugId, merged);
|
|
159
|
+
steps.push({
|
|
160
|
+
type: "sourcemap",
|
|
161
|
+
status: mapResult.status,
|
|
162
|
+
sha8: sha8((0, import_node_fs3.readFileSync)(p.mapPath)),
|
|
163
|
+
body: mapResult.ok ? void 0 : mapResult.body
|
|
164
|
+
});
|
|
165
|
+
let allOk = mapResult.ok;
|
|
166
|
+
if (opts.uploadBundles) {
|
|
167
|
+
const bundleResult = await uploadOne("bundle", p.jsPath, debugId, merged);
|
|
168
|
+
steps.push({
|
|
169
|
+
type: "bundle",
|
|
170
|
+
status: bundleResult.status,
|
|
171
|
+
sha8: sha8((0, import_node_fs3.readFileSync)(p.jsPath)),
|
|
172
|
+
body: bundleResult.ok ? void 0 : bundleResult.body
|
|
173
|
+
});
|
|
174
|
+
allOk = allOk && bundleResult.ok;
|
|
175
|
+
}
|
|
176
|
+
return { bundleName: p.bundleName, debugId, ok: allOk, steps };
|
|
177
|
+
}
|
|
178
|
+
async function uploadAll(pairs, opts) {
|
|
179
|
+
return Promise.all(pairs.map((p) => uploadPair(p, opts)));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/build/sourcemaps.ts
|
|
183
|
+
var import_node_path3 = require("path");
|
|
184
|
+
async function processBuildOutput(opts) {
|
|
185
|
+
const dir = (0, import_node_path3.resolve)(opts.dir);
|
|
186
|
+
const pairs = findPairs(dir);
|
|
187
|
+
const log = opts.silent ? () => void 0 : (m) => console.log(`[allstak/sourcemaps] ${m}`);
|
|
188
|
+
log(`scanning ${dir} \u2014 ${pairs.length} bundle/map pair(s)`);
|
|
189
|
+
if (pairs.length === 0) {
|
|
190
|
+
return { dir, pairs: 0, injected: [] };
|
|
191
|
+
}
|
|
192
|
+
const injectedRaw = injectAll(pairs);
|
|
193
|
+
const injected = injectedRaw.map(({ pair, result }) => ({
|
|
194
|
+
bundleName: pair.bundleName,
|
|
195
|
+
debugId: result.debugId,
|
|
196
|
+
reused: result.reused
|
|
197
|
+
}));
|
|
198
|
+
for (const i of injected) {
|
|
199
|
+
log(` ${i.bundleName} ${i.debugId} ${i.reused ? "(reused)" : "(new)"}`);
|
|
200
|
+
}
|
|
201
|
+
const token = opts.token ?? process.env.ALLSTAK_UPLOAD_TOKEN;
|
|
202
|
+
const release = opts.release ?? process.env.ALLSTAK_RELEASE;
|
|
203
|
+
if (opts.injectOnly || !token) {
|
|
204
|
+
if (!opts.injectOnly && !token) {
|
|
205
|
+
log("skipping upload \u2014 no token (set ALLSTAK_UPLOAD_TOKEN or pass `token`)");
|
|
206
|
+
}
|
|
207
|
+
return { dir, pairs: pairs.length, injected };
|
|
208
|
+
}
|
|
209
|
+
if (!release) {
|
|
210
|
+
log("skipping upload \u2014 no release (set ALLSTAK_RELEASE or pass `release`)");
|
|
211
|
+
return { dir, pairs: pairs.length, injected };
|
|
212
|
+
}
|
|
213
|
+
const uploaded = await uploadAll(pairs, {
|
|
214
|
+
...opts,
|
|
215
|
+
release,
|
|
216
|
+
token
|
|
217
|
+
});
|
|
218
|
+
for (const u of uploaded) {
|
|
219
|
+
if (u.ok) {
|
|
220
|
+
log(` ${u.bundleName} uploaded debugId=${u.debugId}`);
|
|
221
|
+
} else {
|
|
222
|
+
const last = u.steps[u.steps.length - 1];
|
|
223
|
+
log(` ${u.bundleName} FAIL status=${last?.status ?? "?"} body=${last?.body ?? ""}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { dir, pairs: pairs.length, injected, uploaded };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/build/webpack.ts
|
|
230
|
+
var AllStakWebpackPlugin = class {
|
|
231
|
+
constructor(opts = {}) {
|
|
232
|
+
this.opts = opts;
|
|
233
|
+
/** Last successful report — exposed for tests / programmatic inspection. */
|
|
234
|
+
this.lastReport = null;
|
|
235
|
+
}
|
|
236
|
+
apply(compiler) {
|
|
237
|
+
if (this.opts.disabled) return;
|
|
238
|
+
compiler.hooks.afterEmit.tapPromise("AllStakWebpackPlugin", async (compilation) => {
|
|
239
|
+
const dir = this.opts.dir ?? compilation.compiler?.outputPath ?? compiler.outputPath ?? compiler.options?.output?.path ?? process.cwd();
|
|
240
|
+
try {
|
|
241
|
+
this.lastReport = await processBuildOutput({
|
|
242
|
+
...this.opts,
|
|
243
|
+
dir,
|
|
244
|
+
silent: this.opts.silent ?? false
|
|
245
|
+
});
|
|
246
|
+
} catch (e) {
|
|
247
|
+
console.error(`[allstak/webpack] failed: ${e.message}`);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
253
|
+
0 && (module.exports = {
|
|
254
|
+
AllStakWebpackPlugin
|
|
255
|
+
});
|
|
256
|
+
//# sourceMappingURL=webpack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/build/webpack.ts","../../src/build/walk.ts","../../src/build/inject.ts","../../src/build/upload.ts","../../src/build/sourcemaps.ts"],"sourcesContent":["/**\n * Webpack plugin — same job as the Vite plugin: walk the build output\n * after emit, inject debug IDs into every JS+map pair, and (when a\n * token is present) upload to AllStak.\n *\n * // webpack.config.js\n * const { AllStakWebpackPlugin } = require('@allstak/react/webpack');\n *\n * module.exports = {\n * devtool: 'source-map',\n * plugins: [\n * new AllStakWebpackPlugin({\n * release: process.env.ALLSTAK_RELEASE,\n * token: process.env.ALLSTAK_UPLOAD_TOKEN,\n * dist: 'web',\n * }),\n * ],\n * };\n *\n * The plugin hooks `afterEmit`, the moment Webpack guarantees every\n * asset is on disk. Failures log a warning rather than failing the\n * build — a flaky CI step doesn't block shipping.\n */\n\nimport { processBuildOutput, type ProcessOptions, type ProcessReport } from './sourcemaps';\n\nexport interface AllStakWebpackPluginOptions extends Omit<ProcessOptions, 'dir'> {\n /** Override the output directory. By default uses `compiler.outputPath`. */\n dir?: string;\n /** If true, plugin is a no-op. */\n disabled?: boolean;\n}\n\ninterface MinimalCompiler {\n hooks: { afterEmit: { tapPromise: (name: string, fn: (compilation: { compiler?: { outputPath?: string } }) => Promise<void>) => void } };\n outputPath?: string;\n options?: { output?: { path?: string } };\n}\n\nexport class AllStakWebpackPlugin {\n /** Last successful report — exposed for tests / programmatic inspection. */\n public lastReport: ProcessReport | null = null;\n\n constructor(private readonly opts: AllStakWebpackPluginOptions = {} as AllStakWebpackPluginOptions) {}\n\n apply(compiler: MinimalCompiler): void {\n if (this.opts.disabled) return;\n\n compiler.hooks.afterEmit.tapPromise('AllStakWebpackPlugin', async (compilation) => {\n const dir =\n this.opts.dir ??\n compilation.compiler?.outputPath ??\n compiler.outputPath ??\n compiler.options?.output?.path ??\n process.cwd();\n\n try {\n this.lastReport = await processBuildOutput({\n ...this.opts,\n dir,\n silent: this.opts.silent ?? false,\n });\n } catch (e) {\n console.error(`[allstak/webpack] failed: ${(e as Error).message}`);\n }\n });\n }\n}\n","/**\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":["import_node_fs","import_node_fs","import_node_path","import_node_path"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* in-memory ring buffer
|
|
4
|
+
* Fail-open HTTP transport for browser/React. Telemetry sends are best-effort:
|
|
5
|
+
* they use a short timeout, never reject into the host app, and fall into a
|
|
6
|
+
* bounded in-memory ring buffer with circuit-breaker backoff when AllStak is
|
|
7
|
+
* unavailable.
|
|
7
8
|
*
|
|
8
9
|
* No window, no AbortController fallback shims — RN exposes both natively.
|
|
9
10
|
*/
|
|
@@ -12,10 +13,17 @@ declare class HttpTransport {
|
|
|
12
13
|
private apiKey;
|
|
13
14
|
private buffer;
|
|
14
15
|
private flushing;
|
|
16
|
+
private consecutiveFailures;
|
|
17
|
+
private circuitOpenUntil;
|
|
15
18
|
constructor(baseUrl: string, apiKey: string);
|
|
16
19
|
send(path: string, payload: unknown): Promise<void>;
|
|
20
|
+
private enqueueOrDispatch;
|
|
21
|
+
private dispatch;
|
|
17
22
|
private doFetch;
|
|
23
|
+
private push;
|
|
24
|
+
private scheduleFlush;
|
|
18
25
|
private flushBuffer;
|
|
26
|
+
private recordFailure;
|
|
19
27
|
getBufferSize(): number;
|
|
20
28
|
/**
|
|
21
29
|
* Wait for the in-flight retry-buffer to drain. Resolves `true` if the
|
|
@@ -303,9 +311,44 @@ interface HttpTrackingOptions {
|
|
|
303
311
|
maxBodyBytes?: number;
|
|
304
312
|
}
|
|
305
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Idempotent instrumentation of `globalThis.fetch` and `console.warn/error`
|
|
316
|
+
* to feed breadcrumbs into the AllStak client. Safe to call once at init.
|
|
317
|
+
*
|
|
318
|
+
* - `instrumentFetch`: wraps fetch and records a breadcrumb per request
|
|
319
|
+
* (success and failure). Skips requests targeting the SDK's own ingest
|
|
320
|
+
* host so the wrap never recurses. Preserves the original return type
|
|
321
|
+
* and rethrows fetch errors after the breadcrumb is recorded.
|
|
322
|
+
* - `instrumentConsole`: wraps `console.warn` and `console.error` to
|
|
323
|
+
* record `log`-type breadcrumbs at the corresponding level.
|
|
324
|
+
*
|
|
325
|
+
* Both patches use a flag on the wrapper function so a second call is a
|
|
326
|
+
* no-op — important because hot-module-reload in dev would otherwise
|
|
327
|
+
* stack patches and double-fire breadcrumbs.
|
|
328
|
+
*/
|
|
329
|
+
type AddBreadcrumbFn$1 = (type: string, msg: string, level?: string, data?: Record<string, unknown>) => void;
|
|
330
|
+
declare function instrumentFetch(addBreadcrumb: AddBreadcrumbFn$1, ownBaseUrl?: string): void;
|
|
331
|
+
/**
|
|
332
|
+
* Per-console-method capture flags. Defaults: warn + error captured,
|
|
333
|
+
* log + info NOT captured (typical React apps emit thousands of debug
|
|
334
|
+
* lines per session — flooding the dashboard with them creates pure
|
|
335
|
+
* noise, so they're opt-in).
|
|
336
|
+
*
|
|
337
|
+
* <AllStakProvider captureConsole={{ log: true, info: true }} />
|
|
338
|
+
*/
|
|
339
|
+
interface ConsoleCaptureOptions {
|
|
340
|
+
log?: boolean;
|
|
341
|
+
info?: boolean;
|
|
342
|
+
warn?: boolean;
|
|
343
|
+
error?: boolean;
|
|
344
|
+
}
|
|
345
|
+
declare function instrumentConsole(addBreadcrumb: AddBreadcrumbFn$1, options?: ConsoleCaptureOptions): void;
|
|
346
|
+
/** @internal — for tests. Resets the wrap-once flag. */
|
|
347
|
+
declare function __resetConsoleInstrumentationFlagForTest(): void;
|
|
348
|
+
|
|
306
349
|
declare const INGEST_HOST = "https://api.allstak.sa";
|
|
307
350
|
declare const SDK_NAME = "allstak-react";
|
|
308
|
-
declare const SDK_VERSION = "0.3.
|
|
351
|
+
declare const SDK_VERSION = "0.3.1";
|
|
309
352
|
|
|
310
353
|
interface AllStakConfig {
|
|
311
354
|
/** Project API key (`ask_live_…`). Required. */
|
|
@@ -342,8 +385,22 @@ interface AllStakConfig {
|
|
|
342
385
|
autoCaptureBrowserErrors?: boolean;
|
|
343
386
|
/** Wrap `globalThis.fetch` to record HTTP breadcrumbs. Default: true */
|
|
344
387
|
autoBreadcrumbsFetch?: boolean;
|
|
345
|
-
/** Wrap `console
|
|
388
|
+
/** Wrap `console.*` methods to record log breadcrumbs. Default: true.
|
|
389
|
+
* Per-method capture is controlled by `captureConsole` (warn + error
|
|
390
|
+
* default on, log + info default off). */
|
|
346
391
|
autoBreadcrumbsConsole?: boolean;
|
|
392
|
+
/**
|
|
393
|
+
* Per-console-method capture flags. Defaults: warn + error captured,
|
|
394
|
+
* log + info NOT captured (to avoid breadcrumb spam from typical app
|
|
395
|
+
* logging). Set `{ log: true, info: true }` to opt-in.
|
|
396
|
+
*/
|
|
397
|
+
captureConsole?: ConsoleCaptureOptions;
|
|
398
|
+
/**
|
|
399
|
+
* Auto-capture Web Vitals (CLS, LCP, INP, FCP, TTFB) via the browser's
|
|
400
|
+
* PerformanceObserver. Default: true. Each metric ships as a
|
|
401
|
+
* `web_vitals` log on the next backend flush.
|
|
402
|
+
*/
|
|
403
|
+
autoWebVitals?: boolean;
|
|
347
404
|
/** Wrap `history.pushState`/`replaceState` and listen to `popstate` for SPA navigation breadcrumbs. Default: true */
|
|
348
405
|
autoBreadcrumbsNavigation?: boolean;
|
|
349
406
|
/**
|
|
@@ -440,6 +497,7 @@ declare class AllStakClient {
|
|
|
440
497
|
private _instrumentAxios;
|
|
441
498
|
private onErrorHandler;
|
|
442
499
|
private onRejectionHandler;
|
|
500
|
+
private webVitals;
|
|
443
501
|
constructor(config: AllStakConfig);
|
|
444
502
|
/** Manually instrument an axios instance. No-op when HTTP tracking is off. */
|
|
445
503
|
instrumentAxios<T = any>(axios: T): T;
|
|
@@ -576,6 +634,80 @@ declare const AllStak: {
|
|
|
576
634
|
/** @internal */ _getInstance(): AllStakClient | null;
|
|
577
635
|
};
|
|
578
636
|
|
|
637
|
+
interface AllStakProviderProps {
|
|
638
|
+
children: React.ReactNode;
|
|
639
|
+
apiKey: string;
|
|
640
|
+
environment?: string;
|
|
641
|
+
release?: string;
|
|
642
|
+
host?: string;
|
|
643
|
+
user?: {
|
|
644
|
+
id?: string;
|
|
645
|
+
email?: string;
|
|
646
|
+
};
|
|
647
|
+
tags?: Record<string, string>;
|
|
648
|
+
debug?: boolean;
|
|
649
|
+
enableHttpTracking?: boolean;
|
|
650
|
+
httpTracking?: AllStakConfig['httpTracking'];
|
|
651
|
+
/**
|
|
652
|
+
* Per-console-method capture flags. Defaults: warn + error captured,
|
|
653
|
+
* log + info NOT captured (to avoid breadcrumb spam from typical app
|
|
654
|
+
* logging). Set `{ log: true, info: true }` to opt-in.
|
|
655
|
+
*/
|
|
656
|
+
captureConsole?: AllStakConfig['captureConsole'];
|
|
657
|
+
sampleRate?: number;
|
|
658
|
+
beforeSend?: AllStakConfig['beforeSend'];
|
|
659
|
+
replay?: AllStakConfig['replay'];
|
|
660
|
+
tracesSampleRate?: number;
|
|
661
|
+
service?: string;
|
|
662
|
+
dist?: string;
|
|
663
|
+
/** Default true. Capture window error + unhandledrejection. */
|
|
664
|
+
autoCaptureBrowserErrors?: boolean;
|
|
665
|
+
/** Default true. Wrap fetch for breadcrumbs. */
|
|
666
|
+
autoBreadcrumbsFetch?: boolean;
|
|
667
|
+
/** Default true. Wrap console for breadcrumbs (per-method via captureConsole). */
|
|
668
|
+
autoBreadcrumbsConsole?: boolean;
|
|
669
|
+
/** Default true. Patch history.pushState/replaceState + popstate listener. */
|
|
670
|
+
autoBreadcrumbsNavigation?: boolean;
|
|
671
|
+
/** Default true. Collect Web Vitals via PerformanceObserver. */
|
|
672
|
+
autoWebVitals?: boolean;
|
|
673
|
+
/**
|
|
674
|
+
* Tear down the SDK when the provider unmounts. Default `false`.
|
|
675
|
+
*
|
|
676
|
+
* Most apps mount `AllStakProvider` once at the root and never unmount
|
|
677
|
+
* it. Setting this to `true` risks disabling telemetry if the provider
|
|
678
|
+
* re-mounts (Fast Refresh in dev, route key changes, React 18 Strict
|
|
679
|
+
* Mode double-mount, etc.) — there is a brief window between unmount
|
|
680
|
+
* and remount where captures throw.
|
|
681
|
+
*
|
|
682
|
+
* Leave at the default unless you genuinely need to dispose the SDK.
|
|
683
|
+
*/
|
|
684
|
+
destroyOnUnmount?: boolean;
|
|
685
|
+
fallback?: React.ReactNode | ((props: {
|
|
686
|
+
error: Error;
|
|
687
|
+
resetError: () => void;
|
|
688
|
+
}) => React.ReactNode);
|
|
689
|
+
onError?: (error: Error, componentStack?: string) => void;
|
|
690
|
+
}
|
|
691
|
+
declare function AllStakProvider({ children, apiKey, environment, release, host, user, tags, debug, enableHttpTracking, httpTracking, captureConsole, sampleRate, beforeSend, replay, tracesSampleRate, service, dist, autoCaptureBrowserErrors, autoBreadcrumbsFetch, autoBreadcrumbsConsole, autoBreadcrumbsNavigation, autoWebVitals, destroyOnUnmount, fallback, onError, }: AllStakProviderProps): React.ReactElement;
|
|
692
|
+
/**
|
|
693
|
+
* Convenience hook — exposes the most common capture/context APIs with
|
|
694
|
+
* a stable identity so components don't have to import the namespace.
|
|
695
|
+
*/
|
|
696
|
+
declare function useAllStak(): {
|
|
697
|
+
captureException: (error: Error, ctx?: Record<string, unknown>) => void;
|
|
698
|
+
captureMessage: (msg: string, level?: "fatal" | "error" | "warning" | "info") => void;
|
|
699
|
+
setUser: (user: {
|
|
700
|
+
id?: string;
|
|
701
|
+
email?: string;
|
|
702
|
+
}) => void;
|
|
703
|
+
setTag: (key: string, value: string) => void;
|
|
704
|
+
setContext: (name: string, ctx: Record<string, unknown> | null) => void;
|
|
705
|
+
addBreadcrumb: (type: string, message: string, level?: string, data?: Record<string, unknown>) => void;
|
|
706
|
+
flush: (timeoutMs?: number) => Promise<boolean>;
|
|
707
|
+
};
|
|
708
|
+
/** @internal — for tests. Resets the module-level remount-guard. */
|
|
709
|
+
declare function __resetProviderInstanceForTest(): void;
|
|
710
|
+
|
|
579
711
|
/**
|
|
580
712
|
* Browser navigation breadcrumbs — minimal fallback that doesn't depend on
|
|
581
713
|
* any specific router library. Wraps `history.pushState`/`replaceState` and
|
|
@@ -588,41 +720,77 @@ declare const AllStak: {
|
|
|
588
720
|
* For framework-specific instrumentation (React Router's `useNavigate`,
|
|
589
721
|
* Next's `router.events`), bind manually with `AllStak.addBreadcrumb`.
|
|
590
722
|
*/
|
|
591
|
-
type AddBreadcrumbFn
|
|
592
|
-
declare function instrumentBrowserNavigation(addBreadcrumb: AddBreadcrumbFn
|
|
723
|
+
type AddBreadcrumbFn = (type: string, msg: string, level?: string, data?: Record<string, unknown>) => void;
|
|
724
|
+
declare function instrumentBrowserNavigation(addBreadcrumb: AddBreadcrumbFn): void;
|
|
593
725
|
declare function instrumentReactRouter(location: {
|
|
594
726
|
pathname: string;
|
|
595
727
|
search?: string;
|
|
596
|
-
}, addBreadcrumb?: AddBreadcrumbFn
|
|
597
|
-
declare function instrumentNextRouter(url: string, addBreadcrumb?: AddBreadcrumbFn
|
|
728
|
+
}, addBreadcrumb?: AddBreadcrumbFn): void;
|
|
729
|
+
declare function instrumentNextRouter(url: string, addBreadcrumb?: AddBreadcrumbFn): void;
|
|
598
730
|
|
|
599
731
|
/**
|
|
600
|
-
*
|
|
601
|
-
* to feed breadcrumbs into the AllStak client. Safe to call once at init.
|
|
732
|
+
* Lightweight Web Vitals collection via the browser's PerformanceObserver.
|
|
602
733
|
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
* - `instrumentConsole`: wraps `console.warn` and `console.error` to
|
|
608
|
-
* record `log`-type breadcrumbs at the corresponding level.
|
|
734
|
+
* Captures CLS, LCP, INP, FCP, TTFB without pulling in the `web-vitals`
|
|
735
|
+
* package as a runtime dependency. Each metric is shipped as a single
|
|
736
|
+
* `web_vital` log on the SDK's `/ingest/v1/logs` channel — so the
|
|
737
|
+
* dashboard can chart them alongside other logs.
|
|
609
738
|
*
|
|
610
|
-
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
739
|
+
* Privacy: only numeric values + the metric name are sent. No URLs,
|
|
740
|
+
* referrers, or user-identifiable data.
|
|
741
|
+
*
|
|
742
|
+
* Browser compatibility: PerformanceObserver is available in every
|
|
743
|
+
* evergreen browser (Chrome 51+, Safari 11+, Firefox 57+, Edge 79+).
|
|
744
|
+
* Older browsers silently no-op via the `typeof PerformanceObserver`
|
|
745
|
+
* guard.
|
|
746
|
+
*
|
|
747
|
+
* - **CLS** (Cumulative Layout Shift) — layout-shift entries with
|
|
748
|
+
* `hadRecentInput=false`, summed for the session
|
|
749
|
+
* - **LCP** (Largest Contentful Paint) — biggest paint entry's startTime
|
|
750
|
+
* - **INP** (Interaction to Next Paint) — longest event-timing duration
|
|
751
|
+
* - **FCP** (First Contentful Paint) — first paint named `first-contentful-paint`
|
|
752
|
+
* - **TTFB** (Time to First Byte) — `responseStart - requestStart` from the
|
|
753
|
+
* navigation timing entry
|
|
613
754
|
*/
|
|
614
|
-
type
|
|
615
|
-
|
|
616
|
-
|
|
755
|
+
type VitalsReporter = (metric: {
|
|
756
|
+
name: string;
|
|
757
|
+
value: number;
|
|
758
|
+
id?: string;
|
|
759
|
+
}) => void;
|
|
760
|
+
interface WebVitalsHandle {
|
|
761
|
+
/** Disconnects all observers. Idempotent. */
|
|
762
|
+
destroy(): void;
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Start collecting Web Vitals. Returns a handle whose `destroy()` cleans
|
|
766
|
+
* up all PerformanceObservers. Safe no-op on non-browser runtimes.
|
|
767
|
+
*/
|
|
768
|
+
declare function startWebVitals(report: VitalsReporter): WebVitalsHandle;
|
|
769
|
+
/** @internal — reset the started flag so tests can re-init. */
|
|
770
|
+
declare function __resetWebVitalsFlagForTest(): void;
|
|
617
771
|
|
|
618
772
|
/**
|
|
619
773
|
* @allstak/react — standalone React SDK.
|
|
620
774
|
*
|
|
621
775
|
* Self-contained: no @allstak/js or @allstak-io/* dependencies. Ships its own
|
|
622
776
|
* AllStak client (init/capture/breadcrumbs/transport) plus React-specific
|
|
623
|
-
* helpers (ErrorBoundary, useAllStak hook, withAllStakProfiler HOC).
|
|
777
|
+
* helpers (Provider, ErrorBoundary, useAllStak hook, withAllStakProfiler HOC).
|
|
778
|
+
*
|
|
779
|
+
* Recommended usage (one-liner):
|
|
780
|
+
*
|
|
781
|
+
* import { AllStakProvider } from '@allstak/react';
|
|
782
|
+
*
|
|
783
|
+
* export function App() {
|
|
784
|
+
* return (
|
|
785
|
+
* <AllStakProvider apiKey="ask_live_..." environment="production" debug>
|
|
786
|
+
* <AppRoot />
|
|
787
|
+
* </AllStakProvider>
|
|
788
|
+
* );
|
|
789
|
+
* }
|
|
624
790
|
*
|
|
625
|
-
*
|
|
791
|
+
* Advanced / manual usage:
|
|
792
|
+
*
|
|
793
|
+
* import { AllStak } from '@allstak/react';
|
|
626
794
|
* AllStak.init({ apiKey, environment, release });
|
|
627
795
|
* <AllStakErrorBoundary>...</AllStakErrorBoundary>
|
|
628
796
|
* const { captureException } = useAllStak();
|
|
@@ -650,23 +818,9 @@ declare class AllStakErrorBoundary extends React.Component<AllStakErrorBoundaryP
|
|
|
650
818
|
render(): React.ReactNode;
|
|
651
819
|
}
|
|
652
820
|
/**
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
*/
|
|
656
|
-
declare function useAllStak(): {
|
|
657
|
-
captureException: (error: Error, ctx?: Record<string, unknown>) => void;
|
|
658
|
-
captureMessage: (msg: string, level?: "fatal" | "error" | "warning" | "info") => void;
|
|
659
|
-
setUser: (user: {
|
|
660
|
-
id?: string;
|
|
661
|
-
email?: string;
|
|
662
|
-
}) => void;
|
|
663
|
-
setTag: (key: string, value: string) => void;
|
|
664
|
-
addBreadcrumb: (type: string, message: string, level?: string, data?: Record<string, unknown>) => void;
|
|
665
|
-
};
|
|
666
|
-
/**
|
|
667
|
-
* HOC: drops a navigation breadcrumb when a component mounts. Useful for
|
|
668
|
-
* marking screen boundaries without a router.
|
|
821
|
+
* HOC: drops a navigation breadcrumb when a component mounts. Useful
|
|
822
|
+
* for marking screen boundaries without a router.
|
|
669
823
|
*/
|
|
670
824
|
declare function withAllStakProfiler<P extends object>(Component: React.ComponentType<P>, name?: string): React.FC<P>;
|
|
671
825
|
|
|
672
|
-
export { AllStak, AllStakClient, type AllStakConfig, AllStakErrorBoundary, type AllStakErrorBoundaryProps, type Breadcrumb, type HttpRequestEvent, HttpRequestModule, type HttpTrackingOptions, INGEST_HOST, type ReplayOptions, ReplayRecorder, SDK_NAME, SDK_VERSION, Scope, instrumentBrowserNavigation, instrumentConsole, instrumentFetch, instrumentNextRouter, instrumentReactRouter, useAllStak, withAllStakProfiler };
|
|
826
|
+
export { AllStak, AllStakClient, type AllStakConfig, AllStakErrorBoundary, type AllStakErrorBoundaryProps, AllStakProvider, type AllStakProviderProps, type Breadcrumb, type ConsoleCaptureOptions, type HttpRequestEvent, HttpRequestModule, type HttpTrackingOptions, INGEST_HOST, type ReplayOptions, ReplayRecorder, SDK_NAME, SDK_VERSION, Scope, type WebVitalsHandle, __resetConsoleInstrumentationFlagForTest, __resetProviderInstanceForTest, __resetWebVitalsFlagForTest, instrumentBrowserNavigation, instrumentConsole, instrumentFetch, instrumentNextRouter, instrumentReactRouter, startWebVitals, useAllStak, withAllStakProfiler };
|