@aforoai/metering 1.1.2 → 2.0.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 (70) hide show
  1. package/README.md +46 -28
  2. package/dist/cjs/client.d.ts +32 -50
  3. package/dist/cjs/client.d.ts.map +1 -1
  4. package/dist/cjs/client.js +104 -215
  5. package/dist/cjs/client.js.map +1 -1
  6. package/dist/cjs/middleware/common.d.ts +16 -0
  7. package/dist/cjs/middleware/common.d.ts.map +1 -1
  8. package/dist/cjs/middleware/common.js +54 -0
  9. package/dist/cjs/middleware/common.js.map +1 -1
  10. package/dist/cjs/middleware/express.d.ts +10 -5
  11. package/dist/cjs/middleware/express.d.ts.map +1 -1
  12. package/dist/cjs/middleware/express.js +22 -59
  13. package/dist/cjs/middleware/express.js.map +1 -1
  14. package/dist/cjs/middleware/fastify.d.ts +8 -4
  15. package/dist/cjs/middleware/fastify.d.ts.map +1 -1
  16. package/dist/cjs/middleware/fastify.js +25 -45
  17. package/dist/cjs/middleware/fastify.js.map +1 -1
  18. package/dist/cjs/middleware/koa.d.ts +10 -5
  19. package/dist/cjs/middleware/koa.d.ts.map +1 -1
  20. package/dist/cjs/middleware/koa.js +25 -43
  21. package/dist/cjs/middleware/koa.js.map +1 -1
  22. package/dist/cjs/transport-core/core.d.ts +355 -0
  23. package/dist/cjs/transport-core/core.d.ts.map +1 -0
  24. package/dist/cjs/transport-core/core.js +1047 -0
  25. package/dist/cjs/transport-core/core.js.map +1 -0
  26. package/dist/cjs/types.d.ts +82 -46
  27. package/dist/cjs/types.d.ts.map +1 -1
  28. package/dist/esm/client.js +103 -214
  29. package/dist/esm/client.js.map +1 -1
  30. package/dist/esm/middleware/common.js +51 -0
  31. package/dist/esm/middleware/common.js.map +1 -1
  32. package/dist/esm/middleware/express.js +23 -60
  33. package/dist/esm/middleware/express.js.map +1 -1
  34. package/dist/esm/middleware/fastify.js +26 -46
  35. package/dist/esm/middleware/fastify.js.map +1 -1
  36. package/dist/esm/middleware/koa.js +26 -44
  37. package/dist/esm/middleware/koa.js.map +1 -1
  38. package/dist/esm/transport-core/core.js +1038 -0
  39. package/dist/esm/transport-core/core.js.map +1 -0
  40. package/dist/types/client.d.ts +32 -50
  41. package/dist/types/client.d.ts.map +1 -1
  42. package/dist/types/middleware/common.d.ts +16 -0
  43. package/dist/types/middleware/common.d.ts.map +1 -1
  44. package/dist/types/middleware/express.d.ts +10 -5
  45. package/dist/types/middleware/express.d.ts.map +1 -1
  46. package/dist/types/middleware/fastify.d.ts +8 -4
  47. package/dist/types/middleware/fastify.d.ts.map +1 -1
  48. package/dist/types/middleware/koa.d.ts +10 -5
  49. package/dist/types/middleware/koa.d.ts.map +1 -1
  50. package/dist/types/transport-core/core.d.ts +355 -0
  51. package/dist/types/transport-core/core.d.ts.map +1 -0
  52. package/dist/types/types.d.ts +82 -46
  53. package/dist/types/types.d.ts.map +1 -1
  54. package/package.json +1 -1
  55. package/dist/cjs/buffer.d.ts +0 -33
  56. package/dist/cjs/buffer.d.ts.map +0 -1
  57. package/dist/cjs/buffer.js +0 -83
  58. package/dist/cjs/buffer.js.map +0 -1
  59. package/dist/cjs/transport.d.ts +0 -41
  60. package/dist/cjs/transport.d.ts.map +0 -1
  61. package/dist/cjs/transport.js +0 -186
  62. package/dist/cjs/transport.js.map +0 -1
  63. package/dist/esm/buffer.js +0 -79
  64. package/dist/esm/buffer.js.map +0 -1
  65. package/dist/esm/transport.js +0 -182
  66. package/dist/esm/transport.js.map +0 -1
  67. package/dist/types/buffer.d.ts +0 -33
  68. package/dist/types/buffer.d.ts.map +0 -1
  69. package/dist/types/transport.d.ts +0 -41
  70. package/dist/types/transport.d.ts.map +0 -1
@@ -1,83 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RingBuffer = void 0;
4
- /**
5
- * Bounded ring buffer for usage events.
6
- * Thread-safe for Node.js single-threaded event loop.
7
- * When full, the oldest event is dropped (FIFO overflow).
8
- */
9
- class RingBuffer {
10
- constructor(capacity = 10000) {
11
- this.head = 0;
12
- this.tail = 0;
13
- this.count = 0;
14
- if (capacity < 1)
15
- throw new Error('Buffer capacity must be >= 1');
16
- this.capacity = capacity;
17
- this.items = new Array(capacity);
18
- }
19
- /** Add an event to the buffer. Returns true if added, false if overflow (oldest dropped). */
20
- push(event) {
21
- return this.pushEvict(event) === null;
22
- }
23
- /**
24
- * Add an event to the buffer. On overflow, returns the evicted OLDEST
25
- * event so the caller can surface the drop; returns null when no
26
- * eviction occurred.
27
- */
28
- pushEvict(event) {
29
- let evicted = null;
30
- if (this.count === this.capacity) {
31
- // Buffer full — drop oldest (advance head)
32
- evicted = this.items[this.head];
33
- this.head = (this.head + 1) % this.capacity;
34
- this.count--;
35
- }
36
- this.items[this.tail] = event;
37
- this.tail = (this.tail + 1) % this.capacity;
38
- this.count++;
39
- return evicted;
40
- }
41
- /** Drain all events from the buffer. Returns a new array and clears the buffer. */
42
- drain() {
43
- if (this.count === 0)
44
- return [];
45
- const result = [];
46
- for (let i = 0; i < this.count; i++) {
47
- const idx = (this.head + i) % this.capacity;
48
- result.push(this.items[idx]);
49
- }
50
- this.head = 0;
51
- this.tail = 0;
52
- this.count = 0;
53
- return result;
54
- }
55
- /** Drain up to `max` events from the front of the buffer. */
56
- drainUpTo(max) {
57
- if (this.count === 0 || max <= 0)
58
- return [];
59
- const take = Math.min(max, this.count);
60
- const result = [];
61
- for (let i = 0; i < take; i++) {
62
- const idx = (this.head + i) % this.capacity;
63
- result.push(this.items[idx]);
64
- }
65
- this.head = (this.head + take) % this.capacity;
66
- this.count -= take;
67
- return result;
68
- }
69
- /** Current number of events in the buffer. */
70
- get size() {
71
- return this.count;
72
- }
73
- /** Whether the buffer is empty. */
74
- get isEmpty() {
75
- return this.count === 0;
76
- }
77
- /** Whether the buffer is at capacity. */
78
- get isFull() {
79
- return this.count === this.capacity;
80
- }
81
- }
82
- exports.RingBuffer = RingBuffer;
83
- //# sourceMappingURL=buffer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/buffer.ts"],"names":[],"mappings":"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"}
@@ -1,41 +0,0 @@
1
- import { ResolvedEvent, FlushResult } from './types.js';
2
- export interface TransportOptions {
3
- baseUrl: string;
4
- apiKey: string;
5
- timeout: number;
6
- maxRetries: number;
7
- retryBaseMs: number;
8
- }
9
- /**
10
- * HTTP transport that sends batched usage events to the Aforo ingestor.
11
- *
12
- * - POST /v1/ingest/batch with X-API-Key: {apiKey} (never Authorization: Bearer -- the ingestor parses Bearer as a JWT and rejects the request 401)
13
- * - Retry on 5xx, 408, 429 with exponential backoff
14
- * - Respects Retry-After header on 429
15
- * - No retry on 4xx (bad input); the server's error message is surfaced
16
- * - A 2xx whose body reports per-event failures (`errors[]`) is a partial
17
- * failure: those events are reported back as rejected
18
- * - AbortController timeout per request
19
- */
20
- export declare class Transport {
21
- private readonly baseUrl;
22
- private readonly apiKey;
23
- private readonly timeout;
24
- private readonly maxRetries;
25
- private readonly retryBaseMs;
26
- constructor(options: TransportOptions);
27
- /** Send a batch of events. Returns the number sent and failed. */
28
- send(events: ResolvedEvent[]): Promise<FlushResult>;
29
- /**
30
- * Send a single event in its own request, once, best-effort.
31
- *
32
- * Used for session heartbeats: a heartbeat must never share a batch with
33
- * usage (a large batch takes the high-throughput path, which does not
34
- * intercept heartbeats), and a lost heartbeat is simply superseded by the
35
- * next one -- so there is no retry and every failure is swallowed.
36
- * Resolves to true when the ingestor accepted the request.
37
- */
38
- sendSingleBestEffort(event: ResolvedEvent): Promise<boolean>;
39
- private sleep;
40
- }
41
- //# sourceMappingURL=transport.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAgB,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,OAAO,EAAE,gBAAgB;IAQrC,kEAAkE;IAC5D,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAoFzD;;;;;;;;OAQG;IACG,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBlE,OAAO,CAAC,KAAK;CAGd"}
@@ -1,186 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Transport = void 0;
4
- /**
5
- * HTTP transport that sends batched usage events to the Aforo ingestor.
6
- *
7
- * - POST /v1/ingest/batch with X-API-Key: {apiKey} (never Authorization: Bearer -- the ingestor parses Bearer as a JWT and rejects the request 401)
8
- * - Retry on 5xx, 408, 429 with exponential backoff
9
- * - Respects Retry-After header on 429
10
- * - No retry on 4xx (bad input); the server's error message is surfaced
11
- * - A 2xx whose body reports per-event failures (`errors[]`) is a partial
12
- * failure: those events are reported back as rejected
13
- * - AbortController timeout per request
14
- */
15
- class Transport {
16
- constructor(options) {
17
- this.baseUrl = options.baseUrl.replace(/\/+$/, '');
18
- this.apiKey = options.apiKey;
19
- this.timeout = options.timeout;
20
- this.maxRetries = options.maxRetries;
21
- this.retryBaseMs = options.retryBaseMs;
22
- }
23
- /** Send a batch of events. Returns the number sent and failed. */
24
- async send(events) {
25
- if (events.length === 0)
26
- return { sent: 0, failed: 0 };
27
- const url = `${this.baseUrl}/v1/ingest/batch`;
28
- const body = { events };
29
- const bodyStr = JSON.stringify(body);
30
- for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
31
- try {
32
- const controller = new AbortController();
33
- const timer = setTimeout(() => controller.abort(), this.timeout);
34
- const response = await fetch(url, {
35
- method: 'POST',
36
- headers: {
37
- 'Content-Type': 'application/json',
38
- 'X-API-Key': this.apiKey,
39
- },
40
- body: bodyStr,
41
- signal: controller.signal,
42
- });
43
- clearTimeout(timer);
44
- if (response.ok) {
45
- // A 2xx can still carry per-event rejections (`failed` / `errors[]`).
46
- const rejected = await readRejectedEvents(response, events.length);
47
- if (rejected.count > 0) {
48
- return {
49
- sent: events.length - rejected.count,
50
- failed: rejected.count,
51
- reason: 'rejected',
52
- ...(rejected.identified ? { rejected: rejected.errors } : {}),
53
- ...(rejected.errors[0] ? { message: rejected.errors[0].message } : {}),
54
- };
55
- }
56
- return { sent: events.length, failed: 0 };
57
- }
58
- // 4xx (except 408, 429) — bad input, don't retry
59
- if (response.status >= 400 && response.status < 500
60
- && response.status !== 408 && response.status !== 429) {
61
- const message = await readErrorMessage(response);
62
- return {
63
- sent: 0,
64
- failed: events.length,
65
- reason: 'rejected',
66
- message: `HTTP ${response.status}${message ? `: ${message}` : ''}`,
67
- };
68
- }
69
- // 429 — respect Retry-After header
70
- if (response.status === 429) {
71
- const retryAfter = response.headers.get('Retry-After');
72
- const delayMs = retryAfter
73
- ? (parseInt(retryAfter, 10) || 1) * 1000
74
- : this.retryBaseMs * Math.pow(2, attempt);
75
- if (attempt < this.maxRetries) {
76
- await this.sleep(delayMs);
77
- continue;
78
- }
79
- }
80
- // 5xx or 408 — retry with backoff
81
- if (attempt < this.maxRetries) {
82
- await this.sleep(this.retryBaseMs * Math.pow(2, attempt));
83
- continue;
84
- }
85
- return { sent: 0, failed: events.length, reason: 'retry_exhausted' };
86
- }
87
- catch (err) {
88
- // Network error or abort — retry
89
- if (attempt < this.maxRetries) {
90
- await this.sleep(this.retryBaseMs * Math.pow(2, attempt));
91
- continue;
92
- }
93
- return { sent: 0, failed: events.length, reason: 'retry_exhausted' };
94
- }
95
- }
96
- return { sent: 0, failed: events.length, reason: 'retry_exhausted' };
97
- }
98
- /**
99
- * Send a single event in its own request, once, best-effort.
100
- *
101
- * Used for session heartbeats: a heartbeat must never share a batch with
102
- * usage (a large batch takes the high-throughput path, which does not
103
- * intercept heartbeats), and a lost heartbeat is simply superseded by the
104
- * next one -- so there is no retry and every failure is swallowed.
105
- * Resolves to true when the ingestor accepted the request.
106
- */
107
- async sendSingleBestEffort(event) {
108
- const controller = new AbortController();
109
- const timer = setTimeout(() => controller.abort(), this.timeout);
110
- try {
111
- const body = { events: [event] };
112
- const response = await fetch(`${this.baseUrl}/v1/ingest/batch`, {
113
- method: 'POST',
114
- headers: {
115
- 'Content-Type': 'application/json',
116
- 'X-API-Key': this.apiKey,
117
- },
118
- body: JSON.stringify(body),
119
- signal: controller.signal,
120
- });
121
- return response.ok;
122
- }
123
- catch {
124
- return false;
125
- }
126
- finally {
127
- clearTimeout(timer);
128
- }
129
- }
130
- sleep(ms) {
131
- return new Promise((resolve) => setTimeout(resolve, ms));
132
- }
133
- }
134
- exports.Transport = Transport;
135
- async function readJson(response) {
136
- try {
137
- if (typeof response?.json === 'function')
138
- return unwrapEnvelope(await response.json());
139
- if (typeof response?.text === 'function')
140
- return unwrapEnvelope(JSON.parse(await response.text()));
141
- }
142
- catch {
143
- // Empty or non-JSON body — nothing to report.
144
- }
145
- return undefined;
146
- }
147
- /** Per-event rejections reported inside a 2xx batch response. */
148
- async function readRejectedEvents(response, batchSize) {
149
- const body = await readJson(response);
150
- const none = { count: 0, identified: false, errors: [] };
151
- if (!body || typeof body !== 'object')
152
- return none;
153
- const seen = new Set();
154
- const errors = [];
155
- if (Array.isArray(body.errors)) {
156
- for (const e of body.errors) {
157
- const index = Number(e?.index);
158
- if (!Number.isInteger(index) || index < 0 || index >= batchSize || seen.has(index))
159
- continue;
160
- seen.add(index);
161
- errors.push({ index, message: String(e?.message ?? 'rejected by the ingestor').slice(0, 500) });
162
- }
163
- }
164
- const reported = typeof body.failed === 'number' && body.failed > 0 ? Math.floor(body.failed) : 0;
165
- const count = Math.min(batchSize, Math.max(reported, errors.length));
166
- // Only name events when the indexes account for every reported failure.
167
- return { count, identified: errors.length > 0 && errors.length === count, errors };
168
- }
169
- /** Best-effort server explanation for a rejected request. */
170
- async function readErrorMessage(response) {
171
- const body = await readJson(response);
172
- if (!body || typeof body !== 'object')
173
- return undefined;
174
- const first = Array.isArray(body.errors) ? body.errors[0] : undefined;
175
- const text = first?.message ?? body.detail ?? body.message ?? body.error ?? body.title;
176
- return typeof text === 'string' && text ? text.slice(0, 500) : undefined;
177
- }
178
- /**
179
- * The ingestor wraps every 2xx JSON body in `{success, data, meta}`. Returns
180
- * the inner `data` object when present, else the body unchanged (bare shape).
181
- */
182
- function unwrapEnvelope(body) {
183
- const data = body && typeof body === 'object' && !Array.isArray(body) ? body.data : undefined;
184
- return data && typeof data === 'object' && !Array.isArray(data) ? data : body;
185
- }
186
- //# sourceMappingURL=transport.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/transport.ts"],"names":[],"mappings":"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"}
@@ -1,79 +0,0 @@
1
- /**
2
- * Bounded ring buffer for usage events.
3
- * Thread-safe for Node.js single-threaded event loop.
4
- * When full, the oldest event is dropped (FIFO overflow).
5
- */
6
- export class RingBuffer {
7
- constructor(capacity = 10000) {
8
- this.head = 0;
9
- this.tail = 0;
10
- this.count = 0;
11
- if (capacity < 1)
12
- throw new Error('Buffer capacity must be >= 1');
13
- this.capacity = capacity;
14
- this.items = new Array(capacity);
15
- }
16
- /** Add an event to the buffer. Returns true if added, false if overflow (oldest dropped). */
17
- push(event) {
18
- return this.pushEvict(event) === null;
19
- }
20
- /**
21
- * Add an event to the buffer. On overflow, returns the evicted OLDEST
22
- * event so the caller can surface the drop; returns null when no
23
- * eviction occurred.
24
- */
25
- pushEvict(event) {
26
- let evicted = null;
27
- if (this.count === this.capacity) {
28
- // Buffer full — drop oldest (advance head)
29
- evicted = this.items[this.head];
30
- this.head = (this.head + 1) % this.capacity;
31
- this.count--;
32
- }
33
- this.items[this.tail] = event;
34
- this.tail = (this.tail + 1) % this.capacity;
35
- this.count++;
36
- return evicted;
37
- }
38
- /** Drain all events from the buffer. Returns a new array and clears the buffer. */
39
- drain() {
40
- if (this.count === 0)
41
- return [];
42
- const result = [];
43
- for (let i = 0; i < this.count; i++) {
44
- const idx = (this.head + i) % this.capacity;
45
- result.push(this.items[idx]);
46
- }
47
- this.head = 0;
48
- this.tail = 0;
49
- this.count = 0;
50
- return result;
51
- }
52
- /** Drain up to `max` events from the front of the buffer. */
53
- drainUpTo(max) {
54
- if (this.count === 0 || max <= 0)
55
- return [];
56
- const take = Math.min(max, this.count);
57
- const result = [];
58
- for (let i = 0; i < take; i++) {
59
- const idx = (this.head + i) % this.capacity;
60
- result.push(this.items[idx]);
61
- }
62
- this.head = (this.head + take) % this.capacity;
63
- this.count -= take;
64
- return result;
65
- }
66
- /** Current number of events in the buffer. */
67
- get size() {
68
- return this.count;
69
- }
70
- /** Whether the buffer is empty. */
71
- get isEmpty() {
72
- return this.count === 0;
73
- }
74
- /** Whether the buffer is at capacity. */
75
- get isFull() {
76
- return this.count === this.capacity;
77
- }
78
- }
79
- //# sourceMappingURL=buffer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/buffer.ts"],"names":[],"mappings":"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"}
@@ -1,182 +0,0 @@
1
- /**
2
- * HTTP transport that sends batched usage events to the Aforo ingestor.
3
- *
4
- * - POST /v1/ingest/batch with X-API-Key: {apiKey} (never Authorization: Bearer -- the ingestor parses Bearer as a JWT and rejects the request 401)
5
- * - Retry on 5xx, 408, 429 with exponential backoff
6
- * - Respects Retry-After header on 429
7
- * - No retry on 4xx (bad input); the server's error message is surfaced
8
- * - A 2xx whose body reports per-event failures (`errors[]`) is a partial
9
- * failure: those events are reported back as rejected
10
- * - AbortController timeout per request
11
- */
12
- export class Transport {
13
- constructor(options) {
14
- this.baseUrl = options.baseUrl.replace(/\/+$/, '');
15
- this.apiKey = options.apiKey;
16
- this.timeout = options.timeout;
17
- this.maxRetries = options.maxRetries;
18
- this.retryBaseMs = options.retryBaseMs;
19
- }
20
- /** Send a batch of events. Returns the number sent and failed. */
21
- async send(events) {
22
- if (events.length === 0)
23
- return { sent: 0, failed: 0 };
24
- const url = `${this.baseUrl}/v1/ingest/batch`;
25
- const body = { events };
26
- const bodyStr = JSON.stringify(body);
27
- for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
28
- try {
29
- const controller = new AbortController();
30
- const timer = setTimeout(() => controller.abort(), this.timeout);
31
- const response = await fetch(url, {
32
- method: 'POST',
33
- headers: {
34
- 'Content-Type': 'application/json',
35
- 'X-API-Key': this.apiKey,
36
- },
37
- body: bodyStr,
38
- signal: controller.signal,
39
- });
40
- clearTimeout(timer);
41
- if (response.ok) {
42
- // A 2xx can still carry per-event rejections (`failed` / `errors[]`).
43
- const rejected = await readRejectedEvents(response, events.length);
44
- if (rejected.count > 0) {
45
- return {
46
- sent: events.length - rejected.count,
47
- failed: rejected.count,
48
- reason: 'rejected',
49
- ...(rejected.identified ? { rejected: rejected.errors } : {}),
50
- ...(rejected.errors[0] ? { message: rejected.errors[0].message } : {}),
51
- };
52
- }
53
- return { sent: events.length, failed: 0 };
54
- }
55
- // 4xx (except 408, 429) — bad input, don't retry
56
- if (response.status >= 400 && response.status < 500
57
- && response.status !== 408 && response.status !== 429) {
58
- const message = await readErrorMessage(response);
59
- return {
60
- sent: 0,
61
- failed: events.length,
62
- reason: 'rejected',
63
- message: `HTTP ${response.status}${message ? `: ${message}` : ''}`,
64
- };
65
- }
66
- // 429 — respect Retry-After header
67
- if (response.status === 429) {
68
- const retryAfter = response.headers.get('Retry-After');
69
- const delayMs = retryAfter
70
- ? (parseInt(retryAfter, 10) || 1) * 1000
71
- : this.retryBaseMs * Math.pow(2, attempt);
72
- if (attempt < this.maxRetries) {
73
- await this.sleep(delayMs);
74
- continue;
75
- }
76
- }
77
- // 5xx or 408 — retry with backoff
78
- if (attempt < this.maxRetries) {
79
- await this.sleep(this.retryBaseMs * Math.pow(2, attempt));
80
- continue;
81
- }
82
- return { sent: 0, failed: events.length, reason: 'retry_exhausted' };
83
- }
84
- catch (err) {
85
- // Network error or abort — retry
86
- if (attempt < this.maxRetries) {
87
- await this.sleep(this.retryBaseMs * Math.pow(2, attempt));
88
- continue;
89
- }
90
- return { sent: 0, failed: events.length, reason: 'retry_exhausted' };
91
- }
92
- }
93
- return { sent: 0, failed: events.length, reason: 'retry_exhausted' };
94
- }
95
- /**
96
- * Send a single event in its own request, once, best-effort.
97
- *
98
- * Used for session heartbeats: a heartbeat must never share a batch with
99
- * usage (a large batch takes the high-throughput path, which does not
100
- * intercept heartbeats), and a lost heartbeat is simply superseded by the
101
- * next one -- so there is no retry and every failure is swallowed.
102
- * Resolves to true when the ingestor accepted the request.
103
- */
104
- async sendSingleBestEffort(event) {
105
- const controller = new AbortController();
106
- const timer = setTimeout(() => controller.abort(), this.timeout);
107
- try {
108
- const body = { events: [event] };
109
- const response = await fetch(`${this.baseUrl}/v1/ingest/batch`, {
110
- method: 'POST',
111
- headers: {
112
- 'Content-Type': 'application/json',
113
- 'X-API-Key': this.apiKey,
114
- },
115
- body: JSON.stringify(body),
116
- signal: controller.signal,
117
- });
118
- return response.ok;
119
- }
120
- catch {
121
- return false;
122
- }
123
- finally {
124
- clearTimeout(timer);
125
- }
126
- }
127
- sleep(ms) {
128
- return new Promise((resolve) => setTimeout(resolve, ms));
129
- }
130
- }
131
- async function readJson(response) {
132
- try {
133
- if (typeof response?.json === 'function')
134
- return unwrapEnvelope(await response.json());
135
- if (typeof response?.text === 'function')
136
- return unwrapEnvelope(JSON.parse(await response.text()));
137
- }
138
- catch {
139
- // Empty or non-JSON body — nothing to report.
140
- }
141
- return undefined;
142
- }
143
- /** Per-event rejections reported inside a 2xx batch response. */
144
- async function readRejectedEvents(response, batchSize) {
145
- const body = await readJson(response);
146
- const none = { count: 0, identified: false, errors: [] };
147
- if (!body || typeof body !== 'object')
148
- return none;
149
- const seen = new Set();
150
- const errors = [];
151
- if (Array.isArray(body.errors)) {
152
- for (const e of body.errors) {
153
- const index = Number(e?.index);
154
- if (!Number.isInteger(index) || index < 0 || index >= batchSize || seen.has(index))
155
- continue;
156
- seen.add(index);
157
- errors.push({ index, message: String(e?.message ?? 'rejected by the ingestor').slice(0, 500) });
158
- }
159
- }
160
- const reported = typeof body.failed === 'number' && body.failed > 0 ? Math.floor(body.failed) : 0;
161
- const count = Math.min(batchSize, Math.max(reported, errors.length));
162
- // Only name events when the indexes account for every reported failure.
163
- return { count, identified: errors.length > 0 && errors.length === count, errors };
164
- }
165
- /** Best-effort server explanation for a rejected request. */
166
- async function readErrorMessage(response) {
167
- const body = await readJson(response);
168
- if (!body || typeof body !== 'object')
169
- return undefined;
170
- const first = Array.isArray(body.errors) ? body.errors[0] : undefined;
171
- const text = first?.message ?? body.detail ?? body.message ?? body.error ?? body.title;
172
- return typeof text === 'string' && text ? text.slice(0, 500) : undefined;
173
- }
174
- /**
175
- * The ingestor wraps every 2xx JSON body in `{success, data, meta}`. Returns
176
- * the inner `data` object when present, else the body unchanged (bare shape).
177
- */
178
- function unwrapEnvelope(body) {
179
- const data = body && typeof body === 'object' && !Array.isArray(body) ? body.data : undefined;
180
- return data && typeof data === 'object' && !Array.isArray(data) ? data : body;
181
- }
182
- //# sourceMappingURL=transport.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/transport.ts"],"names":[],"mappings":"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"}
@@ -1,33 +0,0 @@
1
- import { ResolvedEvent } from './types.js';
2
- /**
3
- * Bounded ring buffer for usage events.
4
- * Thread-safe for Node.js single-threaded event loop.
5
- * When full, the oldest event is dropped (FIFO overflow).
6
- */
7
- export declare class RingBuffer {
8
- private readonly items;
9
- private readonly capacity;
10
- private head;
11
- private tail;
12
- private count;
13
- constructor(capacity?: number);
14
- /** Add an event to the buffer. Returns true if added, false if overflow (oldest dropped). */
15
- push(event: ResolvedEvent): boolean;
16
- /**
17
- * Add an event to the buffer. On overflow, returns the evicted OLDEST
18
- * event so the caller can surface the drop; returns null when no
19
- * eviction occurred.
20
- */
21
- pushEvict(event: ResolvedEvent): ResolvedEvent | null;
22
- /** Drain all events from the buffer. Returns a new array and clears the buffer. */
23
- drain(): ResolvedEvent[];
24
- /** Drain up to `max` events from the front of the buffer. */
25
- drainUpTo(max: number): ResolvedEvent[];
26
- /** Current number of events in the buffer. */
27
- get size(): number;
28
- /** Whether the buffer is empty. */
29
- get isEmpty(): boolean;
30
- /** Whether the buffer is at capacity. */
31
- get isFull(): boolean;
32
- }
33
- //# sourceMappingURL=buffer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"buffer.d.ts","sourceRoot":"","sources":["../../src/buffer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;GAIG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAK;gBAEN,QAAQ,GAAE,MAAe;IAMrC,6FAA6F;IAC7F,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;IAInC;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,GAAG,IAAI;IAiBrD,mFAAmF;IACnF,KAAK,IAAI,aAAa,EAAE;IAgBxB,6DAA6D;IAC7D,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,EAAE;IAgBvC,8CAA8C;IAC9C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,mCAAmC;IACnC,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,yCAAyC;IACzC,IAAI,MAAM,IAAI,OAAO,CAEpB;CACF"}