@bymax-one/nest-ai-tokens 1.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/CHANGELOG.md +113 -0
- package/LICENSE +21 -0
- package/README.md +795 -0
- package/dist/prices/index.cjs +218 -0
- package/dist/prices/index.d.cts +131 -0
- package/dist/prices/index.d.ts +131 -0
- package/dist/prices/index.mjs +216 -0
- package/dist/prisma/index.cjs +1197 -0
- package/dist/prisma/index.d.cts +677 -0
- package/dist/prisma/index.d.ts +677 -0
- package/dist/prisma/index.mjs +1195 -0
- package/dist/prisma/migrations/0001_init.sql +198 -0
- package/dist/prisma/schema.prisma.fragment +197 -0
- package/dist/redis/index.cjs +110 -0
- package/dist/redis/index.d.cts +98 -0
- package/dist/redis/index.d.ts +98 -0
- package/dist/redis/index.mjs +107 -0
- package/dist/server/index.cjs +4405 -0
- package/dist/server/index.d.cts +3202 -0
- package/dist/server/index.d.ts +3202 -0
- package/dist/server/index.mjs +4354 -0
- package/dist/shared/index.cjs +659 -0
- package/dist/shared/index.d.cts +1044 -0
- package/dist/shared/index.d.ts +1044 -0
- package/dist/shared/index.mjs +629 -0
- package/package.json +216 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@bymax-one/nest-ai-tokens` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
## [1.0.0] - 2026-08-03
|
|
12
|
+
|
|
13
|
+
First published release. Everything below ships in it.
|
|
14
|
+
|
|
15
|
+
The `Fixed` and `Security` entries record defects found and corrected before
|
|
16
|
+
publication, not regressions any consumer saw — there is no earlier release to
|
|
17
|
+
have regressed from. They are kept because the reasoning is worth having.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Nine provider normalizers** — OpenAI Chat Completions, OpenAI Responses API, OpenAI-compatible (DeepSeek, xAI, Groq, Azure OpenAI), Anthropic Messages, Google Gemini / Vertex AI, AWS Bedrock Converse, Mistral, OpenRouter, Vercel AI SDK v5/v6. All are pure functions over plain objects; no provider SDK peer dependency.
|
|
22
|
+
- **Versioned effective-dated pricing** — `PricingService` with a six-step model-resolution chain, in-memory TTL cache, `upsertPrice()`, and idempotent `MODEL_PRICES_SEED` snapshot seed. Rates are bigint nano-USD per million tokens; past records are never re-rated.
|
|
23
|
+
- **Immutable append-only ledger** — `LedgerService` with content-derived idempotency (`deriveIdempotencyKey`), exactly-once semantics (payload-hash replay detection), lifecycle transitions (pending → posted → reversed/released), filtered queries, and optional per-tenant tamper-evident hash chain (`verifyChain()`).
|
|
24
|
+
- **First-class markup / margin** — `MarkupResolver` with a fixed multiplier or a per-call `IMarkupPolicy`. Every record stores both `rawCostNanoUsd` (provider cost) and `billedCostNanoUsd` (after markup). The resale lever is a first-class configuration option, not application code.
|
|
25
|
+
- **Prepaid wallets** — `WalletService` with grant/debit/refund/adjust entries, configurable burn order (expiry/priority/FIFO), overdraft support, and debit-allocation tracking.
|
|
26
|
+
- **Multi-dimension budgets** — `BudgetService` with per-scope, per-feature, per-window (daily/weekly/monthly/total) caps on spend, token count, and operation count. Hard block (`policy: 'block'`) or soft alert with threshold events. Renewal-anchored windows via `anchorAt`.
|
|
27
|
+
- **Optional Redis budget counter** — `RedisBudgetCounterStore` with a single atomic Lua `incrIfBelow` script for sub-ms cross-replica enforcement.
|
|
28
|
+
- **Full hold → capture lifecycle** — `MeteringService.hold()` / `capture()` / `release()` / `meter()` for pre-flight spend reservation. `capture()` is idempotent.
|
|
29
|
+
- **Streaming capture** — `StreamUsageCollector` accumulates SSE chunks, prefers provider-final usage, falls back to tokenizer on abort.
|
|
30
|
+
- **NestJS enforcement** — `BudgetGuard` (CanActivate), `MeteringInterceptor` (NestInterceptor), and the `@Meter`, `@RequireBudget`, `@AiFeature` decorators for declarative controller-level metering.
|
|
31
|
+
- **Usage reporting** — `UsageReportService` with SQL-aggregated `summarize()`, CSV/JSON `export()`, paginated history, per-model analytics, and currency conversion.
|
|
32
|
+
- **Events** — Typed domain events (`ai_tokens.*`) delivered to `@nestjs/event-emitter` (optional peer) and/or an `IEventSink` port.
|
|
33
|
+
- **OpenTelemetry** — Optional `ITelemetrySink` / `OtelTelemetrySink` via `@opentelemetry/api` (optional peer).
|
|
34
|
+
- **PostgreSQL adapter** — `PrismaAiTokensStore` (`./prisma` subpath) implementing all four storage ports via parameterized raw SQL. Seven-table schema with migrations.
|
|
35
|
+
- **Five subpaths** — `.` (server), `./shared` (zero-dep), `./prices` (data-only), `./prisma` (adapter), `./redis` (counter).
|
|
36
|
+
- **Zero runtime dependencies** — `"dependencies": {}`. All runtime functionality via peer dependencies.
|
|
37
|
+
- **100% unit test coverage** — 804 tests; 10-scenario Testcontainers e2e suite; Stryker mutation gate at 100.00% (0 surviving mutants, break 95).
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
- **Prisma 7 support, alongside Prisma 6.** The declared peer range has always
|
|
41
|
+
been `>=6.0.0`, but nothing verified the claim; the end-to-end suite now runs
|
|
42
|
+
against Prisma 7 and a real PostgreSQL, and it is what surfaced the conflict
|
|
43
|
+
defect above. The library's own code needed no change — it reaches PostgreSQL
|
|
44
|
+
exclusively through parameterized raw SQL, and every `Prisma.*` helper it uses
|
|
45
|
+
is unchanged across the two majors.
|
|
46
|
+
|
|
47
|
+
What changed is the development schema and the test harness, mirroring what a
|
|
48
|
+
host application has to do: `url` left the `datasource` block for a new
|
|
49
|
+
`prisma.config.ts`, and clients are opened through `@prisma/adapter-pg` rather
|
|
50
|
+
than the removed `datasourceUrl`. Both setups are documented in the README.
|
|
51
|
+
|
|
52
|
+
- **`pnpm check:exports`** runs `attw --pack . --profile strict` against the packed
|
|
53
|
+
tarball, which is what surfaced both resolution defects above.
|
|
54
|
+
- **`pnpm check:runtime`** packs the tarball, lays it out the way npm would, and
|
|
55
|
+
asserts in ESM *and* CommonJS that an error thrown by `PrismaAiTokensStore`
|
|
56
|
+
satisfies `instanceof AiTokensException` against the class the root exports. No
|
|
57
|
+
source-based suite can observe this: the unit tests map the subpath specifiers to
|
|
58
|
+
`src` and therefore see a single copy. Both gates run in CI.
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
|
|
62
|
+
- **Idempotency conflicts were reported as generic store errors under Prisma 7.**
|
|
63
|
+
A raw-query unique violation carries the native SQLSTATE, and where it carries
|
|
64
|
+
it depends on how the client reaches the database. Prisma 6 puts it flat on
|
|
65
|
+
`meta.code`; a Prisma 7 driver adapter nests the driver's own error under
|
|
66
|
+
`meta.driverAdapterError.cause`. `isUniqueViolation` read only the first, so on
|
|
67
|
+
Prisma 7 the exactly-once replay-or-conflict path (§15.2) degraded a 409
|
|
68
|
+
`AI_TOKENS_IDEMPOTENCY_CONFLICT` into a 500-class `AI_TOKENS_STORE_ERROR` —
|
|
69
|
+
silently, since nothing threw. Both shapes are now read.
|
|
70
|
+
|
|
71
|
+
- **Errors thrown by `PrismaAiTokensStore` were not recognised as
|
|
72
|
+
`AiTokensException`.** The store lives in the `./prisma` entry point and reached
|
|
73
|
+
the class through a relative path into `../server`, so the separate bundle got
|
|
74
|
+
its own copy. The copy carries the same name and the same shape, so nothing
|
|
75
|
+
crashed — but the four `instanceof AiTokensException` guards in
|
|
76
|
+
`bymax-ai-tokens.module.ts`, `hold-support.ts`, `wallet.service.ts` and
|
|
77
|
+
`metering.service.ts` all stopped matching, silently reclassifying store errors
|
|
78
|
+
such as `AI_TOKENS_INSUFFICIENT_CREDITS` and `AI_TOKENS_IDEMPOTENCY_CONFLICT` as
|
|
79
|
+
unexpected failures.
|
|
80
|
+
|
|
81
|
+
`./prisma` now imports the class by package specifier, which the bundler already
|
|
82
|
+
kept external for the lazily-loaded `./prices` subpath, so one identity is shared
|
|
83
|
+
in CommonJS as well as ESM. `isLedgerIdempotencyConflict` was unaffected: it is
|
|
84
|
+
deliberately duck-typed rather than `instanceof`-based.
|
|
85
|
+
|
|
86
|
+
- **CommonJS consumers resolved ESM type declarations** on all five subpaths. The
|
|
87
|
+
`exports` map declared a single `types` condition, so `require()` landed on
|
|
88
|
+
`.d.ts` instead of `.d.cts`. Types are now declared per condition.
|
|
89
|
+
|
|
90
|
+
- **`node10` type resolution failed outright**: the manifest carried no `main`,
|
|
91
|
+
`module` or `types`, and no `typesVersions`. All four are now present.
|
|
92
|
+
|
|
93
|
+
### Security
|
|
94
|
+
|
|
95
|
+
- **Peer floors raised to exclude known-vulnerable NestJS versions.** The declared
|
|
96
|
+
ranges were `@nestjs/common ^11.0.0` and `@nestjs/core ^11.0.0`, and both
|
|
97
|
+
admitted versions carrying published advisories:
|
|
98
|
+
|
|
99
|
+
| Peer | Advisory | Vulnerable | New floor |
|
|
100
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ---------- |
|
|
101
|
+
| `@nestjs/common` | [GHSA-cj7v-w2c7-cp7c](https://github.com/advisories/GHSA-cj7v-w2c7-cp7c) — remote code execution via the `Content-Type` header | `>= 11.0.0-next.1, < 11.0.16` | `^11.0.16` |
|
|
102
|
+
| `@nestjs/core` | [GHSA-36xv-jgw5-4q75](https://github.com/advisories/GHSA-36xv-jgw5-4q75) — improper neutralization of special elements in downstream output | `<= 11.1.17` | `^11.1.18` |
|
|
103
|
+
|
|
104
|
+
A peer range is a statement about which versions this library supports. A floor
|
|
105
|
+
below a published advisory tells a consumer that a vulnerable install is a
|
|
106
|
+
supported one, and nothing in their tooling contradicts it — the install resolves
|
|
107
|
+
cleanly and silently. Corrected before the first publish, so no released version
|
|
108
|
+
ever carried the permissive range. No runtime behaviour changed.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
[1.0.0]: https://github.com/bymaxone/nest-ai-tokens/releases/tag/v1.0.0
|
|
113
|
+
[Unreleased]: https://github.com/bymaxone/nest-ai-tokens/compare/v1.0.0...HEAD
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bymax One
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|