@fixback/node 0.2.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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/dist/client.d.ts +79 -0
  4. package/dist/client.js +220 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/config.d.ts +71 -0
  7. package/dist/config.js +68 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/context.d.ts +53 -0
  10. package/dist/context.js +97 -0
  11. package/dist/context.js.map +1 -0
  12. package/dist/express.d.ts +42 -0
  13. package/dist/express.js +51 -0
  14. package/dist/express.js.map +1 -0
  15. package/dist/http.d.ts +53 -0
  16. package/dist/http.js +75 -0
  17. package/dist/http.js.map +1 -0
  18. package/dist/index.d.ts +23 -0
  19. package/dist/index.js +38 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/nestjs.d.ts +44 -0
  22. package/dist/nestjs.js +114 -0
  23. package/dist/nestjs.js.map +1 -0
  24. package/dist/package.json +3 -0
  25. package/dist/process.d.ts +52 -0
  26. package/dist/process.js +124 -0
  27. package/dist/process.js.map +1 -0
  28. package/dist/stack.d.ts +33 -0
  29. package/dist/stack.js +142 -0
  30. package/dist/stack.js.map +1 -0
  31. package/dist/transport.d.ts +90 -0
  32. package/dist/transport.js +172 -0
  33. package/dist/transport.js.map +1 -0
  34. package/dist/version.d.ts +13 -0
  35. package/dist/version.js +17 -0
  36. package/dist/version.js.map +1 -0
  37. package/dist/wire.d.ts +108 -0
  38. package/dist/wire.js +15 -0
  39. package/dist/wire.js.map +1 -0
  40. package/package.json +91 -0
  41. package/src/client.test.ts +272 -0
  42. package/src/client.ts +262 -0
  43. package/src/config.test.ts +79 -0
  44. package/src/config.ts +131 -0
  45. package/src/context.test.ts +99 -0
  46. package/src/context.ts +124 -0
  47. package/src/express.test.ts +112 -0
  48. package/src/express.ts +85 -0
  49. package/src/fingerprint-parity.test.ts +86 -0
  50. package/src/http.ts +83 -0
  51. package/src/index.ts +51 -0
  52. package/src/nestjs.test.ts +146 -0
  53. package/src/nestjs.ts +123 -0
  54. package/src/process.test.ts +154 -0
  55. package/src/process.ts +153 -0
  56. package/src/stack.test.ts +89 -0
  57. package/src/stack.ts +153 -0
  58. package/src/transport.test.ts +187 -0
  59. package/src/transport.ts +227 -0
  60. package/src/version.test.ts +10 -0
  61. package/src/version.ts +13 -0
  62. package/src/wire.ts +116 -0
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * Process-level capture (spec §D9, stories 4 & 5) — the *polite* `uncaughtException`
4
+ * and `unhandledRejection` handlers that **never change the app's exit behaviour**.
5
+ *
6
+ * The subtlety: merely *adding* an `uncaughtException` listener suppresses Node's
7
+ * default crash. So to stay polite:
8
+ *
9
+ * - **uncaughtException:** capture (`handled: false`), then — only when we are the
10
+ * *sole* listener (the app installed none of its own) — best-effort flush and hand
11
+ * off to `onFatalError`, which preserves Node's default (log + non-zero exit). When
12
+ * the app has its own handler, we do nothing further: the app owns the exit.
13
+ * - **unhandledRejection:** capture (`handled: false`) and stop. We never escalate a
14
+ * rejection to a process exit — exactly the "never turn a logged rejection into an
15
+ * exit" guarantee (story 5).
16
+ *
17
+ * Everything is wrapped so a capture failure can never break the handler.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.installProcessHandlers = installProcessHandlers;
21
+ const DEFAULT_FLUSH_TIMEOUT_MS = 2_000;
22
+ /** The real `process`, when running on Node. */
23
+ function resolveProcess() {
24
+ const g = globalThis;
25
+ const proc = g.process;
26
+ return proc && typeof proc.on === "function" ? proc : undefined;
27
+ }
28
+ /** Node's default fatal behaviour: print the error and exit non-zero. */
29
+ function defaultOnFatalError(proc) {
30
+ return (error) => {
31
+ try {
32
+ console.error(error);
33
+ }
34
+ catch {
35
+ /* a hostile console must not stop the exit */
36
+ }
37
+ const exit = proc.exit;
38
+ if (typeof exit === "function") {
39
+ try {
40
+ exit(1);
41
+ }
42
+ catch {
43
+ /* exit is terminal; nothing to do if it throws */
44
+ }
45
+ }
46
+ };
47
+ }
48
+ /** Race a flush against a timeout so a wedged transport can't hang a crashing process. */
49
+ function flushWithTimeout(flush, ms) {
50
+ let result;
51
+ try {
52
+ result = Promise.resolve(flush());
53
+ }
54
+ catch {
55
+ return Promise.resolve();
56
+ }
57
+ if (ms <= 0)
58
+ return result.catch(() => { });
59
+ let timer;
60
+ const timeout = new Promise((resolve) => {
61
+ timer = setTimeout(resolve, ms);
62
+ });
63
+ return Promise.race([result.catch(() => { }), timeout]).finally(() => {
64
+ if (timer !== undefined)
65
+ clearTimeout(timer);
66
+ });
67
+ }
68
+ /**
69
+ * Install the polite process handlers for `sink`, returning an uninstall function.
70
+ * A no-op (and a no-op uninstall) when no `process` is available or when both gates
71
+ * are off.
72
+ */
73
+ function installProcessHandlers(sink, options, deps = {}) {
74
+ const proc = deps.process ?? resolveProcess();
75
+ if (!proc)
76
+ return () => { };
77
+ const onFatalError = deps.onFatalError ?? defaultOnFatalError(proc);
78
+ const flushTimeoutMs = options.flushTimeoutMs ?? DEFAULT_FLUSH_TIMEOUT_MS;
79
+ const onUncaughtException = (...args) => {
80
+ const error = args[0];
81
+ try {
82
+ sink.captureException(error, { handled: false });
83
+ }
84
+ catch {
85
+ /* capture must never break the handler */
86
+ }
87
+ // If the app installed its own uncaughtException handler, it owns the exit — we
88
+ // must not force one (that would *change* its behaviour). Only when we are the
89
+ // sole listener do we preserve Node's default crash.
90
+ const others = proc.listeners("uncaughtException").filter((l) => l !== onUncaughtException);
91
+ if (others.length > 0)
92
+ return;
93
+ void flushWithTimeout(() => sink.flush(), flushTimeoutMs).finally(() => {
94
+ try {
95
+ onFatalError(error);
96
+ }
97
+ catch {
98
+ /* the fatal handler is terminal */
99
+ }
100
+ });
101
+ };
102
+ const onUnhandledRejection = (...args) => {
103
+ // Capture only — never escalate a rejection to a process exit (story 5).
104
+ try {
105
+ sink.captureException(args[0], { handled: false });
106
+ }
107
+ catch {
108
+ /* capture must never break the handler */
109
+ }
110
+ };
111
+ if (options.captureUncaughtException)
112
+ proc.on("uncaughtException", onUncaughtException);
113
+ if (options.captureUnhandledRejection)
114
+ proc.on("unhandledRejection", onUnhandledRejection);
115
+ return () => {
116
+ if (options.captureUncaughtException) {
117
+ proc.removeListener("uncaughtException", onUncaughtException);
118
+ }
119
+ if (options.captureUnhandledRejection) {
120
+ proc.removeListener("unhandledRejection", onUnhandledRejection);
121
+ }
122
+ };
123
+ }
124
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../src/process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Turning a thrown value into the pieces a capture needs: the distilled
3
+ * `type | value | stack` a fingerprint is built from ({@link extractError}), and the
4
+ * **structured stack frames** for the wire ({@link extractStructuredFrames}).
5
+ *
6
+ * The distillation mirrors the browser SDK's exactly (`packages/sdk/src/error-capture.ts`)
7
+ * — `type = error.name`, `value = error.message`, `stack = error.stack` — so the same
8
+ * logical error yields the same shared-core fingerprint on either surface (the
9
+ * ADR-0028 parity guarantee). Frame parsing keeps the full script path (server-side
10
+ * symbolication matches it against uploaded sourcemaps) but scrubs each URL through
11
+ * the shared-core scrubber before it can leave the process.
12
+ */
13
+ import { type CapturedFrame } from "@fixback/sdk-core";
14
+ /** The distilled shape a fingerprint and a report are built from. */
15
+ export interface ExtractedError {
16
+ readonly type: string;
17
+ readonly value: string;
18
+ readonly stack?: string;
19
+ }
20
+ /**
21
+ * Distil any thrown value into `{ type, value, stack }`. An `Error` (or error-shaped
22
+ * object) contributes its `name` / `message` / `stack`; a thrown string becomes the
23
+ * value; any other value is stringified. Never throws.
24
+ */
25
+ export declare function extractError(input: unknown): ExtractedError;
26
+ /**
27
+ * Extract structured frames from a stack for the wire (#117, ADR-0024), top of stack
28
+ * first. Keeps the full (scrubbed) script path so server-side symbolication can match
29
+ * it against uploaded sourcemaps; skips unlocatable frames (`native`, `<anonymous>`,
30
+ * eval); caps the count. V8 is Node's format; the Firefox/Safari form is handled too
31
+ * so the parser matches the browser SDK's exactly.
32
+ */
33
+ export declare function extractStructuredFrames(stack: string | undefined, limit?: number): CapturedFrame[];
package/dist/stack.js ADDED
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ /**
3
+ * Turning a thrown value into the pieces a capture needs: the distilled
4
+ * `type | value | stack` a fingerprint is built from ({@link extractError}), and the
5
+ * **structured stack frames** for the wire ({@link extractStructuredFrames}).
6
+ *
7
+ * The distillation mirrors the browser SDK's exactly (`packages/sdk/src/error-capture.ts`)
8
+ * — `type = error.name`, `value = error.message`, `stack = error.stack` — so the same
9
+ * logical error yields the same shared-core fingerprint on either surface (the
10
+ * ADR-0028 parity guarantee). Frame parsing keeps the full script path (server-side
11
+ * symbolication matches it against uploaded sourcemaps) but scrubs each URL through
12
+ * the shared-core scrubber before it can leave the process.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.extractError = extractError;
16
+ exports.extractStructuredFrames = extractStructuredFrames;
17
+ const sdk_core_1 = require("@fixback/sdk-core");
18
+ /** The most structured frames shipped per error (mirrors the server's cap). */
19
+ const STRUCTURED_FRAME_LIMIT = 30;
20
+ /** Coerce any value to a string without throwing (a hostile `toString` can throw). */
21
+ function asString(value) {
22
+ if (typeof value === "string")
23
+ return value;
24
+ if (value == null)
25
+ return "";
26
+ try {
27
+ return String(value);
28
+ }
29
+ catch {
30
+ return "";
31
+ }
32
+ }
33
+ /** Best-effort JSON for a non-Error thrown object, falling back to `String`. */
34
+ function safeStringify(value) {
35
+ try {
36
+ return JSON.stringify(value) ?? asString(value);
37
+ }
38
+ catch {
39
+ return asString(value);
40
+ }
41
+ }
42
+ /** True for a duck-typed error (a cross-realm Error, or an error-shaped throwable). */
43
+ function isErrorLike(value) {
44
+ const err = value;
45
+ return (typeof err.message === "string" ||
46
+ typeof err.stack === "string" ||
47
+ typeof err.name === "string");
48
+ }
49
+ /**
50
+ * Distil any thrown value into `{ type, value, stack }`. An `Error` (or error-shaped
51
+ * object) contributes its `name` / `message` / `stack`; a thrown string becomes the
52
+ * value; any other value is stringified. Never throws.
53
+ */
54
+ function extractError(input) {
55
+ if (input instanceof Error) {
56
+ return {
57
+ type: asString(input.name) || "Error",
58
+ value: asString(input.message),
59
+ stack: typeof input.stack === "string" ? input.stack : undefined,
60
+ };
61
+ }
62
+ if (typeof input === "string") {
63
+ return { type: "Error", value: input, stack: undefined };
64
+ }
65
+ if (input && typeof input === "object") {
66
+ if (isErrorLike(input)) {
67
+ const err = input;
68
+ return {
69
+ type: asString(err.name) || "Error",
70
+ value: asString(err.message),
71
+ stack: typeof err.stack === "string" ? err.stack : undefined,
72
+ };
73
+ }
74
+ return { type: "Error", value: safeStringify(input), stack: undefined };
75
+ }
76
+ return { type: "Error", value: asString(input), stack: undefined };
77
+ }
78
+ /** Split a `file:line:col` location into parts; `null` when it has no line. */
79
+ function parseLocation(location) {
80
+ // The file may itself contain colons (https://…, node:internal/…), so split from
81
+ // the right: the trailing `:line(:col)?` are the numeric groups.
82
+ const match = location.match(/^(.*?):(\d+)(?::(\d+))?$/);
83
+ if (!match)
84
+ return null;
85
+ const file = match[1] ?? "";
86
+ if (file.length === 0 || file === "native" || file.includes("<anonymous>")) {
87
+ return null;
88
+ }
89
+ const line = Number(match[2]);
90
+ if (!Number.isFinite(line))
91
+ return null;
92
+ const column = match[3] !== undefined ? Number(match[3]) : null;
93
+ return { file, line, column };
94
+ }
95
+ /**
96
+ * Extract structured frames from a stack for the wire (#117, ADR-0024), top of stack
97
+ * first. Keeps the full (scrubbed) script path so server-side symbolication can match
98
+ * it against uploaded sourcemaps; skips unlocatable frames (`native`, `<anonymous>`,
99
+ * eval); caps the count. V8 is Node's format; the Firefox/Safari form is handled too
100
+ * so the parser matches the browser SDK's exactly.
101
+ */
102
+ function extractStructuredFrames(stack, limit = STRUCTURED_FRAME_LIMIT) {
103
+ if (typeof stack !== "string" || stack.length === 0)
104
+ return [];
105
+ const frames = [];
106
+ for (const raw of stack.split("\n")) {
107
+ if (frames.length >= limit)
108
+ break;
109
+ const line = raw.trim();
110
+ let fn = null;
111
+ let location = null;
112
+ // V8: "at fn (loc)" | "at loc"
113
+ const v8Named = line.match(/^at\s+(.+?)\s+\((.+)\)$/);
114
+ const v8Bare = v8Named ? null : line.match(/^at\s+(.+)$/);
115
+ const geckoAt = v8Named || v8Bare ? -1 : line.indexOf("@");
116
+ if (v8Named) {
117
+ fn = v8Named[1] ?? null;
118
+ location = v8Named[2] ?? null;
119
+ }
120
+ else if (v8Bare) {
121
+ location = v8Bare[1] ?? null;
122
+ }
123
+ else if (geckoAt >= 0) {
124
+ // Firefox / Safari: "fn@loc" | "@loc"
125
+ fn = geckoAt > 0 ? line.slice(0, geckoAt) : null;
126
+ location = line.slice(geckoAt + 1);
127
+ }
128
+ if (!location)
129
+ continue;
130
+ const parsed = parseLocation(location);
131
+ if (!parsed)
132
+ continue;
133
+ frames.push({
134
+ file: (0, sdk_core_1.scrubUrl)(parsed.file),
135
+ line: parsed.line,
136
+ column: parsed.column,
137
+ function: fn && fn.length > 0 ? fn : null,
138
+ });
139
+ }
140
+ return frames;
141
+ }
142
+ //# sourceMappingURL=stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The **batched secret-key transport** (ADR-0026): the SDK buffers captured errors
3
+ * and flushes them to `POST /api/errors` as a JSON batch authenticated with the
4
+ * Project secret key as an `Authorization: Bearer` token.
5
+ *
6
+ * - **Batched:** a server emits many errors, so they buffer and flush together
7
+ * (on the batch cap, on an interval, on demand, and on `close`).
8
+ * - **Coalesced:** repeats of one fingerprint in a flush window fold into a single
9
+ * item with a summed occurrence count — the client half of the crash-loop defence
10
+ * (the server folds across flushes, ADR-0027).
11
+ * - **Backed off:** a `429` opens the shared-core {@link AutoReportBackoff} window
12
+ * (honouring `Retry-After`); while it is open captures are shed without a request.
13
+ * - **Fail-quiet (story 34):** an unreachable, timing-out, or refusing endpoint never
14
+ * throws to the host and never grows the buffer without bound (`maxQueueSize`).
15
+ */
16
+ import { AutoReportBackoff } from "@fixback/sdk-core";
17
+ import type { ServerErrorPayload } from "./wire";
18
+ /** The response slice the transport reads. Satisfied by the global `fetch` Response. */
19
+ export interface FetchResponseLike {
20
+ readonly ok: boolean;
21
+ readonly status: number;
22
+ readonly headers?: {
23
+ get(name: string): string | null;
24
+ } | null;
25
+ }
26
+ /** The request init the transport sends — a subset of the standard `RequestInit`. */
27
+ export interface FetchRequestInit {
28
+ readonly method: string;
29
+ readonly headers?: Record<string, string>;
30
+ readonly body?: string;
31
+ }
32
+ /** A `fetch`-shaped function, injectable for tests. */
33
+ export type FetchLike = (url: string, init: FetchRequestInit) => Promise<FetchResponseLike>;
34
+ /**
35
+ * The narrow transport surface the client depends on — buffer one error, flush, and
36
+ * close. {@link Transport} is the real implementation; tests inject a fake recorder.
37
+ */
38
+ export interface CaptureTransport {
39
+ enqueue(payload: ServerErrorPayload): void;
40
+ flush(): Promise<void>;
41
+ close(): Promise<void>;
42
+ }
43
+ /** The transport's configuration (a slice of the resolved SDK config). */
44
+ export interface TransportConfig {
45
+ readonly endpoint: string;
46
+ readonly secretKey: string;
47
+ readonly maxBatchSize: number;
48
+ readonly maxQueueSize: number;
49
+ readonly flushIntervalMs: number;
50
+ readonly timeoutMs: number;
51
+ }
52
+ /** Injectable collaborators, defaulted to the real implementations. */
53
+ export interface TransportDeps {
54
+ readonly fetchImpl?: FetchLike;
55
+ readonly backoff?: AutoReportBackoff;
56
+ }
57
+ export declare class Transport implements CaptureTransport {
58
+ private readonly config;
59
+ private readonly fetchImpl;
60
+ private readonly backoff;
61
+ /** Insertion-ordered buffer keyed by fingerprint, so a coalesce is O(1). */
62
+ private readonly buffer;
63
+ private timer;
64
+ private inFlight;
65
+ private closed;
66
+ constructor(config: TransportConfig, deps?: TransportDeps);
67
+ /**
68
+ * Buffer one captured error for the next flush. Coalesces onto an existing
69
+ * fingerprint (summing occurrences, keeping the first occurrence's evidence);
70
+ * drops the capture while the backoff window is open or the buffer is full; and
71
+ * triggers an immediate flush once the batch cap is reached.
72
+ */
73
+ enqueue(payload: ServerErrorPayload): void;
74
+ /** Flush the buffer to ingest. Never rejects (fail-quiet). Coalesces concurrent calls. */
75
+ flush(): Promise<void>;
76
+ /** Stop the flush timer and drain what remains. Safe to call more than once. */
77
+ close(): Promise<void>;
78
+ /** Send batches until the buffer drains, the window opens, or a send fails. */
79
+ private drain;
80
+ /** Remove and return up to `maxBatchSize` buffered errors, oldest first. */
81
+ private takeBatch;
82
+ /**
83
+ * POST one batch. Returns `true` on acceptance; on a `429` it opens the backoff
84
+ * window from `Retry-After` and returns `false`; any other failure (unreachable,
85
+ * timeout, refusal) returns `false` without throwing.
86
+ */
87
+ private send;
88
+ /** Race a request against the configured timeout (`0` disables it). */
89
+ private withTimeout;
90
+ }
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ /**
3
+ * The **batched secret-key transport** (ADR-0026): the SDK buffers captured errors
4
+ * and flushes them to `POST /api/errors` as a JSON batch authenticated with the
5
+ * Project secret key as an `Authorization: Bearer` token.
6
+ *
7
+ * - **Batched:** a server emits many errors, so they buffer and flush together
8
+ * (on the batch cap, on an interval, on demand, and on `close`).
9
+ * - **Coalesced:** repeats of one fingerprint in a flush window fold into a single
10
+ * item with a summed occurrence count — the client half of the crash-loop defence
11
+ * (the server folds across flushes, ADR-0027).
12
+ * - **Backed off:** a `429` opens the shared-core {@link AutoReportBackoff} window
13
+ * (honouring `Retry-After`); while it is open captures are shed without a request.
14
+ * - **Fail-quiet (story 34):** an unreachable, timing-out, or refusing endpoint never
15
+ * throws to the host and never grows the buffer without bound (`maxQueueSize`).
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.Transport = void 0;
19
+ const sdk_core_1 = require("@fixback/sdk-core");
20
+ /** The global `fetch`, adapted to {@link FetchLike}, or `undefined` when unavailable. */
21
+ function resolveFetch() {
22
+ const g = globalThis;
23
+ if (typeof g.fetch !== "function")
24
+ return undefined;
25
+ const bound = g.fetch.bind(globalThis);
26
+ return (url, init) => bound(url, init);
27
+ }
28
+ /** Read a response header defensively — `null` when unavailable or on any throw. */
29
+ function safeHeader(response, name) {
30
+ try {
31
+ return response.headers?.get?.(name) ?? null;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ class Transport {
38
+ config;
39
+ fetchImpl;
40
+ backoff;
41
+ /** Insertion-ordered buffer keyed by fingerprint, so a coalesce is O(1). */
42
+ buffer = new Map();
43
+ timer;
44
+ inFlight = null;
45
+ closed = false;
46
+ constructor(config, deps = {}) {
47
+ this.config = config;
48
+ this.fetchImpl = deps.fetchImpl ?? resolveFetch();
49
+ this.backoff = deps.backoff ?? new sdk_core_1.AutoReportBackoff();
50
+ if (config.flushIntervalMs > 0) {
51
+ this.timer = setInterval(() => {
52
+ void this.flush();
53
+ }, config.flushIntervalMs);
54
+ // Never keep the process alive for the flush timer.
55
+ const handle = this.timer;
56
+ if (typeof handle.unref === "function")
57
+ handle.unref();
58
+ }
59
+ }
60
+ /**
61
+ * Buffer one captured error for the next flush. Coalesces onto an existing
62
+ * fingerprint (summing occurrences, keeping the first occurrence's evidence);
63
+ * drops the capture while the backoff window is open or the buffer is full; and
64
+ * triggers an immediate flush once the batch cap is reached.
65
+ */
66
+ enqueue(payload) {
67
+ const signature = payload.errorSignature;
68
+ if (this.closed || typeof signature !== "string" || signature.length === 0)
69
+ return;
70
+ // Shed while the 429 window is open — the client half of the flood defence.
71
+ if (this.backoff.isPaused())
72
+ return;
73
+ const existing = this.buffer.get(signature);
74
+ if (existing) {
75
+ existing.occurrences = (existing.occurrences ?? 1) + (payload.occurrences ?? 1);
76
+ }
77
+ else {
78
+ if (this.buffer.size >= this.config.maxQueueSize)
79
+ return; // bounded — drop the newest
80
+ this.buffer.set(signature, { ...payload });
81
+ }
82
+ if (this.buffer.size >= this.config.maxBatchSize)
83
+ void this.flush();
84
+ }
85
+ /** Flush the buffer to ingest. Never rejects (fail-quiet). Coalesces concurrent calls. */
86
+ flush() {
87
+ if (this.inFlight)
88
+ return this.inFlight;
89
+ this.inFlight = this.drain().finally(() => {
90
+ this.inFlight = null;
91
+ });
92
+ return this.inFlight;
93
+ }
94
+ /** Stop the flush timer and drain what remains. Safe to call more than once. */
95
+ async close() {
96
+ this.closed = true;
97
+ if (this.timer !== undefined) {
98
+ clearInterval(this.timer);
99
+ this.timer = undefined;
100
+ }
101
+ await this.flush();
102
+ }
103
+ /** Send batches until the buffer drains, the window opens, or a send fails. */
104
+ async drain() {
105
+ while (this.buffer.size > 0) {
106
+ if (this.backoff.isPaused())
107
+ return;
108
+ const ok = await this.send(this.takeBatch());
109
+ if (!ok)
110
+ return; // 429 (held) or unreachable/refused — stop; the rest waits or is dropped
111
+ }
112
+ }
113
+ /** Remove and return up to `maxBatchSize` buffered errors, oldest first. */
114
+ takeBatch() {
115
+ const batch = [];
116
+ for (const [signature, entry] of this.buffer) {
117
+ batch.push(entry);
118
+ this.buffer.delete(signature);
119
+ if (batch.length >= this.config.maxBatchSize)
120
+ break;
121
+ }
122
+ return batch;
123
+ }
124
+ /**
125
+ * POST one batch. Returns `true` on acceptance; on a `429` it opens the backoff
126
+ * window from `Retry-After` and returns `false`; any other failure (unreachable,
127
+ * timeout, refusal) returns `false` without throwing.
128
+ */
129
+ async send(batch) {
130
+ const doFetch = this.fetchImpl;
131
+ if (!doFetch || batch.length === 0)
132
+ return false;
133
+ let response;
134
+ try {
135
+ response = await this.withTimeout(doFetch(this.config.endpoint, {
136
+ method: "POST",
137
+ headers: {
138
+ authorization: `Bearer ${this.config.secretKey}`,
139
+ "content-type": "application/json",
140
+ },
141
+ body: JSON.stringify({ errors: batch }),
142
+ }));
143
+ }
144
+ catch {
145
+ return false; // unreachable or timed out — fail quiet
146
+ }
147
+ if (!response.ok) {
148
+ if (response.status === 429) {
149
+ this.backoff.hold(safeHeader(response, "retry-after"));
150
+ return false;
151
+ }
152
+ return false; // refused — fail quiet
153
+ }
154
+ return true;
155
+ }
156
+ /** Race a request against the configured timeout (`0` disables it). */
157
+ withTimeout(request) {
158
+ const ms = this.config.timeoutMs;
159
+ if (ms <= 0)
160
+ return request;
161
+ let timer;
162
+ const timeout = new Promise((_, reject) => {
163
+ timer = setTimeout(() => reject(new Error("fixback: request timed out")), ms);
164
+ });
165
+ return Promise.race([request, timeout]).finally(() => {
166
+ if (timer !== undefined)
167
+ clearTimeout(timer);
168
+ });
169
+ }
170
+ }
171
+ exports.Transport = Transport;
172
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The backend SDK's own version string, reported to ingest as
3
+ * `environment.sdkVersion`. Kept as an inlined constant rather than a runtime
4
+ * `package.json` import so the published build stays self-contained (the same
5
+ * posture as `packages/sdk/src/version.ts` and `packages/expo/src/version.ts`).
6
+ *
7
+ * It must equal `package.json`'s `version`. Two things keep it there: the
8
+ * Changesets `version` step syncs it automatically (`scripts/sync-sdk-version.mjs`,
9
+ * wired into `version-packages`), and `version.test.ts` fails the build if the two
10
+ * ever drift. Do not hand-edit this line to a value other than `package.json`'s
11
+ * version.
12
+ */
13
+ export declare const NODE_SDK_VERSION = "0.2.0";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NODE_SDK_VERSION = void 0;
4
+ /**
5
+ * The backend SDK's own version string, reported to ingest as
6
+ * `environment.sdkVersion`. Kept as an inlined constant rather than a runtime
7
+ * `package.json` import so the published build stays self-contained (the same
8
+ * posture as `packages/sdk/src/version.ts` and `packages/expo/src/version.ts`).
9
+ *
10
+ * It must equal `package.json`'s `version`. Two things keep it there: the
11
+ * Changesets `version` step syncs it automatically (`scripts/sync-sdk-version.mjs`,
12
+ * wired into `version-packages`), and `version.test.ts` fails the build if the two
13
+ * ever drift. Do not hand-edit this line to a value other than `package.json`'s
14
+ * version.
15
+ */
16
+ exports.NODE_SDK_VERSION = "0.2.0";
17
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACU,QAAA,gBAAgB,GAAG,OAAO,CAAC"}