@bymax-one/nest-ai-tokens 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,35 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and
6
6
 
7
7
  ---
8
8
 
9
+ ## [Unreleased]
10
+
11
+ ## [1.1.2] - 2026-10-30
12
+
13
+ ### Fixed
14
+
15
+ - **`PricingService.resolveRate` no longer serves one request's price to another.** The
16
+ resolution cache key joined provider, model, baseModel, operation, tier and time bucket
17
+ with `|`, which can also appear inside a model id. Two different requests could spell the
18
+ same key — `model: 'b|c'` with no `baseModel` against `model: 'b'` with `baseModel: 'c|'` —
19
+ and the second received the first's rate from the cache. The key is now a JSON array of
20
+ the fields, so no value can move the boundary between them. (#117)
21
+
22
+ **Apply:** no action required. Requests whose model names contain `|` now resolve their
23
+ own price instead of a colliding entry's.
24
+
25
+ ### Security
26
+
27
+ - Advisories on transitive **dev** packages are cleared through `pnpm-workspace.yaml`
28
+ overrides: body-parser, multer, qs and zod (#115). deepmerge GHSA-gcqx-w48m-w2h4 has no
29
+ patched release and is reached only through `jest-config`, so it is ignored in
30
+ `osv-scanner.toml` until 2027-01-27. None of these reach a consumer install: the package
31
+ has no runtime dependencies.
32
+
33
+ ### Documentation
34
+
35
+ - `SECURITY.md`: only the latest minor line (1.1.x) receives security fixes; 1.0.x is
36
+ unsupported and consumers should upgrade.
37
+
9
38
  ## [1.1.1] - 2026-10-23
10
39
 
11
40
  ### Fixed
@@ -62,7 +91,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and
62
91
 
63
92
  ## [1.0.4] - 2026-08-10
64
93
 
65
- Remediation of a local audit's settlement findings (merged in #65). No API changed.
94
+ Remediation of a local audit's settlement findings (merged in #65). No API changes.
66
95
 
67
96
  ### Fixed
68
97
 
@@ -267,7 +296,8 @@ have regressed from. They are kept because the reasoning is worth having.
267
296
  [1.0.1]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.0.0...v1.0.1
268
297
  [1.0.2]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.0.1...v1.0.2
269
298
  [1.1.0]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.0.4...v1.1.0
299
+ [1.1.2]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.1.1...v1.1.2
270
300
  [1.1.1]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.1.0...v1.1.1
271
301
  [1.0.4]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.0.3...v1.0.4
272
302
  [1.0.3]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.0.2...v1.0.3
273
- [Unreleased]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.1.1...HEAD
303
+ [Unreleased]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.1.2...HEAD
@@ -1224,7 +1224,7 @@ exports.PricingService = class PricingService {
1224
1224
  const serviceTier = input.serviceTier ?? "standard";
1225
1225
  const operation = input.operation === "responses" ? "chat" : input.operation;
1226
1226
  const bucket = Math.floor(input.at.getTime() / this.options.pricing.cacheTtlMs);
1227
- const cacheKey = `${input.provider}|${input.model}|${input.baseModel ?? ""}|${operation}|${serviceTier}|${String(bucket)}`;
1227
+ const cacheKey = JSON.stringify([input.provider, input.model, input.baseModel, operation, serviceTier, bucket]);
1228
1228
  let resolved;
1229
1229
  const cached = this.cache.get(cacheKey);
1230
1230
  if (cached !== void 0 && this.now() < cached.expiresAt) {
@@ -2775,10 +2775,10 @@ exports.WalletService = class WalletService {
2775
2775
  options;
2776
2776
  events;
2777
2777
  /**
2778
- * The current balance, excluding not-yet-effective and expired grants (§9.2).
2779
- * Reads the materialized balance; expiry is applied lazily on debit, so a grant
2780
- * that lapses purely with the passage of time is reflected at the next debit or
2781
- * `reconcile` (the documented §9.3 lazy-materialization trade-off).
2778
+ * The current balance (§9.2). Reads the materialized balance; expiry is applied
2779
+ * lazily on debit, so a grant that lapses purely with the passage of time is still
2780
+ * counted until the next debit or `reconcile` (the documented §9.3
2781
+ * lazy-materialization trade-off).
2782
2782
  *
2783
2783
  * @param ref The wallet owner (a `'tenant' | 'team' | 'user'`, never `'key'`).
2784
2784
  * @returns The nano-USD balance and its presentation credits.
@@ -2965,7 +2965,7 @@ exports.WalletService = class WalletService {
2965
2965
  * `ai_tokens.audit`.
2966
2966
  *
2967
2967
  * @param ref The wallet owner.
2968
- * @returns The reconciled wallet.
2968
+ * @returns The reconciled nano-USD balance and its presentation credits.
2969
2969
  */
2970
2970
  async reconcile(ref) {
2971
2971
  this.assertOwner(ref);
@@ -2224,10 +2224,10 @@ declare class WalletService {
2224
2224
  */
2225
2225
  constructor(store: IWalletStore, options: WalletServiceOptions, events?: WalletEventHooks);
2226
2226
  /**
2227
- * The current balance, excluding not-yet-effective and expired grants (§9.2).
2228
- * Reads the materialized balance; expiry is applied lazily on debit, so a grant
2229
- * that lapses purely with the passage of time is reflected at the next debit or
2230
- * `reconcile` (the documented §9.3 lazy-materialization trade-off).
2227
+ * The current balance (§9.2). Reads the materialized balance; expiry is applied
2228
+ * lazily on debit, so a grant that lapses purely with the passage of time is still
2229
+ * counted until the next debit or `reconcile` (the documented §9.3
2230
+ * lazy-materialization trade-off).
2231
2231
  *
2232
2232
  * @param ref The wallet owner (a `'tenant' | 'team' | 'user'`, never `'key'`).
2233
2233
  * @returns The nano-USD balance and its presentation credits.
@@ -2306,7 +2306,7 @@ declare class WalletService {
2306
2306
  * `ai_tokens.audit`.
2307
2307
  *
2308
2308
  * @param ref The wallet owner.
2309
- * @returns The reconciled wallet.
2309
+ * @returns The reconciled nano-USD balance and its presentation credits.
2310
2310
  */
2311
2311
  reconcile(ref: WalletRef): Promise<WalletBalance>;
2312
2312
  /** Append a non-debit entry, mapping a store idempotency/missing-wallet signal to the catalog. */
@@ -3119,7 +3119,7 @@ declare class MeteringInterceptor implements NestInterceptor {
3119
3119
  * @fileoverview The controller-level metering/enforcement decorators (spec §11.4).
3120
3120
  * `@Meter` marks a handler for the `MeteringInterceptor` and is metadata-only
3121
3121
  * here; `@RequireBudget` marks it for the `BudgetGuard`; `@AiFeature`
3122
- * is a lightweight feature tag. Each stores its config under an exported symbol key
3122
+ * is a lightweight feature tag. Each stores its config under an exported string key
3123
3123
  * that the guard/interceptor read via the NestJS `Reflector`. Feature precedence is
3124
3124
  * `@RequireBudget.feature` > `@Meter.feature` > `@AiFeature` (§11.4). These are pure
3125
3125
  * metadata; they never touch a wallet, budget, or the request.
@@ -2224,10 +2224,10 @@ declare class WalletService {
2224
2224
  */
2225
2225
  constructor(store: IWalletStore, options: WalletServiceOptions, events?: WalletEventHooks);
2226
2226
  /**
2227
- * The current balance, excluding not-yet-effective and expired grants (§9.2).
2228
- * Reads the materialized balance; expiry is applied lazily on debit, so a grant
2229
- * that lapses purely with the passage of time is reflected at the next debit or
2230
- * `reconcile` (the documented §9.3 lazy-materialization trade-off).
2227
+ * The current balance (§9.2). Reads the materialized balance; expiry is applied
2228
+ * lazily on debit, so a grant that lapses purely with the passage of time is still
2229
+ * counted until the next debit or `reconcile` (the documented §9.3
2230
+ * lazy-materialization trade-off).
2231
2231
  *
2232
2232
  * @param ref The wallet owner (a `'tenant' | 'team' | 'user'`, never `'key'`).
2233
2233
  * @returns The nano-USD balance and its presentation credits.
@@ -2306,7 +2306,7 @@ declare class WalletService {
2306
2306
  * `ai_tokens.audit`.
2307
2307
  *
2308
2308
  * @param ref The wallet owner.
2309
- * @returns The reconciled wallet.
2309
+ * @returns The reconciled nano-USD balance and its presentation credits.
2310
2310
  */
2311
2311
  reconcile(ref: WalletRef): Promise<WalletBalance>;
2312
2312
  /** Append a non-debit entry, mapping a store idempotency/missing-wallet signal to the catalog. */
@@ -3119,7 +3119,7 @@ declare class MeteringInterceptor implements NestInterceptor {
3119
3119
  * @fileoverview The controller-level metering/enforcement decorators (spec §11.4).
3120
3120
  * `@Meter` marks a handler for the `MeteringInterceptor` and is metadata-only
3121
3121
  * here; `@RequireBudget` marks it for the `BudgetGuard`; `@AiFeature`
3122
- * is a lightweight feature tag. Each stores its config under an exported symbol key
3122
+ * is a lightweight feature tag. Each stores its config under an exported string key
3123
3123
  * that the guard/interceptor read via the NestJS `Reflector`. Feature precedence is
3124
3124
  * `@RequireBudget.feature` > `@Meter.feature` > `@AiFeature` (§11.4). These are pure
3125
3125
  * metadata; they never touch a wallet, budget, or the request.
@@ -1222,7 +1222,7 @@ var PricingService = class {
1222
1222
  const serviceTier = input.serviceTier ?? "standard";
1223
1223
  const operation = input.operation === "responses" ? "chat" : input.operation;
1224
1224
  const bucket = Math.floor(input.at.getTime() / this.options.pricing.cacheTtlMs);
1225
- const cacheKey = `${input.provider}|${input.model}|${input.baseModel ?? ""}|${operation}|${serviceTier}|${String(bucket)}`;
1225
+ const cacheKey = JSON.stringify([input.provider, input.model, input.baseModel, operation, serviceTier, bucket]);
1226
1226
  let resolved;
1227
1227
  const cached = this.cache.get(cacheKey);
1228
1228
  if (cached !== void 0 && this.now() < cached.expiresAt) {
@@ -2773,10 +2773,10 @@ var WalletService = class {
2773
2773
  options;
2774
2774
  events;
2775
2775
  /**
2776
- * The current balance, excluding not-yet-effective and expired grants (§9.2).
2777
- * Reads the materialized balance; expiry is applied lazily on debit, so a grant
2778
- * that lapses purely with the passage of time is reflected at the next debit or
2779
- * `reconcile` (the documented §9.3 lazy-materialization trade-off).
2776
+ * The current balance (§9.2). Reads the materialized balance; expiry is applied
2777
+ * lazily on debit, so a grant that lapses purely with the passage of time is still
2778
+ * counted until the next debit or `reconcile` (the documented §9.3
2779
+ * lazy-materialization trade-off).
2780
2780
  *
2781
2781
  * @param ref The wallet owner (a `'tenant' | 'team' | 'user'`, never `'key'`).
2782
2782
  * @returns The nano-USD balance and its presentation credits.
@@ -2963,7 +2963,7 @@ var WalletService = class {
2963
2963
  * `ai_tokens.audit`.
2964
2964
  *
2965
2965
  * @param ref The wallet owner.
2966
- * @returns The reconciled wallet.
2966
+ * @returns The reconciled nano-USD balance and its presentation credits.
2967
2967
  */
2968
2968
  async reconcile(ref) {
2969
2969
  this.assertOwner(ref);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bymax-one/nest-ai-tokens",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "AI token metering & usage-based billing for NestJS 11: provider usage normalizer (×9), versioned effective-dated pricing, immutable append-only ledger, prepaid wallets, multi-dimension budgets, and markup/margin. Zero runtime dependencies.",
5
5
  "author": "Bymax One <support@bymax.one>",
6
6
  "license": "MIT",