@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.
Files changed (91) hide show
  1. package/README.md +71 -25
  2. package/dist/cjs/client.d.ts +33 -35
  3. package/dist/cjs/client.d.ts.map +1 -1
  4. package/dist/cjs/client.js +193 -147
  5. package/dist/cjs/client.js.map +1 -1
  6. package/dist/cjs/idempotency.d.ts +9 -9
  7. package/dist/cjs/idempotency.js +9 -9
  8. package/dist/cjs/index.d.ts +8 -8
  9. package/dist/cjs/index.d.ts.map +1 -1
  10. package/dist/cjs/index.js +20 -19
  11. package/dist/cjs/index.js.map +1 -1
  12. package/dist/cjs/limits.d.ts +47 -7
  13. package/dist/cjs/limits.d.ts.map +1 -1
  14. package/dist/cjs/limits.js +65 -10
  15. package/dist/cjs/limits.js.map +1 -1
  16. package/dist/cjs/middleware/common.d.ts +24 -2
  17. package/dist/cjs/middleware/common.d.ts.map +1 -1
  18. package/dist/cjs/middleware/common.js +66 -4
  19. package/dist/cjs/middleware/common.js.map +1 -1
  20. package/dist/cjs/middleware/express.d.ts +11 -6
  21. package/dist/cjs/middleware/express.d.ts.map +1 -1
  22. package/dist/cjs/middleware/express.js +25 -62
  23. package/dist/cjs/middleware/express.js.map +1 -1
  24. package/dist/cjs/middleware/fastify.d.ts +9 -5
  25. package/dist/cjs/middleware/fastify.d.ts.map +1 -1
  26. package/dist/cjs/middleware/fastify.js +28 -48
  27. package/dist/cjs/middleware/fastify.js.map +1 -1
  28. package/dist/cjs/middleware/koa.d.ts +11 -6
  29. package/dist/cjs/middleware/koa.d.ts.map +1 -1
  30. package/dist/cjs/middleware/koa.js +28 -46
  31. package/dist/cjs/middleware/koa.js.map +1 -1
  32. package/dist/cjs/transport-core/core.d.ts +355 -0
  33. package/dist/cjs/transport-core/core.d.ts.map +1 -0
  34. package/dist/cjs/transport-core/core.js +1047 -0
  35. package/dist/cjs/transport-core/core.js.map +1 -0
  36. package/dist/cjs/types.d.ts +108 -20
  37. package/dist/cjs/types.d.ts.map +1 -1
  38. package/dist/esm/client.js +191 -145
  39. package/dist/esm/client.js.map +1 -1
  40. package/dist/esm/idempotency.js +9 -9
  41. package/dist/esm/index.js +7 -7
  42. package/dist/esm/index.js.map +1 -1
  43. package/dist/esm/limits.js +62 -10
  44. package/dist/esm/limits.js.map +1 -1
  45. package/dist/esm/middleware/common.js +62 -4
  46. package/dist/esm/middleware/common.js.map +1 -1
  47. package/dist/esm/middleware/express.js +24 -61
  48. package/dist/esm/middleware/express.js.map +1 -1
  49. package/dist/esm/middleware/fastify.js +27 -47
  50. package/dist/esm/middleware/fastify.js.map +1 -1
  51. package/dist/esm/middleware/koa.js +27 -45
  52. package/dist/esm/middleware/koa.js.map +1 -1
  53. package/dist/esm/package.json +1 -0
  54. package/dist/esm/transport-core/core.js +1038 -0
  55. package/dist/esm/transport-core/core.js.map +1 -0
  56. package/dist/types/client.d.ts +33 -35
  57. package/dist/types/client.d.ts.map +1 -1
  58. package/dist/types/idempotency.d.ts +9 -9
  59. package/dist/types/index.d.ts +8 -8
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/dist/types/limits.d.ts +47 -7
  62. package/dist/types/limits.d.ts.map +1 -1
  63. package/dist/types/middleware/common.d.ts +24 -2
  64. package/dist/types/middleware/common.d.ts.map +1 -1
  65. package/dist/types/middleware/express.d.ts +11 -6
  66. package/dist/types/middleware/express.d.ts.map +1 -1
  67. package/dist/types/middleware/fastify.d.ts +9 -5
  68. package/dist/types/middleware/fastify.d.ts.map +1 -1
  69. package/dist/types/middleware/koa.d.ts +11 -6
  70. package/dist/types/middleware/koa.d.ts.map +1 -1
  71. package/dist/types/transport-core/core.d.ts +355 -0
  72. package/dist/types/transport-core/core.d.ts.map +1 -0
  73. package/dist/types/types.d.ts +108 -20
  74. package/dist/types/types.d.ts.map +1 -1
  75. package/package.json +22 -6
  76. package/dist/cjs/buffer.d.ts +0 -27
  77. package/dist/cjs/buffer.d.ts.map +0 -1
  78. package/dist/cjs/buffer.js +0 -75
  79. package/dist/cjs/buffer.js.map +0 -1
  80. package/dist/cjs/transport.d.ts +0 -39
  81. package/dist/cjs/transport.d.ts.map +0 -1
  82. package/dist/cjs/transport.js +0 -116
  83. package/dist/cjs/transport.js.map +0 -1
  84. package/dist/esm/buffer.js +0 -71
  85. package/dist/esm/buffer.js.map +0 -1
  86. package/dist/esm/transport.js +0 -112
  87. package/dist/esm/transport.js.map +0 -1
  88. package/dist/types/buffer.d.ts +0 -27
  89. package/dist/types/buffer.d.ts.map +0 -1
  90. package/dist/types/transport.d.ts +0 -39
  91. package/dist/types/transport.d.ts.map +0 -1
@@ -5,8 +5,10 @@
5
5
  * billed. The server reports it per event (`errors[]` in the batch response,
6
6
  * indexed), so the rest of the batch survives — but the SDK sends in the
7
7
  * background, so that report reaches nobody: the usage is simply gone. Checking
8
- * the same limits at `track()` surfaces the problem to the caller, at the call
9
- * site that produced it, while the event can still be corrected.
8
+ * the same limits at `track()` reports the problem where it happens: the event
9
+ * is not buffered or sent, it is counted in `droppedCount`, WARN-logged with
10
+ * the field, the limit and the offending value, and handed to the opt-in
11
+ * `onDrop` hook with reason `'invalid'`. `track()` does not throw for it.
10
12
  *
11
13
  * Source: `dto/IngestUsageEventRequest` (aforo-nextgen-usage-ingestor-service) —
12
14
  * the `@Size` and `@Digits` bean constraints, which are compiled into the
@@ -21,9 +23,15 @@
21
23
  * Only `occurredAt`'s shape is checked, since a malformed timestamp is invalid
22
24
  * under every configuration.
23
25
  *
24
- * Nothing here truncates or rounds — that would change what is billed. The
25
- * offending event is rejected instead, naming the field, the limit, and the
26
- * offending value.
26
+ * The limit check never truncates or rounds a value the caller set — that would
27
+ * change what is billed. The offending event is dropped instead (reason
28
+ * `'invalid'`), naming the field, the limit, and the offending value.
29
+ *
30
+ * The one exception is a label the SDK itself reads off an incoming request
31
+ * (the middlewares' `endpointPath` and `httpMethod`): dropping there would let
32
+ * an API consumer avoid metering by sending an over-long path. Those are cut to
33
+ * the limit where they are derived (`truncateRequestLabel`) and the event is
34
+ * still sent.
27
35
  */
28
36
  /** Maximum characters the ingestor accepts per string field (`@Size(max = ...)`). */
29
37
  export declare const MAX_LENGTHS: {
@@ -36,12 +44,35 @@ export declare const MAX_LENGTHS: {
36
44
  readonly sessionId: 64;
37
45
  readonly agentId: 36;
38
46
  readonly toolName: 64;
47
+ readonly capabilityName: 64;
39
48
  readonly endpointPath: 512;
40
49
  readonly httpMethod: 16;
50
+ readonly subscriptionId: 64;
51
+ readonly grpcService: 255;
52
+ readonly grpcMethod: 128;
53
+ readonly gqlOperationName: 255;
54
+ readonly wsConnectionId: 64;
55
+ readonly mqttTopic: 500;
56
+ readonly mqttClientId: 128;
41
57
  };
42
58
  /** `@Digits(integer = 14, fraction = 6)` — `usage_events.quantity` is NUMERIC(20,6). */
43
59
  export declare const MAX_QUANTITY_INTEGER_DIGITS = 14;
44
60
  export declare const MAX_QUANTITY_DECIMAL_PLACES = 6;
61
+ /** Fields whose length the ingestor caps, checked when the caller sets them. */
62
+ type LengthCheckedField = keyof typeof MAX_LENGTHS;
63
+ /**
64
+ * Cut `value` to at most `max` UTF-16 code units — how the ingestor counts —
65
+ * without leaving half a surrogate pair at the end.
66
+ */
67
+ export declare function truncateToLimit(value: string, max: number): string;
68
+ /**
69
+ * Bound a label the SDK derived from an incoming request to the ingestor's
70
+ * limit for `field`. Logs one WARN per field per process. Not for values the
71
+ * SDK caller sets: those are never altered (an over-long one drops the event).
72
+ */
73
+ export declare function truncateRequestLabel(field: LengthCheckedField, value: string): string;
74
+ /** Test hook: forget which labels were already reported as truncated. */
75
+ export declare function resetTruncationWarnings(): void;
45
76
  /**
46
77
  * Integer and decimal digit counts as the ingestor's `@Digits` sees them, i.e.
47
78
  * as `BigDecimal` reads the number the SDK puts on the wire:
@@ -70,11 +101,20 @@ export interface LimitCheckedEvent {
70
101
  sessionId?: unknown;
71
102
  agentId?: unknown;
72
103
  toolName?: unknown;
104
+ capabilityName?: unknown;
105
+ subscriptionId?: unknown;
106
+ grpcService?: unknown;
107
+ grpcMethod?: unknown;
108
+ gqlOperationName?: unknown;
109
+ wsConnectionId?: unknown;
110
+ mqttTopic?: unknown;
111
+ mqttClientId?: unknown;
73
112
  }
74
113
  /**
75
114
  * Describe the first ingestor constraint this event breaks, or `null` when it
76
- * would be accepted. Callers that must not throw (the framework middlewares)
77
- * can use this to skip an event instead.
115
+ * would be accepted. `AforoClient.track()` uses it to drop an invalid event
116
+ * (reason `'invalid'`) before it is buffered.
78
117
  */
79
118
  export declare function describeLimitViolation(event: LimitCheckedEvent): string | null;
119
+ export {};
80
120
  //# sourceMappingURL=limits.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,qFAAqF;AACrF,eAAO,MAAM,WAAW;;;;;;;;;;;;CAYd,CAAC;AAEX,wFAAwF;AACxF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAoC7C;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAYnG;AA+BD,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAe9E"}
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,qFAAqF;AACrF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;CAoBd,CAAC;AAEX,wFAAwF;AACxF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAc7C,gFAAgF;AAChF,KAAK,kBAAkB,GAAG,MAAM,OAAO,WAAW,CAAC;AAUnD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAOlE;AAKD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAWrF;AAED,yEAAyE;AACzE,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAaD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAYnG;AA+BD,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAe9E"}
@@ -6,8 +6,10 @@
6
6
  * billed. The server reports it per event (`errors[]` in the batch response,
7
7
  * indexed), so the rest of the batch survives — but the SDK sends in the
8
8
  * background, so that report reaches nobody: the usage is simply gone. Checking
9
- * the same limits at `track()` surfaces the problem to the caller, at the call
10
- * site that produced it, while the event can still be corrected.
9
+ * the same limits at `track()` reports the problem where it happens: the event
10
+ * is not buffered or sent, it is counted in `droppedCount`, WARN-logged with
11
+ * the field, the limit and the offending value, and handed to the opt-in
12
+ * `onDrop` hook with reason `'invalid'`. `track()` does not throw for it.
11
13
  *
12
14
  * Source: `dto/IngestUsageEventRequest` (aforo-nextgen-usage-ingestor-service) —
13
15
  * the `@Size` and `@Digits` bean constraints, which are compiled into the
@@ -22,12 +24,21 @@
22
24
  * Only `occurredAt`'s shape is checked, since a malformed timestamp is invalid
23
25
  * under every configuration.
24
26
  *
25
- * Nothing here truncates or rounds — that would change what is billed. The
26
- * offending event is rejected instead, naming the field, the limit, and the
27
- * offending value.
27
+ * The limit check never truncates or rounds a value the caller set — that would
28
+ * change what is billed. The offending event is dropped instead (reason
29
+ * `'invalid'`), naming the field, the limit, and the offending value.
30
+ *
31
+ * The one exception is a label the SDK itself reads off an incoming request
32
+ * (the middlewares' `endpointPath` and `httpMethod`): dropping there would let
33
+ * an API consumer avoid metering by sending an over-long path. Those are cut to
34
+ * the limit where they are derived (`truncateRequestLabel`) and the event is
35
+ * still sent.
28
36
  */
29
37
  Object.defineProperty(exports, "__esModule", { value: true });
30
38
  exports.MAX_QUANTITY_DECIMAL_PLACES = exports.MAX_QUANTITY_INTEGER_DIGITS = exports.MAX_LENGTHS = void 0;
39
+ exports.truncateToLimit = truncateToLimit;
40
+ exports.truncateRequestLabel = truncateRequestLabel;
41
+ exports.resetTruncationWarnings = resetTruncationWarnings;
31
42
  exports.quantityDigits = quantityDigits;
32
43
  exports.describeLimitViolation = describeLimitViolation;
33
44
  /** Maximum characters the ingestor accepts per string field (`@Size(max = ...)`). */
@@ -41,8 +52,16 @@ exports.MAX_LENGTHS = {
41
52
  sessionId: 64,
42
53
  agentId: 36,
43
54
  toolName: 64,
55
+ capabilityName: 64,
44
56
  endpointPath: 512,
45
57
  httpMethod: 16,
58
+ subscriptionId: 64,
59
+ grpcService: 255,
60
+ grpcMethod: 128,
61
+ gqlOperationName: 255,
62
+ wsConnectionId: 64,
63
+ mqttTopic: 500,
64
+ mqttClientId: 128,
46
65
  };
47
66
  /** `@Digits(integer = 14, fraction = 6)` — `usage_events.quantity` is NUMERIC(20,6). */
48
67
  exports.MAX_QUANTITY_INTEGER_DIGITS = 14;
@@ -61,6 +80,42 @@ const ISO_DATE_TIME = /^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?([Z
61
80
  function lengthOf(value) {
62
81
  return value.length;
63
82
  }
83
+ /**
84
+ * Cut `value` to at most `max` UTF-16 code units — how the ingestor counts —
85
+ * without leaving half a surrogate pair at the end.
86
+ */
87
+ function truncateToLimit(value, max) {
88
+ if (value.length <= max)
89
+ return value;
90
+ let end = Math.max(0, max);
91
+ const last = end > 0 ? value.charCodeAt(end - 1) : 0;
92
+ // A high surrogate at the cut means its low half was cut off: drop it too.
93
+ if (last >= 0xd800 && last <= 0xdbff)
94
+ end -= 1;
95
+ return value.slice(0, end);
96
+ }
97
+ /** Labels already reported as truncated — one WARN per label name per process. */
98
+ const truncationWarned = new Set();
99
+ /**
100
+ * Bound a label the SDK derived from an incoming request to the ingestor's
101
+ * limit for `field`. Logs one WARN per field per process. Not for values the
102
+ * SDK caller sets: those are never altered (an over-long one drops the event).
103
+ */
104
+ function truncateRequestLabel(field, value) {
105
+ const max = exports.MAX_LENGTHS[field];
106
+ if (typeof value !== 'string' || value.length <= max)
107
+ return value;
108
+ if (!truncationWarned.has(field)) {
109
+ truncationWarned.add(field);
110
+ console.warn(`[aforo] ${field} taken from the request was longer than the ingestor's limit and was `
111
+ + `truncated to ${max} characters; the event is still sent. Logged once per label.`);
112
+ }
113
+ return truncateToLimit(value, max);
114
+ }
115
+ /** Test hook: forget which labels were already reported as truncated. */
116
+ function resetTruncationWarnings() {
117
+ truncationWarned.clear();
118
+ }
64
119
  function lengthError(field, value) {
65
120
  if (value === undefined || value === null)
66
121
  return null;
@@ -70,7 +125,7 @@ function lengthError(field, value) {
70
125
  if (len <= max)
71
126
  return null;
72
127
  return `${field} is ${len} characters, exceeding the ingestor's ${max}-character limit `
73
- + `(value starts "${text.slice(0, 32)}"). Shorten it — the SDK will not truncate it, `
128
+ + `(value starts "${text.slice(0, 80)}"). Shorten it — the SDK will not truncate it, `
74
129
  + 'because a truncated id bills the wrong thing.';
75
130
  }
76
131
  /**
@@ -112,18 +167,18 @@ function quantityError(quantity) {
112
167
  }
113
168
  function occurredAtError(occurredAt) {
114
169
  if (!ISO_DATE_TIME.test(occurredAt)) {
115
- return `occurredAt "${occurredAt}" is not an ISO-8601 timestamp `
170
+ return `occurredAt "${String(occurredAt).slice(0, 80)}" is not an ISO-8601 timestamp `
116
171
  + '(expected e.g. "2026-03-01T14:30:00Z").';
117
172
  }
118
173
  if (Number.isNaN(Date.parse(occurredAt))) {
119
- return `occurredAt "${occurredAt}" is not a valid date.`;
174
+ return `occurredAt "${String(occurredAt).slice(0, 80)}" is not a valid date.`;
120
175
  }
121
176
  return null;
122
177
  }
123
178
  /**
124
179
  * Describe the first ingestor constraint this event breaks, or `null` when it
125
- * would be accepted. Callers that must not throw (the framework middlewares)
126
- * can use this to skip an event instead.
180
+ * would be accepted. `AforoClient.track()` uses it to drop an invalid event
181
+ * (reason `'invalid'`) before it is buffered.
127
182
  */
128
183
  function describeLimitViolation(event) {
129
184
  if (event.idempotencyKey !== undefined && event.idempotencyKey !== null
@@ -1 +1 @@
1
- {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/limits.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/limits.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { MiddlewareOptions } from '../types';
1
+ import { MiddlewareOptions } from '../types.js';
2
+ import { AforoClient } from '../client.js';
2
3
  /**
3
4
  * Metric recorded for each request when `metricName` is not configured.
4
5
  *
@@ -15,8 +16,29 @@ export declare const DEFAULT_METRIC_NAME = "api_calls";
15
16
  export declare function isPreflight(method: string | undefined): boolean;
16
17
  /** Resolve the metric: resolver function, then fixed name, then DEFAULT_METRIC_NAME. */
17
18
  export declare function resolveMetricName(options: MiddlewareOptions, req: any, res: any): string;
18
- /** Request path without its query string, capped to what the ingestor accepts. */
19
+ /**
20
+ * Request path without its query string. Read off the incoming request, so an
21
+ * over-long path is cut to the ingestor's limit (512) instead of dropping the
22
+ * event — otherwise a caller could avoid metering with a long URL.
23
+ */
19
24
  export declare function endpointPathOf(url: string): string;
25
+ /** HTTP method off the incoming request, cut to the ingestor's limit (16). */
26
+ export declare function httpMethodOf(method: string): string;
20
27
  /** First non-empty string among the candidates, or null. */
21
28
  export declare function firstNonEmpty(...values: unknown[]): string | null;
29
+ /** The client a middleware meters through: the app's own, or one built from the options. */
30
+ export declare function middlewareClient(options: MiddlewareOptions): AforoClient;
31
+ /**
32
+ * Build the per-request customer lookup. Throws at setup when neither a
33
+ * resolver nor the explicit `trustCustomerHeader` opt-in is configured: the
34
+ * X-Customer-Id header is set by whoever calls the API, so it is never read
35
+ * by default.
36
+ */
37
+ export declare function customerLookup(options: MiddlewareOptions, readHeader: (req: any) => unknown): (req: any, headerSource?: any) => string | null;
38
+ /**
39
+ * The endpointPath to send: the query string removed, dynamic segments
40
+ * collapsed unless `normalizePath` is false (a framework route template wins),
41
+ * and cut to the ingestor's 512-character limit.
42
+ */
43
+ export declare function endpointPathFor(options: MiddlewareOptions, rawPath: string, req: any, routeTemplate?: unknown): string;
22
44
  //# sourceMappingURL=common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/middleware/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,cAAc,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE/D;AAED,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAKxF;AAKD,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,IAAI,CAOjE"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/middleware/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,cAAc,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE/D;AAED,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAKxF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,WAAW,CAMxE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,GAChC,CAAC,GAAG,EAAE,GAAG,EAAE,YAAY,CAAC,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,CAkBjD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,CAUtH"}
@@ -4,7 +4,14 @@ exports.DEFAULT_METRIC_NAME = void 0;
4
4
  exports.isPreflight = isPreflight;
5
5
  exports.resolveMetricName = resolveMetricName;
6
6
  exports.endpointPathOf = endpointPathOf;
7
+ exports.httpMethodOf = httpMethodOf;
7
8
  exports.firstNonEmpty = firstNonEmpty;
9
+ exports.middlewareClient = middlewareClient;
10
+ exports.customerLookup = customerLookup;
11
+ exports.endpointPathFor = endpointPathFor;
12
+ const limits_js_1 = require("../limits.js");
13
+ const client_js_1 = require("../client.js");
14
+ const path_normalizer_js_1 = require("../path-normalizer.js");
8
15
  /**
9
16
  * Metric recorded for each request when `metricName` is not configured.
10
17
  *
@@ -28,11 +35,17 @@ function resolveMetricName(options, req, res) {
28
35
  }
29
36
  return options.metricName || exports.DEFAULT_METRIC_NAME;
30
37
  }
31
- /** Longest endpointPath the ingestor accepts. */
32
- const MAX_ENDPOINT_PATH = 512;
33
- /** Request path without its query string, capped to what the ingestor accepts. */
38
+ /**
39
+ * Request path without its query string. Read off the incoming request, so an
40
+ * over-long path is cut to the ingestor's limit (512) instead of dropping the
41
+ * event — otherwise a caller could avoid metering with a long URL.
42
+ */
34
43
  function endpointPathOf(url) {
35
- return (url.split('?')[0] || '/').slice(0, MAX_ENDPOINT_PATH);
44
+ return (0, limits_js_1.truncateRequestLabel)('endpointPath', String(url).split('?')[0] || '/');
45
+ }
46
+ /** HTTP method off the incoming request, cut to the ingestor's limit (16). */
47
+ function httpMethodOf(method) {
48
+ return (0, limits_js_1.truncateRequestLabel)('httpMethod', String(method));
36
49
  }
37
50
  /** First non-empty string among the candidates, or null. */
38
51
  function firstNonEmpty(...values) {
@@ -45,4 +58,53 @@ function firstNonEmpty(...values) {
45
58
  }
46
59
  return null;
47
60
  }
61
+ /** The client a middleware meters through: the app's own, or one built from the options. */
62
+ function middlewareClient(options) {
63
+ if (options?.client)
64
+ return options.client;
65
+ if (!options?.apiKey) {
66
+ throw new Error('Aforo middleware needs either `client` (an AforoClient your app owns) or `apiKey`.');
67
+ }
68
+ return new client_js_1.AforoClient({ ...(options.clientOptions ?? {}), apiKey: options.apiKey, baseUrl: options.baseUrl });
69
+ }
70
+ /**
71
+ * Build the per-request customer lookup. Throws at setup when neither a
72
+ * resolver nor the explicit `trustCustomerHeader` opt-in is configured: the
73
+ * X-Customer-Id header is set by whoever calls the API, so it is never read
74
+ * by default.
75
+ */
76
+ function customerLookup(options, readHeader) {
77
+ const legacy = options?.customerId;
78
+ const resolver = options?.customerResolver ??
79
+ (typeof legacy === 'function' ? legacy : typeof legacy === 'string' && legacy.trim() ? () => legacy : undefined);
80
+ const trustHeader = options?.trustCustomerHeader === true;
81
+ if (!resolver && !trustHeader) {
82
+ throw new Error('Aforo middleware needs a customerResolver: (req) => the Aforo customer id from identity your app ' +
83
+ 'authenticated (session, verified JWT subject, API-key lookup). To bill the X-Customer-Id header ' +
84
+ 'instead — only safe behind a proxy that sets it — pass trustCustomerHeader: true.');
85
+ }
86
+ return (req, headerSource = req) => {
87
+ const fromResolver = resolver ? firstNonEmpty(resolver(req)) : null;
88
+ if (fromResolver)
89
+ return fromResolver;
90
+ return trustHeader ? firstNonEmpty(readHeader(headerSource)) : null;
91
+ };
92
+ }
93
+ /**
94
+ * The endpointPath to send: the query string removed, dynamic segments
95
+ * collapsed unless `normalizePath` is false (a framework route template wins),
96
+ * and cut to the ingestor's 512-character limit.
97
+ */
98
+ function endpointPathFor(options, rawPath, req, routeTemplate) {
99
+ const path = String(rawPath ?? '/').split('?')[0] || '/';
100
+ const mode = options?.normalizePath ?? true;
101
+ let out = path;
102
+ if (typeof mode === 'function') {
103
+ out = String(mode(path, req) || path);
104
+ }
105
+ else if (mode) {
106
+ out = (0, path_normalizer_js_1.normalizePath)(path, typeof routeTemplate === 'string' && routeTemplate ? routeTemplate : undefined);
107
+ }
108
+ return endpointPathOf(out);
109
+ }
48
110
  //# sourceMappingURL=common.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/middleware/common.ts"],"names":[],"mappings":";;;AAgBA,kCAEC;AAGD,8CAKC;AAMD,wCAEC;AAGD,sCAOC;AA1CD;;;;;;;GAOG;AACU,QAAA,mBAAmB,GAAG,WAAW,CAAC;AAE/C;;;GAGG;AACH,SAAgB,WAAW,CAAC,MAA0B;IACpD,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC;AACpD,CAAC;AAED,wFAAwF;AACxF,SAAgB,iBAAiB,CAAC,OAA0B,EAAE,GAAQ,EAAE,GAAQ;IAC9E,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAC7C,OAAO,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,2BAAmB,CAAC;IAC7D,CAAC;IACD,OAAO,OAAO,CAAC,UAAU,IAAI,2BAAmB,CAAC;AACnD,CAAC;AAED,iDAAiD;AACjD,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B,kFAAkF;AAClF,SAAgB,cAAc,CAAC,GAAW;IACxC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;AAChE,CAAC;AAED,4DAA4D;AAC5D,SAAgB,aAAa,CAAC,GAAG,MAAiB;IAChD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;YAAE,SAAS;QAC5C,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrD,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/middleware/common.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,20 @@
1
- import { MiddlewareOptions } from '../types';
1
+ import { MiddlewareOptions } from '../types.js';
2
2
  /**
3
- * Express middleware that automatically captures API usage events.
3
+ * Express middleware that records one usage event per request.
4
4
  *
5
- * Hooks into `res.on('finish')` — runs AFTER the response is sent to the client.
6
- * Zero latency impact on the API call itself.
5
+ * Hooks into `res.on('finish')` — runs AFTER the response is sent, so it adds
6
+ * no latency to the request. Metering never throws into the request.
7
7
  *
8
8
  * ```typescript
9
+ * import { AforoClient } from '@aforoai/metering';
9
10
  * import { expressMiddleware } from '@aforoai/metering/middleware/express';
10
- * app.use(expressMiddleware({ apiKey: process.env.AFORO_API_KEY, productType: 'API' }));
11
+ * const aforo = new AforoClient({ apiKey: process.env.AFORO_API_KEY!, productType: 'API' });
12
+ * app.use(expressMiddleware({ client: aforo, customerResolver: (req) => req.user?.aforoCustomerId }));
13
+ * process.once('SIGTERM', async () => { await aforo.shutdown(); process.exit(0); });
11
14
  * ```
12
15
  */
13
- export declare function expressMiddleware(options: MiddlewareOptions): (req: any, res: any, next: any) => void;
16
+ export declare function expressMiddleware(options: MiddlewareOptions): ((req: any, res: any, next: any) => void) & {
17
+ client: import("../client.js").AforoClient;
18
+ };
14
19
  export declare const middleware: typeof expressMiddleware;
15
20
  //# sourceMappingURL=express.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../../src/middleware/express.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAK7C;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,IAUlB,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,UAgEtE;AAeD,eAAO,MAAM,UAAU,0BAAoB,CAAC"}
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../../src/middleware/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAKhD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,UAOJ,GAAG,OAAO,GAAG,QAAQ,GAAG;;EAsC/E;AAGD,eAAO,MAAM,UAAU,0BAAoB,CAAC"}
@@ -2,102 +2,65 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.middleware = void 0;
4
4
  exports.expressMiddleware = expressMiddleware;
5
- const client_1 = require("../client");
6
- const common_1 = require("./common");
5
+ const common_js_1 = require("./common.js");
7
6
  const DEFAULT_EXCLUDE_PATHS = ['/health', '/ready', '/metrics', '/favicon.ico'];
8
7
  /**
9
- * Express middleware that automatically captures API usage events.
8
+ * Express middleware that records one usage event per request.
10
9
  *
11
- * Hooks into `res.on('finish')` — runs AFTER the response is sent to the client.
12
- * Zero latency impact on the API call itself.
10
+ * Hooks into `res.on('finish')` — runs AFTER the response is sent, so it adds
11
+ * no latency to the request. Metering never throws into the request.
13
12
  *
14
13
  * ```typescript
14
+ * import { AforoClient } from '@aforoai/metering';
15
15
  * import { expressMiddleware } from '@aforoai/metering/middleware/express';
16
- * app.use(expressMiddleware({ apiKey: process.env.AFORO_API_KEY, productType: 'API' }));
16
+ * const aforo = new AforoClient({ apiKey: process.env.AFORO_API_KEY!, productType: 'API' });
17
+ * app.use(expressMiddleware({ client: aforo, customerResolver: (req) => req.user?.aforoCustomerId }));
18
+ * process.once('SIGTERM', async () => { await aforo.shutdown(); process.exit(0); });
17
19
  * ```
18
20
  */
19
21
  function expressMiddleware(options) {
20
- const client = new client_1.AforoClient({
21
- apiKey: options.apiKey,
22
- baseUrl: options.baseUrl,
23
- ...options.clientOptions,
24
- });
22
+ const lookupCustomer = (0, common_js_1.customerLookup)(options, (req) => req.headers?.['x-customer-id']);
23
+ const client = (0, common_js_1.middlewareClient)(options);
25
24
  const excludePaths = options.excludePaths ?? DEFAULT_EXCLUDE_PATHS;
26
25
  const excludeStatusCodes = options.excludeStatusCodes ?? [];
27
- return function aforoMeteringMiddleware(req, res, next) {
28
- // Capture timing
26
+ const handler = function aforoMeteringMiddleware(req, res, next) {
29
27
  const startTime = Date.now();
30
28
  res.on('finish', () => {
31
29
  try {
32
30
  const path = req.originalUrl || req.url || '/';
33
31
  const method = req.method || 'UNKNOWN';
34
32
  const statusCode = res.statusCode || 0;
35
- // Check exclusions
36
- if ((0, common_1.isPreflight)(method))
33
+ if ((0, common_js_1.isPreflight)(method))
37
34
  return;
38
35
  if (excludePaths.some((p) => path.startsWith(p)))
39
36
  return;
40
37
  if (excludeStatusCodes.includes(statusCode))
41
38
  return;
42
- // Resolve metric name (must be a metric in the tenant's catalog)
43
- const metricName = (0, common_1.resolveMetricName)(options, req, res);
44
- // Resolve quantity
45
- let quantity;
46
- if (typeof options.quantity === 'function') {
47
- quantity = options.quantity(req, res);
48
- }
49
- else {
50
- quantity = options.quantity ?? 1;
51
- }
52
- // Resolve customer ID
53
- let customerId;
54
- if (typeof options.customerId === 'function') {
55
- customerId = options.customerId(req);
56
- }
57
- else if (options.customerId) {
58
- customerId = options.customerId;
59
- }
60
- else {
61
- customerId = extractCustomerId(req);
62
- }
63
- if (!customerId)
64
- return; // Can't meter without a customer
39
+ const quantity = typeof options.quantity === 'function' ? options.quantity(req, res) : options.quantity ?? 1;
65
40
  if (!(quantity > 0))
66
41
  return; // The ingestor rejects quantity <= 0
67
- // Build metadata
68
- let metadata;
69
- if (options.metadata) {
70
- metadata = options.metadata(req, res);
71
- }
42
+ const route = typeof req.route?.path === 'string' ? `${req.baseUrl ?? ''}${req.route.path}` : undefined;
43
+ // No customer → track() drops it as 'invalid' and counts it; the request is unaffected.
72
44
  client.track({
73
- customerId,
74
- metricName,
45
+ customerId: lookupCustomer(req) ?? '',
46
+ metricName: (0, common_js_1.resolveMetricName)(options, req, res),
75
47
  quantity,
76
- metadata,
48
+ metadata: options.metadata ? options.metadata(req, res) : undefined,
77
49
  productType: options.productType,
78
- endpointPath: (0, common_1.endpointPathOf)(path),
79
- httpMethod: method,
50
+ endpointPath: (0, common_js_1.endpointPathFor)(options, path, req, route),
51
+ httpMethod: (0, common_js_1.httpMethodOf)(method),
80
52
  statusCode,
81
53
  responseTimeMs: Date.now() - startTime,
82
- }).catch(() => { }); // Fire-and-forget
54
+ }).catch((err) => console.debug(`[aforo] metering skipped: ${err?.message ?? err}`));
83
55
  }
84
- catch {
85
- // Never let metering errors affect the API
56
+ catch (err) {
57
+ // Never let metering errors affect the API.
58
+ console.debug(`[aforo] metering skipped: ${err?.message ?? err}`);
86
59
  }
87
60
  });
88
61
  next();
89
62
  };
90
- }
91
- /**
92
- * Default customer resolution: authenticated user id, then X-Customer-Id.
93
- *
94
- * The caller's X-Api-Key header is deliberately NOT used: that is the end
95
- * user's secret, and using it as customerId wrote credentials into billing data
96
- * while never matching an Aforo customer. Configure `customerId` to map your
97
- * callers to Aforo customer ids.
98
- */
99
- function extractCustomerId(req) {
100
- return (0, common_1.firstNonEmpty)(req.user?.id, req.user?.sub, req.headers?.['x-customer-id']);
63
+ return Object.assign(handler, { client });
101
64
  }
102
65
  // Also export as `middleware` for convenience
103
66
  exports.middleware = expressMiddleware;
@@ -1 +1 @@
1
- {"version":3,"file":"express.js","sourceRoot":"","sources":["../../../src/middleware/express.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../../src/middleware/express.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,17 @@
1
- import { MiddlewareOptions } from '../types';
1
+ import { MiddlewareOptions } from '../types.js';
2
2
  /**
3
- * Fastify plugin that automatically captures API usage events.
4
- *
5
- * Uses the `onResponse` hook — runs AFTER the response is sent.
3
+ * Fastify plugin that records one usage event per request in the
4
+ * `onResponse` hook (after the response is sent).
6
5
  *
7
6
  * ```typescript
7
+ * import { AforoClient } from '@aforoai/metering';
8
8
  * import { fastifyPlugin } from '@aforoai/metering/middleware/fastify';
9
- * fastify.register(fastifyPlugin, { apiKey: process.env.AFORO_API_KEY, productType: 'API' });
9
+ * const aforo = new AforoClient({ apiKey: process.env.AFORO_API_KEY!, productType: 'API' });
10
+ * fastify.register(fastifyPlugin, { client: aforo, customerResolver: (request) => request.user?.aforoCustomerId });
10
11
  * ```
12
+ *
13
+ * When the plugin creates its own client (apiKey instead of client), it shuts
14
+ * that client down in Fastify's onClose hook. A client you pass in is yours to shut down.
11
15
  */
12
16
  export declare function fastifyPlugin(fastify: any, options: MiddlewareOptions): Promise<void>;
13
17
  //# sourceMappingURL=fastify.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../../../src/middleware/fastify.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAK7C;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,iBAAiB,iBAwE3E"}
1
+ {"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../../../src/middleware/fastify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAKhD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,iBAAiB,iBAiD3E"}