@aforoai/metering 1.0.0 → 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.
- package/README.md +71 -25
- package/dist/cjs/client.d.ts +33 -35
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/client.js +193 -147
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/idempotency.d.ts +9 -9
- package/dist/cjs/idempotency.js +9 -9
- package/dist/cjs/index.d.ts +8 -8
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +20 -19
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/limits.d.ts +47 -7
- package/dist/cjs/limits.d.ts.map +1 -1
- package/dist/cjs/limits.js +65 -10
- package/dist/cjs/limits.js.map +1 -1
- package/dist/cjs/middleware/common.d.ts +24 -2
- package/dist/cjs/middleware/common.d.ts.map +1 -1
- package/dist/cjs/middleware/common.js +66 -4
- package/dist/cjs/middleware/common.js.map +1 -1
- package/dist/cjs/middleware/express.d.ts +11 -6
- package/dist/cjs/middleware/express.d.ts.map +1 -1
- package/dist/cjs/middleware/express.js +25 -62
- package/dist/cjs/middleware/express.js.map +1 -1
- package/dist/cjs/middleware/fastify.d.ts +9 -5
- package/dist/cjs/middleware/fastify.d.ts.map +1 -1
- package/dist/cjs/middleware/fastify.js +28 -48
- package/dist/cjs/middleware/fastify.js.map +1 -1
- package/dist/cjs/middleware/koa.d.ts +11 -6
- package/dist/cjs/middleware/koa.d.ts.map +1 -1
- package/dist/cjs/middleware/koa.js +28 -46
- package/dist/cjs/middleware/koa.js.map +1 -1
- package/dist/cjs/transport-core/core.d.ts +355 -0
- package/dist/cjs/transport-core/core.d.ts.map +1 -0
- package/dist/cjs/transport-core/core.js +1047 -0
- package/dist/cjs/transport-core/core.js.map +1 -0
- package/dist/cjs/types.d.ts +108 -20
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/client.js +191 -145
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/idempotency.js +9 -9
- package/dist/esm/index.js +7 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/limits.js +62 -10
- package/dist/esm/limits.js.map +1 -1
- package/dist/esm/middleware/common.js +62 -4
- package/dist/esm/middleware/common.js.map +1 -1
- package/dist/esm/middleware/express.js +24 -61
- package/dist/esm/middleware/express.js.map +1 -1
- package/dist/esm/middleware/fastify.js +27 -47
- package/dist/esm/middleware/fastify.js.map +1 -1
- package/dist/esm/middleware/koa.js +27 -45
- package/dist/esm/middleware/koa.js.map +1 -1
- package/dist/esm/package.json +1 -0
- package/dist/esm/transport-core/core.js +1038 -0
- package/dist/esm/transport-core/core.js.map +1 -0
- package/dist/types/client.d.ts +33 -35
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/idempotency.d.ts +9 -9
- package/dist/types/index.d.ts +8 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/limits.d.ts +47 -7
- package/dist/types/limits.d.ts.map +1 -1
- package/dist/types/middleware/common.d.ts +24 -2
- package/dist/types/middleware/common.d.ts.map +1 -1
- package/dist/types/middleware/express.d.ts +11 -6
- package/dist/types/middleware/express.d.ts.map +1 -1
- package/dist/types/middleware/fastify.d.ts +9 -5
- package/dist/types/middleware/fastify.d.ts.map +1 -1
- package/dist/types/middleware/koa.d.ts +11 -6
- package/dist/types/middleware/koa.d.ts.map +1 -1
- package/dist/types/transport-core/core.d.ts +355 -0
- package/dist/types/transport-core/core.d.ts.map +1 -0
- package/dist/types/types.d.ts +108 -20
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +22 -6
- package/dist/cjs/buffer.d.ts +0 -27
- package/dist/cjs/buffer.d.ts.map +0 -1
- package/dist/cjs/buffer.js +0 -75
- package/dist/cjs/buffer.js.map +0 -1
- package/dist/cjs/transport.d.ts +0 -39
- package/dist/cjs/transport.d.ts.map +0 -1
- package/dist/cjs/transport.js +0 -116
- package/dist/cjs/transport.js.map +0 -1
- package/dist/esm/buffer.js +0 -71
- package/dist/esm/buffer.js.map +0 -1
- package/dist/esm/transport.js +0 -112
- package/dist/esm/transport.js.map +0 -1
- package/dist/types/buffer.d.ts +0 -27
- package/dist/types/buffer.d.ts.map +0 -1
- package/dist/types/transport.d.ts +0 -39
- package/dist/types/transport.d.ts.map +0 -1
package/dist/cjs/buffer.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { ResolvedEvent } from './types';
|
|
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
|
-
/** Drain all events from the buffer. Returns a new array and clears the buffer. */
|
|
17
|
-
drain(): ResolvedEvent[];
|
|
18
|
-
/** Drain up to `max` events from the front of the buffer. */
|
|
19
|
-
drainUpTo(max: number): ResolvedEvent[];
|
|
20
|
-
/** Current number of events in the buffer. */
|
|
21
|
-
get size(): number;
|
|
22
|
-
/** Whether the buffer is empty. */
|
|
23
|
-
get isEmpty(): boolean;
|
|
24
|
-
/** Whether the buffer is at capacity. */
|
|
25
|
-
get isFull(): boolean;
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=buffer.d.ts.map
|
package/dist/cjs/buffer.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"buffer.d.ts","sourceRoot":"","sources":["../../src/buffer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC;;;;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;IAiBnC,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"}
|
package/dist/cjs/buffer.js
DELETED
|
@@ -1,75 +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
|
-
let overflow = false;
|
|
22
|
-
if (this.count === this.capacity) {
|
|
23
|
-
// Buffer full — drop oldest (advance head)
|
|
24
|
-
this.head = (this.head + 1) % this.capacity;
|
|
25
|
-
this.count--;
|
|
26
|
-
overflow = true;
|
|
27
|
-
}
|
|
28
|
-
this.items[this.tail] = event;
|
|
29
|
-
this.tail = (this.tail + 1) % this.capacity;
|
|
30
|
-
this.count++;
|
|
31
|
-
return !overflow;
|
|
32
|
-
}
|
|
33
|
-
/** Drain all events from the buffer. Returns a new array and clears the buffer. */
|
|
34
|
-
drain() {
|
|
35
|
-
if (this.count === 0)
|
|
36
|
-
return [];
|
|
37
|
-
const result = [];
|
|
38
|
-
for (let i = 0; i < this.count; i++) {
|
|
39
|
-
const idx = (this.head + i) % this.capacity;
|
|
40
|
-
result.push(this.items[idx]);
|
|
41
|
-
}
|
|
42
|
-
this.head = 0;
|
|
43
|
-
this.tail = 0;
|
|
44
|
-
this.count = 0;
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
/** Drain up to `max` events from the front of the buffer. */
|
|
48
|
-
drainUpTo(max) {
|
|
49
|
-
if (this.count === 0 || max <= 0)
|
|
50
|
-
return [];
|
|
51
|
-
const take = Math.min(max, this.count);
|
|
52
|
-
const result = [];
|
|
53
|
-
for (let i = 0; i < take; i++) {
|
|
54
|
-
const idx = (this.head + i) % this.capacity;
|
|
55
|
-
result.push(this.items[idx]);
|
|
56
|
-
}
|
|
57
|
-
this.head = (this.head + take) % this.capacity;
|
|
58
|
-
this.count -= take;
|
|
59
|
-
return result;
|
|
60
|
-
}
|
|
61
|
-
/** Current number of events in the buffer. */
|
|
62
|
-
get size() {
|
|
63
|
-
return this.count;
|
|
64
|
-
}
|
|
65
|
-
/** Whether the buffer is empty. */
|
|
66
|
-
get isEmpty() {
|
|
67
|
-
return this.count === 0;
|
|
68
|
-
}
|
|
69
|
-
/** Whether the buffer is at capacity. */
|
|
70
|
-
get isFull() {
|
|
71
|
-
return this.count === this.capacity;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
exports.RingBuffer = RingBuffer;
|
|
75
|
-
//# sourceMappingURL=buffer.js.map
|
package/dist/cjs/buffer.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/buffer.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/transport.d.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { ResolvedEvent, FlushResult } from './types';
|
|
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)
|
|
16
|
-
* - AbortController timeout per request
|
|
17
|
-
*/
|
|
18
|
-
export declare class Transport {
|
|
19
|
-
private readonly baseUrl;
|
|
20
|
-
private readonly apiKey;
|
|
21
|
-
private readonly timeout;
|
|
22
|
-
private readonly maxRetries;
|
|
23
|
-
private readonly retryBaseMs;
|
|
24
|
-
constructor(options: TransportOptions);
|
|
25
|
-
/** Send a batch of events. Returns the number sent and failed. */
|
|
26
|
-
send(events: ResolvedEvent[]): Promise<FlushResult>;
|
|
27
|
-
/**
|
|
28
|
-
* Send a single event in its own request, once, best-effort.
|
|
29
|
-
*
|
|
30
|
-
* Used for session heartbeats: a heartbeat must never share a batch with
|
|
31
|
-
* usage (a large batch takes the high-throughput path, which does not
|
|
32
|
-
* intercept heartbeats), and a lost heartbeat is simply superseded by the
|
|
33
|
-
* next one -- so there is no retry and every failure is swallowed.
|
|
34
|
-
* Resolves to true when the ingestor accepted the request.
|
|
35
|
-
*/
|
|
36
|
-
sendSingleBestEffort(event: ResolvedEvent): Promise<boolean>;
|
|
37
|
-
private sleep;
|
|
38
|
-
}
|
|
39
|
-
//# 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,SAAS,CAAC;AAEnE,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;;;;;;;;GAQG;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;IAmEzD;;;;;;;;OAQG;IACG,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBlE,OAAO,CAAC,KAAK;CAGd"}
|
package/dist/cjs/transport.js
DELETED
|
@@ -1,116 +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)
|
|
11
|
-
* - AbortController timeout per request
|
|
12
|
-
*/
|
|
13
|
-
class Transport {
|
|
14
|
-
constructor(options) {
|
|
15
|
-
this.baseUrl = options.baseUrl.replace(/\/+$/, '');
|
|
16
|
-
this.apiKey = options.apiKey;
|
|
17
|
-
this.timeout = options.timeout;
|
|
18
|
-
this.maxRetries = options.maxRetries;
|
|
19
|
-
this.retryBaseMs = options.retryBaseMs;
|
|
20
|
-
}
|
|
21
|
-
/** Send a batch of events. Returns the number sent and failed. */
|
|
22
|
-
async send(events) {
|
|
23
|
-
if (events.length === 0)
|
|
24
|
-
return { sent: 0, failed: 0 };
|
|
25
|
-
const url = `${this.baseUrl}/v1/ingest/batch`;
|
|
26
|
-
const body = { events };
|
|
27
|
-
const bodyStr = JSON.stringify(body);
|
|
28
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
29
|
-
try {
|
|
30
|
-
const controller = new AbortController();
|
|
31
|
-
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
32
|
-
const response = await fetch(url, {
|
|
33
|
-
method: 'POST',
|
|
34
|
-
headers: {
|
|
35
|
-
'Content-Type': 'application/json',
|
|
36
|
-
'X-API-Key': this.apiKey,
|
|
37
|
-
},
|
|
38
|
-
body: bodyStr,
|
|
39
|
-
signal: controller.signal,
|
|
40
|
-
});
|
|
41
|
-
clearTimeout(timer);
|
|
42
|
-
if (response.ok) {
|
|
43
|
-
return { sent: events.length, failed: 0 };
|
|
44
|
-
}
|
|
45
|
-
// 4xx (except 408, 429) — bad input, don't retry
|
|
46
|
-
if (response.status >= 400 && response.status < 500
|
|
47
|
-
&& response.status !== 408 && response.status !== 429) {
|
|
48
|
-
return { sent: 0, failed: events.length };
|
|
49
|
-
}
|
|
50
|
-
// 429 — respect Retry-After header
|
|
51
|
-
if (response.status === 429) {
|
|
52
|
-
const retryAfter = response.headers.get('Retry-After');
|
|
53
|
-
const delayMs = retryAfter
|
|
54
|
-
? (parseInt(retryAfter, 10) || 1) * 1000
|
|
55
|
-
: this.retryBaseMs * Math.pow(2, attempt);
|
|
56
|
-
if (attempt < this.maxRetries) {
|
|
57
|
-
await this.sleep(delayMs);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
// 5xx or 408 — retry with backoff
|
|
62
|
-
if (attempt < this.maxRetries) {
|
|
63
|
-
await this.sleep(this.retryBaseMs * Math.pow(2, attempt));
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
return { sent: 0, failed: events.length };
|
|
67
|
-
}
|
|
68
|
-
catch (err) {
|
|
69
|
-
// Network error or abort — retry
|
|
70
|
-
if (attempt < this.maxRetries) {
|
|
71
|
-
await this.sleep(this.retryBaseMs * Math.pow(2, attempt));
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
return { sent: 0, failed: events.length };
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return { sent: 0, failed: events.length };
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Send a single event in its own request, once, best-effort.
|
|
81
|
-
*
|
|
82
|
-
* Used for session heartbeats: a heartbeat must never share a batch with
|
|
83
|
-
* usage (a large batch takes the high-throughput path, which does not
|
|
84
|
-
* intercept heartbeats), and a lost heartbeat is simply superseded by the
|
|
85
|
-
* next one -- so there is no retry and every failure is swallowed.
|
|
86
|
-
* Resolves to true when the ingestor accepted the request.
|
|
87
|
-
*/
|
|
88
|
-
async sendSingleBestEffort(event) {
|
|
89
|
-
const controller = new AbortController();
|
|
90
|
-
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
91
|
-
try {
|
|
92
|
-
const body = { events: [event] };
|
|
93
|
-
const response = await fetch(`${this.baseUrl}/v1/ingest/batch`, {
|
|
94
|
-
method: 'POST',
|
|
95
|
-
headers: {
|
|
96
|
-
'Content-Type': 'application/json',
|
|
97
|
-
'X-API-Key': this.apiKey,
|
|
98
|
-
},
|
|
99
|
-
body: JSON.stringify(body),
|
|
100
|
-
signal: controller.signal,
|
|
101
|
-
});
|
|
102
|
-
return response.ok;
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
finally {
|
|
108
|
-
clearTimeout(timer);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
sleep(ms) {
|
|
112
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
exports.Transport = Transport;
|
|
116
|
-
//# sourceMappingURL=transport.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/buffer.js
DELETED
|
@@ -1,71 +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
|
-
let overflow = false;
|
|
19
|
-
if (this.count === this.capacity) {
|
|
20
|
-
// Buffer full — drop oldest (advance head)
|
|
21
|
-
this.head = (this.head + 1) % this.capacity;
|
|
22
|
-
this.count--;
|
|
23
|
-
overflow = true;
|
|
24
|
-
}
|
|
25
|
-
this.items[this.tail] = event;
|
|
26
|
-
this.tail = (this.tail + 1) % this.capacity;
|
|
27
|
-
this.count++;
|
|
28
|
-
return !overflow;
|
|
29
|
-
}
|
|
30
|
-
/** Drain all events from the buffer. Returns a new array and clears the buffer. */
|
|
31
|
-
drain() {
|
|
32
|
-
if (this.count === 0)
|
|
33
|
-
return [];
|
|
34
|
-
const result = [];
|
|
35
|
-
for (let i = 0; i < this.count; i++) {
|
|
36
|
-
const idx = (this.head + i) % this.capacity;
|
|
37
|
-
result.push(this.items[idx]);
|
|
38
|
-
}
|
|
39
|
-
this.head = 0;
|
|
40
|
-
this.tail = 0;
|
|
41
|
-
this.count = 0;
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
44
|
-
/** Drain up to `max` events from the front of the buffer. */
|
|
45
|
-
drainUpTo(max) {
|
|
46
|
-
if (this.count === 0 || max <= 0)
|
|
47
|
-
return [];
|
|
48
|
-
const take = Math.min(max, this.count);
|
|
49
|
-
const result = [];
|
|
50
|
-
for (let i = 0; i < take; i++) {
|
|
51
|
-
const idx = (this.head + i) % this.capacity;
|
|
52
|
-
result.push(this.items[idx]);
|
|
53
|
-
}
|
|
54
|
-
this.head = (this.head + take) % this.capacity;
|
|
55
|
-
this.count -= take;
|
|
56
|
-
return result;
|
|
57
|
-
}
|
|
58
|
-
/** Current number of events in the buffer. */
|
|
59
|
-
get size() {
|
|
60
|
-
return this.count;
|
|
61
|
-
}
|
|
62
|
-
/** Whether the buffer is empty. */
|
|
63
|
-
get isEmpty() {
|
|
64
|
-
return this.count === 0;
|
|
65
|
-
}
|
|
66
|
-
/** Whether the buffer is at capacity. */
|
|
67
|
-
get isFull() {
|
|
68
|
-
return this.count === this.capacity;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
//# sourceMappingURL=buffer.js.map
|
package/dist/esm/buffer.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/buffer.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/transport.js
DELETED
|
@@ -1,112 +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)
|
|
8
|
-
* - AbortController timeout per request
|
|
9
|
-
*/
|
|
10
|
-
export class Transport {
|
|
11
|
-
constructor(options) {
|
|
12
|
-
this.baseUrl = options.baseUrl.replace(/\/+$/, '');
|
|
13
|
-
this.apiKey = options.apiKey;
|
|
14
|
-
this.timeout = options.timeout;
|
|
15
|
-
this.maxRetries = options.maxRetries;
|
|
16
|
-
this.retryBaseMs = options.retryBaseMs;
|
|
17
|
-
}
|
|
18
|
-
/** Send a batch of events. Returns the number sent and failed. */
|
|
19
|
-
async send(events) {
|
|
20
|
-
if (events.length === 0)
|
|
21
|
-
return { sent: 0, failed: 0 };
|
|
22
|
-
const url = `${this.baseUrl}/v1/ingest/batch`;
|
|
23
|
-
const body = { events };
|
|
24
|
-
const bodyStr = JSON.stringify(body);
|
|
25
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
26
|
-
try {
|
|
27
|
-
const controller = new AbortController();
|
|
28
|
-
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
29
|
-
const response = await fetch(url, {
|
|
30
|
-
method: 'POST',
|
|
31
|
-
headers: {
|
|
32
|
-
'Content-Type': 'application/json',
|
|
33
|
-
'X-API-Key': this.apiKey,
|
|
34
|
-
},
|
|
35
|
-
body: bodyStr,
|
|
36
|
-
signal: controller.signal,
|
|
37
|
-
});
|
|
38
|
-
clearTimeout(timer);
|
|
39
|
-
if (response.ok) {
|
|
40
|
-
return { sent: events.length, failed: 0 };
|
|
41
|
-
}
|
|
42
|
-
// 4xx (except 408, 429) — bad input, don't retry
|
|
43
|
-
if (response.status >= 400 && response.status < 500
|
|
44
|
-
&& response.status !== 408 && response.status !== 429) {
|
|
45
|
-
return { sent: 0, failed: events.length };
|
|
46
|
-
}
|
|
47
|
-
// 429 — respect Retry-After header
|
|
48
|
-
if (response.status === 429) {
|
|
49
|
-
const retryAfter = response.headers.get('Retry-After');
|
|
50
|
-
const delayMs = retryAfter
|
|
51
|
-
? (parseInt(retryAfter, 10) || 1) * 1000
|
|
52
|
-
: this.retryBaseMs * Math.pow(2, attempt);
|
|
53
|
-
if (attempt < this.maxRetries) {
|
|
54
|
-
await this.sleep(delayMs);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// 5xx or 408 — retry with backoff
|
|
59
|
-
if (attempt < this.maxRetries) {
|
|
60
|
-
await this.sleep(this.retryBaseMs * Math.pow(2, attempt));
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
return { sent: 0, failed: events.length };
|
|
64
|
-
}
|
|
65
|
-
catch (err) {
|
|
66
|
-
// Network error or abort — retry
|
|
67
|
-
if (attempt < this.maxRetries) {
|
|
68
|
-
await this.sleep(this.retryBaseMs * Math.pow(2, attempt));
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
return { sent: 0, failed: events.length };
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return { sent: 0, failed: events.length };
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Send a single event in its own request, once, best-effort.
|
|
78
|
-
*
|
|
79
|
-
* Used for session heartbeats: a heartbeat must never share a batch with
|
|
80
|
-
* usage (a large batch takes the high-throughput path, which does not
|
|
81
|
-
* intercept heartbeats), and a lost heartbeat is simply superseded by the
|
|
82
|
-
* next one -- so there is no retry and every failure is swallowed.
|
|
83
|
-
* Resolves to true when the ingestor accepted the request.
|
|
84
|
-
*/
|
|
85
|
-
async sendSingleBestEffort(event) {
|
|
86
|
-
const controller = new AbortController();
|
|
87
|
-
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
88
|
-
try {
|
|
89
|
-
const body = { events: [event] };
|
|
90
|
-
const response = await fetch(`${this.baseUrl}/v1/ingest/batch`, {
|
|
91
|
-
method: 'POST',
|
|
92
|
-
headers: {
|
|
93
|
-
'Content-Type': 'application/json',
|
|
94
|
-
'X-API-Key': this.apiKey,
|
|
95
|
-
},
|
|
96
|
-
body: JSON.stringify(body),
|
|
97
|
-
signal: controller.signal,
|
|
98
|
-
});
|
|
99
|
-
return response.ok;
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
finally {
|
|
105
|
-
clearTimeout(timer);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
sleep(ms) {
|
|
109
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
//# sourceMappingURL=transport.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/types/buffer.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { ResolvedEvent } from './types';
|
|
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
|
-
/** Drain all events from the buffer. Returns a new array and clears the buffer. */
|
|
17
|
-
drain(): ResolvedEvent[];
|
|
18
|
-
/** Drain up to `max` events from the front of the buffer. */
|
|
19
|
-
drainUpTo(max: number): ResolvedEvent[];
|
|
20
|
-
/** Current number of events in the buffer. */
|
|
21
|
-
get size(): number;
|
|
22
|
-
/** Whether the buffer is empty. */
|
|
23
|
-
get isEmpty(): boolean;
|
|
24
|
-
/** Whether the buffer is at capacity. */
|
|
25
|
-
get isFull(): boolean;
|
|
26
|
-
}
|
|
27
|
-
//# 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,SAAS,CAAC;AAExC;;;;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;IAiBnC,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"}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { ResolvedEvent, FlushResult } from './types';
|
|
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)
|
|
16
|
-
* - AbortController timeout per request
|
|
17
|
-
*/
|
|
18
|
-
export declare class Transport {
|
|
19
|
-
private readonly baseUrl;
|
|
20
|
-
private readonly apiKey;
|
|
21
|
-
private readonly timeout;
|
|
22
|
-
private readonly maxRetries;
|
|
23
|
-
private readonly retryBaseMs;
|
|
24
|
-
constructor(options: TransportOptions);
|
|
25
|
-
/** Send a batch of events. Returns the number sent and failed. */
|
|
26
|
-
send(events: ResolvedEvent[]): Promise<FlushResult>;
|
|
27
|
-
/**
|
|
28
|
-
* Send a single event in its own request, once, best-effort.
|
|
29
|
-
*
|
|
30
|
-
* Used for session heartbeats: a heartbeat must never share a batch with
|
|
31
|
-
* usage (a large batch takes the high-throughput path, which does not
|
|
32
|
-
* intercept heartbeats), and a lost heartbeat is simply superseded by the
|
|
33
|
-
* next one -- so there is no retry and every failure is swallowed.
|
|
34
|
-
* Resolves to true when the ingestor accepted the request.
|
|
35
|
-
*/
|
|
36
|
-
sendSingleBestEffort(event: ResolvedEvent): Promise<boolean>;
|
|
37
|
-
private sleep;
|
|
38
|
-
}
|
|
39
|
-
//# 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,SAAS,CAAC;AAEnE,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;;;;;;;;GAQG;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;IAmEzD;;;;;;;;OAQG;IACG,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBlE,OAAO,CAAC,KAAK;CAGd"}
|