@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
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Redis } from 'ioredis';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview The optional live cross-replica budget counter port (spec §15.1,
|
|
5
|
+
* §10.8). The fast path for budget enforcement; the DB conditional consume is the
|
|
6
|
+
* fallback. Values are serialized as int64 decimal strings. Implemented by the
|
|
7
|
+
* official Redis adapter (`./redis`).
|
|
8
|
+
* @layer server
|
|
9
|
+
*/
|
|
10
|
+
/** The live budget counter port. */
|
|
11
|
+
interface IBudgetCounterStore {
|
|
12
|
+
/**
|
|
13
|
+
* Atomically increment `key` by `amount` iff the result stays `<= limit`,
|
|
14
|
+
* setting the TTL. Returns `false` when the increment would exceed the limit.
|
|
15
|
+
*/
|
|
16
|
+
incrIfBelow(key: string, amount: bigint, limit: bigint, ttlSeconds: number): Promise<boolean>;
|
|
17
|
+
/** Decrement `key` by `amount` (release/reverse). */
|
|
18
|
+
decr(key: string, amount: bigint): Promise<void>;
|
|
19
|
+
/** Reset `key` (window rotation). */
|
|
20
|
+
reset(key: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @fileoverview `RedisBudgetCounterStore` — the optional live cross-replica budget
|
|
25
|
+
* counter (`./redis` subpath, spec §10.8, §15.1). It makes budget enforcement cheap
|
|
26
|
+
* under high concurrency: `incrIfBelow` is a SINGLE atomic Lua script (increment,
|
|
27
|
+
* then undo and reject when the result would exceed the limit), so two racing
|
|
28
|
+
* replicas can never both pass a nearly-full budget. The DB conditional consume
|
|
29
|
+
* stays authoritative — this counter is the fast path, not the source of truth.
|
|
30
|
+
* Values are int64 (Redis native) written/compared as decimal strings; keys carry
|
|
31
|
+
* the caller's optional prefix. `ioredis` is an OPTIONAL peer: the constructor
|
|
32
|
+
* accepts a live `Redis` instance, or a connection URL that is lazily connected via
|
|
33
|
+
* a DYNAMIC import so the base library never statically depends on the client.
|
|
34
|
+
* Connection URLs (which may embed credentials) are never logged or placed on an
|
|
35
|
+
* exception.
|
|
36
|
+
* @layer redis
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/** Construction options for {@link RedisBudgetCounterStore}. */
|
|
40
|
+
interface RedisBudgetCounterStoreOptions {
|
|
41
|
+
/** Prepended to every counter key (namespacing a shared Redis). */
|
|
42
|
+
keyPrefix?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Signals that a counter operand or result falls outside the IEEE-754 safe-integer
|
|
46
|
+
* range (`±(2^53 − 1)`), where the Lua/Redis double arithmetic the fast path relies
|
|
47
|
+
* on would silently lose precision. It is thrown so `BudgetService` treats the counter
|
|
48
|
+
* as unavailable and falls back to the authoritative DB conditional consume, which
|
|
49
|
+
* compares int64 nano-USD exactly.
|
|
50
|
+
*/
|
|
51
|
+
declare class CounterValueOutOfRangeError extends Error {
|
|
52
|
+
readonly isCounterValueOutOfRange = true;
|
|
53
|
+
constructor();
|
|
54
|
+
}
|
|
55
|
+
/** The official Redis adapter for the live budget counter port. */
|
|
56
|
+
declare class RedisBudgetCounterStore implements IBudgetCounterStore {
|
|
57
|
+
private readonly keyPrefix;
|
|
58
|
+
private readonly source;
|
|
59
|
+
private connecting;
|
|
60
|
+
/**
|
|
61
|
+
* @param redis A live `ioredis` client, or a connection URL to lazily connect.
|
|
62
|
+
* @param options Optional key prefix.
|
|
63
|
+
*/
|
|
64
|
+
constructor(redis: Redis | string, options?: RedisBudgetCounterStoreOptions);
|
|
65
|
+
/**
|
|
66
|
+
* Atomically increment `key` by `amount` iff the result stays `<= limit`, setting
|
|
67
|
+
* the TTL. Returns `false` when the increment would exceed the limit.
|
|
68
|
+
*
|
|
69
|
+
* @param key The dimension counter key.
|
|
70
|
+
* @param amount The amount to add (int64 nano-USD or token/count units).
|
|
71
|
+
* @param limit The dimension limit.
|
|
72
|
+
* @param ttlSeconds The key TTL (window length + grace).
|
|
73
|
+
* @returns `true` when incremented within the limit, `false` when it would exceed it.
|
|
74
|
+
* @throws {CounterValueOutOfRangeError} when an operand or the resulting counter exceeds the safe-integer range.
|
|
75
|
+
*/
|
|
76
|
+
incrIfBelow(key: string, amount: bigint, limit: bigint, ttlSeconds: number): Promise<boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* Decrement `key` by `amount`, flooring the stored value at zero (release/reverse).
|
|
79
|
+
*
|
|
80
|
+
* @param key The dimension counter key.
|
|
81
|
+
* @param amount The amount to subtract.
|
|
82
|
+
*/
|
|
83
|
+
decr(key: string, amount: bigint): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Reset (delete) `key` — used on window rotation.
|
|
86
|
+
*
|
|
87
|
+
* @param key The dimension counter key.
|
|
88
|
+
*/
|
|
89
|
+
reset(key: string): Promise<void>;
|
|
90
|
+
/** Prepend the configured key prefix. */
|
|
91
|
+
private prefixed;
|
|
92
|
+
/** Resolve the live client, lazily connecting a URL via a dynamic `ioredis` import. */
|
|
93
|
+
private resolve;
|
|
94
|
+
/** Dynamically import `ioredis` and connect the given URL. */
|
|
95
|
+
private connect;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { CounterValueOutOfRangeError, RedisBudgetCounterStore, type RedisBudgetCounterStoreOptions };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/redis/index.ts
|
|
2
|
+
var SAFE_INTEGER_MAX = "9007199254740991";
|
|
3
|
+
var INCR_IF_BELOW_LUA = `
|
|
4
|
+
local amount = tonumber(ARGV[1])
|
|
5
|
+
local limit = tonumber(ARGV[2])
|
|
6
|
+
if amount > ${SAFE_INTEGER_MAX} or amount < -${SAFE_INTEGER_MAX} or limit > ${SAFE_INTEGER_MAX} or limit < -${SAFE_INTEGER_MAX} then
|
|
7
|
+
return -1
|
|
8
|
+
end
|
|
9
|
+
local newValue = redis.call('INCRBY', KEYS[1], ARGV[1])
|
|
10
|
+
if newValue > ${SAFE_INTEGER_MAX} or newValue < -${SAFE_INTEGER_MAX} then
|
|
11
|
+
redis.call('DECRBY', KEYS[1], ARGV[1])
|
|
12
|
+
return -1
|
|
13
|
+
end
|
|
14
|
+
if newValue > limit then
|
|
15
|
+
redis.call('DECRBY', KEYS[1], ARGV[1])
|
|
16
|
+
return 0
|
|
17
|
+
end
|
|
18
|
+
redis.call('PEXPIRE', KEYS[1], ARGV[3])
|
|
19
|
+
return 1
|
|
20
|
+
`;
|
|
21
|
+
var DECR_FLOOR_LUA = `
|
|
22
|
+
local newValue = redis.call('DECRBY', KEYS[1], ARGV[1])
|
|
23
|
+
if newValue < 0 then
|
|
24
|
+
redis.call('SET', KEYS[1], '0')
|
|
25
|
+
end
|
|
26
|
+
return 1
|
|
27
|
+
`;
|
|
28
|
+
var CounterValueOutOfRangeError = class extends Error {
|
|
29
|
+
isCounterValueOutOfRange = true;
|
|
30
|
+
constructor() {
|
|
31
|
+
super("counter operand or result exceeds the safe-integer range");
|
|
32
|
+
this.name = "CounterValueOutOfRangeError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var RedisBudgetCounterStore = class {
|
|
36
|
+
keyPrefix;
|
|
37
|
+
source;
|
|
38
|
+
connecting = null;
|
|
39
|
+
/**
|
|
40
|
+
* @param redis A live `ioredis` client, or a connection URL to lazily connect.
|
|
41
|
+
* @param options Optional key prefix.
|
|
42
|
+
*/
|
|
43
|
+
constructor(redis, options = {}) {
|
|
44
|
+
this.keyPrefix = options.keyPrefix ?? "";
|
|
45
|
+
this.source = redis;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Atomically increment `key` by `amount` iff the result stays `<= limit`, setting
|
|
49
|
+
* the TTL. Returns `false` when the increment would exceed the limit.
|
|
50
|
+
*
|
|
51
|
+
* @param key The dimension counter key.
|
|
52
|
+
* @param amount The amount to add (int64 nano-USD or token/count units).
|
|
53
|
+
* @param limit The dimension limit.
|
|
54
|
+
* @param ttlSeconds The key TTL (window length + grace).
|
|
55
|
+
* @returns `true` when incremented within the limit, `false` when it would exceed it.
|
|
56
|
+
* @throws {CounterValueOutOfRangeError} when an operand or the resulting counter exceeds the safe-integer range.
|
|
57
|
+
*/
|
|
58
|
+
async incrIfBelow(key, amount, limit, ttlSeconds) {
|
|
59
|
+
const client = await this.resolve();
|
|
60
|
+
const result = await client.eval(
|
|
61
|
+
INCR_IF_BELOW_LUA,
|
|
62
|
+
1,
|
|
63
|
+
this.prefixed(key),
|
|
64
|
+
amount.toString(),
|
|
65
|
+
limit.toString(),
|
|
66
|
+
Math.ceil(ttlSeconds * 1e3).toString()
|
|
67
|
+
);
|
|
68
|
+
if (result === -1) throw new CounterValueOutOfRangeError();
|
|
69
|
+
return result === 1;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Decrement `key` by `amount`, flooring the stored value at zero (release/reverse).
|
|
73
|
+
*
|
|
74
|
+
* @param key The dimension counter key.
|
|
75
|
+
* @param amount The amount to subtract.
|
|
76
|
+
*/
|
|
77
|
+
async decr(key, amount) {
|
|
78
|
+
const client = await this.resolve();
|
|
79
|
+
await client.eval(DECR_FLOOR_LUA, 1, this.prefixed(key), amount.toString());
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Reset (delete) `key` — used on window rotation.
|
|
83
|
+
*
|
|
84
|
+
* @param key The dimension counter key.
|
|
85
|
+
*/
|
|
86
|
+
async reset(key) {
|
|
87
|
+
const client = await this.resolve();
|
|
88
|
+
await client.del(this.prefixed(key));
|
|
89
|
+
}
|
|
90
|
+
/** Prepend the configured key prefix. */
|
|
91
|
+
prefixed(key) {
|
|
92
|
+
return `${this.keyPrefix}${key}`;
|
|
93
|
+
}
|
|
94
|
+
/** Resolve the live client, lazily connecting a URL via a dynamic `ioredis` import. */
|
|
95
|
+
resolve() {
|
|
96
|
+
if (typeof this.source !== "string") return Promise.resolve(this.source);
|
|
97
|
+
this.connecting ??= this.connect(this.source);
|
|
98
|
+
return this.connecting;
|
|
99
|
+
}
|
|
100
|
+
/** Dynamically import `ioredis` and connect the given URL. */
|
|
101
|
+
async connect(url) {
|
|
102
|
+
const { default: RedisClient } = await import('ioredis');
|
|
103
|
+
return new RedisClient(url);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export { CounterValueOutOfRangeError, RedisBudgetCounterStore };
|