@capixjs/core 0.1.0-alpha.18 → 0.1.0-alpha.22
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/dist/enhancers.d.ts +26 -10
- package/dist/enhancers.d.ts.map +1 -1
- package/dist/enhancers.js +13 -53
- package/dist/enhancers.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/stores.d.ts +53 -0
- package/dist/stores.d.ts.map +1 -0
- package/dist/stores.js +80 -0
- package/dist/stores.js.map +1 -0
- package/package.json +4 -2
package/dist/enhancers.d.ts
CHANGED
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
* Depends on: capability.ts
|
|
4
4
|
*/
|
|
5
5
|
import type { Enhancer } from './capability.js';
|
|
6
|
+
import type { CacheStore, RateLimitStore } from './stores.js';
|
|
6
7
|
/** Pass-through for type inference. */
|
|
7
8
|
export declare function defineEnhancer(fn: Enhancer): Enhancer;
|
|
8
9
|
/** Logs capability name, duration, and outcome. Falls back to console if ctx has no logger. */
|
|
9
10
|
export declare const withLogging: Enhancer;
|
|
10
11
|
export type CacheOptions = {
|
|
11
12
|
/**
|
|
12
|
-
* Maximum number of cached entries
|
|
13
|
-
* when the limit is reached.
|
|
13
|
+
* Maximum number of cached entries in the default in-memory store.
|
|
14
|
+
* Least-recently-used entries are evicted when the limit is reached.
|
|
15
|
+
* Default: 1_000. Ignored when a custom `store` is provided.
|
|
14
16
|
*/
|
|
15
17
|
readonly maxSize?: number;
|
|
16
18
|
/**
|
|
@@ -25,8 +27,15 @@ export type CacheOptions = {
|
|
|
25
27
|
* withCache(30, { keyFn: (input, ctx) => `${(ctx as AppContext).user?.id}:${JSON.stringify(input)}` })
|
|
26
28
|
*/
|
|
27
29
|
readonly keyFn?: (input: unknown, ctx: unknown) => string;
|
|
30
|
+
/**
|
|
31
|
+
* Cache backend. Defaults to an in-memory LRU, which is per-process — behind
|
|
32
|
+
* a load balancer each instance caches independently. Use a shared store
|
|
33
|
+
* (e.g. redisCacheStore from @capixjs/store-redis) for multi-instance
|
|
34
|
+
* deployments.
|
|
35
|
+
*/
|
|
36
|
+
readonly store?: CacheStore;
|
|
28
37
|
};
|
|
29
|
-
/**
|
|
38
|
+
/** Caches resolver results. Key = capabilityName:keyFn(input, ctx). TTL in seconds. */
|
|
30
39
|
export declare function withCache(ttlSeconds: number, options?: CacheOptions): Enhancer;
|
|
31
40
|
/** Rejects if the resolver exceeds the given milliseconds. */
|
|
32
41
|
export declare function withTimeout(ms: number): Enhancer;
|
|
@@ -49,16 +58,23 @@ export type RateLimitOptions = {
|
|
|
49
58
|
*/
|
|
50
59
|
readonly keyFn?: (input: unknown, ctx: unknown) => string;
|
|
51
60
|
/**
|
|
52
|
-
* Maximum number of distinct keys tracked
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
61
|
+
* Maximum number of distinct keys tracked by the default in-memory store.
|
|
62
|
+
* Default: 10_000. When exceeded, keys with no activity in the current
|
|
63
|
+
* window are swept; if every key is still active, the oldest-tracked keys
|
|
64
|
+
* are evicted (their rate-limit state resets). This bounds memory when
|
|
65
|
+
* keyFn has unbounded cardinality (per-user, per-IP).
|
|
66
|
+
* Ignored when a custom `store` is provided.
|
|
58
67
|
*/
|
|
59
68
|
readonly maxKeys?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Rate-limit backend. Defaults to an in-memory sliding window, which is
|
|
71
|
+
* per-process — N instances behind a load balancer enforce N times the
|
|
72
|
+
* intended limit. Use a shared store (e.g. redisRateLimitStore from
|
|
73
|
+
* @capixjs/store-redis) for multi-instance deployments.
|
|
74
|
+
*/
|
|
75
|
+
readonly store?: RateLimitStore;
|
|
60
76
|
};
|
|
61
|
-
/**
|
|
77
|
+
/** Rate limiter. Throws 429 with retryAfter meta when the limit is exceeded. */
|
|
62
78
|
export declare function withRateLimit(options: RateLimitOptions): Enhancer;
|
|
63
79
|
export type RollbackFn = () => unknown;
|
|
64
80
|
/**
|
package/dist/enhancers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enhancers.d.ts","sourceRoot":"","sources":["../src/enhancers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,iBAAiB,CAAC;AAI/D,uCAAuC;AACvC,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,QAAQ,CAErD;AAED,+FAA+F;AAC/F,eAAO,MAAM,WAAW,EAiBjB,QAAQ,CAAC;AAEhB,MAAM,MAAM,YAAY,GAAG;IACzB
|
|
1
|
+
{"version":3,"file":"enhancers.d.ts","sourceRoot":"","sources":["../src/enhancers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,iBAAiB,CAAC;AAI/D,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9D,uCAAuC;AACvC,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,QAAQ,CAErD;AAED,+FAA+F;AAC/F,eAAO,MAAM,WAAW,EAiBjB,QAAQ,CAAC;AAEhB,MAAM,MAAM,YAAY,GAAG;IACzB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IAC1D;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;CAC7B,CAAC;AAEF,uFAAuF;AACvF,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,QAAQ,CAkBlF;AAED,8DAA8D;AAC9D,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAqBhD;AAED,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,SAAM,GAAG,QAAQ,CAoBtE;AAMD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IAC1D;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,cAAc,CAAC;CACjC,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,QAAQ,CAwBjE;AAMD,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC;AAEvC;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG;IAChC,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,YAAY,EAsBlB,QAAQ,CAAC;AAMhB,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC7D,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CAC7E;AAED,8CAA8C;AAC9C,eAAO,MAAM,uBAAuB,EAAE,gBAOrC,CAAC;AAEF,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,SAAS,EAAE,gBAAgB,GAAG,QAAQ,CAkBjE;AAMD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAMF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,qBAAqB,GAAG,QAAQ,CA+C3E"}
|
package/dist/enhancers.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { defineError, isFrameworkError } from './errors.js';
|
|
6
6
|
import { defaultErrors } from './errors.js';
|
|
7
|
+
import { createMemoryCacheStore, createMemoryRateLimitStore } from './stores.js';
|
|
7
8
|
/** Pass-through for type inference. */
|
|
8
9
|
export function defineEnhancer(fn) {
|
|
9
10
|
return fn;
|
|
@@ -27,33 +28,19 @@ export const withLogging = defineEnhancer((cap) => ({
|
|
|
27
28
|
}
|
|
28
29
|
},
|
|
29
30
|
}));
|
|
30
|
-
/**
|
|
31
|
+
/** Caches resolver results. Key = capabilityName:keyFn(input, ctx). TTL in seconds. */
|
|
31
32
|
export function withCache(ttlSeconds, options = {}) {
|
|
32
|
-
const maxSize = options.maxSize ?? 1_000;
|
|
33
33
|
const keyFn = options.keyFn;
|
|
34
|
-
const store =
|
|
34
|
+
const store = options.store ?? createMemoryCacheStore(options.maxSize !== undefined ? { maxSize: options.maxSize } : {});
|
|
35
35
|
return defineEnhancer((cap) => ({
|
|
36
36
|
...cap,
|
|
37
37
|
resolve: async (input, ctx) => {
|
|
38
38
|
const key = `${cap.name}:${keyFn ? keyFn(input, ctx) : JSON.stringify(input)}`;
|
|
39
|
-
const cached = store.get(key);
|
|
40
|
-
if (cached !== undefined)
|
|
41
|
-
|
|
42
|
-
// Refresh recency for LRU ordering
|
|
43
|
-
store.delete(key);
|
|
44
|
-
store.set(key, cached);
|
|
45
|
-
return cached.value;
|
|
46
|
-
}
|
|
47
|
-
store.delete(key); // expired — don't let dead entries occupy capacity
|
|
48
|
-
}
|
|
39
|
+
const cached = await store.get(key);
|
|
40
|
+
if (cached !== undefined)
|
|
41
|
+
return cached;
|
|
49
42
|
const result = await cap._resolverOnly(input, ctx);
|
|
50
|
-
|
|
51
|
-
// Evict least-recently-used (first key in Map insertion order)
|
|
52
|
-
const oldest = store.keys().next().value;
|
|
53
|
-
if (oldest !== undefined)
|
|
54
|
-
store.delete(oldest);
|
|
55
|
-
}
|
|
56
|
-
store.set(key, { value: result, expiresAt: Date.now() + ttlSeconds * 1000 });
|
|
43
|
+
await store.set(key, result, ttlSeconds * 1000);
|
|
57
44
|
return result;
|
|
58
45
|
},
|
|
59
46
|
}));
|
|
@@ -101,50 +88,23 @@ export function withRetry(maxAttempts, delayMs = 100) {
|
|
|
101
88
|
},
|
|
102
89
|
}));
|
|
103
90
|
}
|
|
104
|
-
/**
|
|
91
|
+
/** Rate limiter. Throws 429 with retryAfter meta when the limit is exceeded. */
|
|
105
92
|
export function withRateLimit(options) {
|
|
106
93
|
const { limit, windowMs, keyFn } = options;
|
|
107
|
-
const
|
|
108
|
-
const store = new Map();
|
|
109
|
-
function evictStale(now) {
|
|
110
|
-
const cutoff = now - windowMs;
|
|
111
|
-
for (const [k, ts] of store) {
|
|
112
|
-
const last = ts[ts.length - 1];
|
|
113
|
-
if (last === undefined || last <= cutoff)
|
|
114
|
-
store.delete(k);
|
|
115
|
-
}
|
|
116
|
-
// Every key still active — hard-cap by evicting oldest-tracked keys
|
|
117
|
-
if (store.size > maxKeys) {
|
|
118
|
-
for (const k of store.keys()) {
|
|
119
|
-
store.delete(k);
|
|
120
|
-
if (store.size <= maxKeys)
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
94
|
+
const store = options.store ?? createMemoryRateLimitStore(options.maxKeys !== undefined ? { maxKeys: options.maxKeys } : {});
|
|
125
95
|
return defineEnhancer((cap) => ({
|
|
126
96
|
...cap,
|
|
127
97
|
resolve: async (input, ctx) => {
|
|
128
98
|
const key = keyFn ? keyFn(input, ctx) : cap.name;
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
evictStale(now);
|
|
133
|
-
let timestamps = store.get(key) ?? [];
|
|
134
|
-
timestamps = timestamps.filter((t) => t > windowStart);
|
|
135
|
-
if (timestamps.length >= limit) {
|
|
136
|
-
// Find the oldest timestamp in the window — client can retry after it expires
|
|
137
|
-
const oldestInWindow = timestamps[0] ?? now;
|
|
138
|
-
const resetAt = oldestInWindow + windowMs;
|
|
139
|
-
const retryAfter = Math.ceil((resetAt - now) / 1000);
|
|
99
|
+
const decision = await store.hit(key, limit, windowMs);
|
|
100
|
+
if (!decision.allowed) {
|
|
101
|
+
const resetAt = Date.now() + decision.retryAfterMs;
|
|
140
102
|
throw defaultErrors.TooManyRequests({
|
|
141
|
-
retryAfter,
|
|
103
|
+
retryAfter: Math.ceil(decision.retryAfterMs / 1000),
|
|
142
104
|
resetAt: new Date(resetAt).toISOString(),
|
|
143
105
|
limit,
|
|
144
106
|
});
|
|
145
107
|
}
|
|
146
|
-
timestamps.push(now);
|
|
147
|
-
store.set(key, timestamps);
|
|
148
108
|
return cap._resolverOnly(input, ctx);
|
|
149
109
|
},
|
|
150
110
|
}));
|
package/dist/enhancers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enhancers.js","sourceRoot":"","sources":["../src/enhancers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"enhancers.js","sourceRoot":"","sources":["../src/enhancers.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export { defineEnhancer, withLogging, withCache, withTimeout, withRetry, withRat
|
|
|
13
13
|
export type { CacheOptions, RateLimitOptions, MetricsCollector, CircuitBreakerOptions, RollbackFn, WithRollback, } from './enhancers.js';
|
|
14
14
|
export { createExecutionEngine } from './execution-engine.js';
|
|
15
15
|
export { closeHttpServerGracefully } from './http-shutdown.js';
|
|
16
|
+
export { createMemoryCacheStore, createMemoryRateLimitStore } from './stores.js';
|
|
17
|
+
export type { CacheStore, RateLimitStore, RateLimitResult, MemoryCacheStoreOptions, MemoryRateLimitStoreOptions, } from './stores.js';
|
|
16
18
|
export type { CapabilityRequest, CapabilityResponse, SerializedError, InvokeFn, ExecutionEngineOptions, } from './execution-engine.js';
|
|
17
19
|
export { definePlugin, mergePlugins } from './plugin.js';
|
|
18
20
|
export type { Plugin, MergedPlugins } from './plugin.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3E,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG5E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACvG,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG7G,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxG,YAAY,EACV,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,WAAW,EACX,YAAY,EACZ,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,cAAc,EACd,WAAW,EACX,SAAS,EACT,WAAW,EACX,SAAS,EACT,aAAa,EACb,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,UAAU,EACV,YAAY,GACb,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3E,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG5E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACvG,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG7G,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxG,YAAY,EACV,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,WAAW,EACX,YAAY,EACZ,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,cAAc,EACd,WAAW,EACX,SAAS,EACT,WAAW,EACX,SAAS,EACT,aAAa,EACb,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,UAAU,EACV,YAAY,GACb,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAG/D,OAAO,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACjF,YAAY,EACV,UAAU,EACV,cAAc,EACd,eAAe,EACf,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,QAAQ,EACR,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EAAE,SAAS,EAAE,yBAAyB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG5G,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,8 @@ export { defineEnhancer, withLogging, withCache, withTimeout, withRetry, withRat
|
|
|
14
14
|
// Execution engine
|
|
15
15
|
export { createExecutionEngine } from './execution-engine.js';
|
|
16
16
|
export { closeHttpServerGracefully } from './http-shutdown.js';
|
|
17
|
+
// Stores (pluggable backends for withCache / withRateLimit)
|
|
18
|
+
export { createMemoryCacheStore, createMemoryRateLimitStore } from './stores.js';
|
|
17
19
|
// Plugin
|
|
18
20
|
export { definePlugin, mergePlugins } from './plugin.js';
|
|
19
21
|
// Server
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,SAAS;AACT,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG3E,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGxD,SAAS;AACT,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGvG,aAAa;AACb,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAexG,YAAY;AACZ,OAAO,EACL,cAAc,EACd,WAAW,EACX,SAAS,EACT,WAAW,EACX,SAAS,EACT,aAAa,EACb,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAUxB,mBAAmB;AACnB,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,SAAS;AACT,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG3E,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGxD,SAAS;AACT,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGvG,aAAa;AACb,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAexG,YAAY;AACZ,OAAO,EACL,cAAc,EACd,WAAW,EACX,SAAS,EACT,WAAW,EACX,SAAS,EACT,aAAa,EACb,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAUxB,mBAAmB;AACnB,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,4DAA4D;AAC5D,OAAO,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAgBjF,SAAS;AACT,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGzD,SAAS;AACT,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGzD,YAAY;AACZ,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/stores.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stores.ts — pluggable backends for withCache and withRateLimit.
|
|
3
|
+
*
|
|
4
|
+
* The in-memory implementations below are the defaults and are correct for a
|
|
5
|
+
* single process. Behind a load balancer they silently diverge: each instance
|
|
6
|
+
* keeps its own cache and its own rate-limit counters (N instances = N times
|
|
7
|
+
* the intended limit). For multi-instance deployments plug in a shared
|
|
8
|
+
* backend — see @capixjs/store-redis.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Pluggable cache backend for {@link withCache}.
|
|
12
|
+
* Implementations own expiry and capacity policy.
|
|
13
|
+
*/
|
|
14
|
+
export type CacheStore = {
|
|
15
|
+
/** Returns the cached value, or undefined when missing or expired. */
|
|
16
|
+
get(key: string): unknown | undefined | Promise<unknown | undefined>;
|
|
17
|
+
/** Stores a value for ttlMs milliseconds. */
|
|
18
|
+
set(key: string, value: unknown, ttlMs: number): void | Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
export type MemoryCacheStoreOptions = {
|
|
21
|
+
/** Maximum number of entries; least-recently-used are evicted. Default: 1_000. */
|
|
22
|
+
readonly maxSize?: number;
|
|
23
|
+
};
|
|
24
|
+
/** In-memory LRU cache store — the withCache default. Single-process only. */
|
|
25
|
+
export declare function createMemoryCacheStore(options?: MemoryCacheStoreOptions): CacheStore;
|
|
26
|
+
export type RateLimitResult = {
|
|
27
|
+
readonly allowed: true;
|
|
28
|
+
} | {
|
|
29
|
+
readonly allowed: false;
|
|
30
|
+
readonly retryAfterMs: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Pluggable rate-limit backend for {@link withRateLimit}.
|
|
34
|
+
*
|
|
35
|
+
* `hit` records an attempt and decides in one call; implementations must make
|
|
36
|
+
* that atomic per key (a check-then-set race would admit bursts over the
|
|
37
|
+
* limit). Window semantics belong to the store: the in-memory default uses a
|
|
38
|
+
* sliding window, the Redis adapter a fixed window.
|
|
39
|
+
*/
|
|
40
|
+
export type RateLimitStore = {
|
|
41
|
+
hit(key: string, limit: number, windowMs: number): RateLimitResult | Promise<RateLimitResult>;
|
|
42
|
+
};
|
|
43
|
+
export type MemoryRateLimitStoreOptions = {
|
|
44
|
+
/**
|
|
45
|
+
* Maximum number of distinct keys tracked. Default: 10_000.
|
|
46
|
+
* When exceeded, keys with no activity in the current window are swept;
|
|
47
|
+
* if every key is still active, the oldest-tracked keys are evicted.
|
|
48
|
+
*/
|
|
49
|
+
readonly maxKeys?: number;
|
|
50
|
+
};
|
|
51
|
+
/** In-memory sliding-window rate-limit store — the withRateLimit default. Single-process only. */
|
|
52
|
+
export declare function createMemoryRateLimitStore(options?: MemoryRateLimitStoreOptions): RateLimitStore;
|
|
53
|
+
//# sourceMappingURL=stores.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,sEAAsE;IACtE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACrE,6CAA6C;IAC7C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,kFAAkF;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,8EAA8E;AAC9E,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CA4BxF;AAMD,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAC/F,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,kGAAkG;AAClG,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,2BAAgC,GAAG,cAAc,CAyCpG"}
|
package/dist/stores.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stores.ts — pluggable backends for withCache and withRateLimit.
|
|
3
|
+
*
|
|
4
|
+
* The in-memory implementations below are the defaults and are correct for a
|
|
5
|
+
* single process. Behind a load balancer they silently diverge: each instance
|
|
6
|
+
* keeps its own cache and its own rate-limit counters (N instances = N times
|
|
7
|
+
* the intended limit). For multi-instance deployments plug in a shared
|
|
8
|
+
* backend — see @capixjs/store-redis.
|
|
9
|
+
*/
|
|
10
|
+
/** In-memory LRU cache store — the withCache default. Single-process only. */
|
|
11
|
+
export function createMemoryCacheStore(options = {}) {
|
|
12
|
+
const maxSize = options.maxSize ?? 1_000;
|
|
13
|
+
const entries = new Map();
|
|
14
|
+
return {
|
|
15
|
+
get(key) {
|
|
16
|
+
const cached = entries.get(key);
|
|
17
|
+
if (cached === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (cached.expiresAt <= Date.now()) {
|
|
20
|
+
entries.delete(key); // expired — don't let dead entries occupy capacity
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
// Refresh recency for LRU ordering
|
|
24
|
+
entries.delete(key);
|
|
25
|
+
entries.set(key, cached);
|
|
26
|
+
return cached.value;
|
|
27
|
+
},
|
|
28
|
+
set(key, value, ttlMs) {
|
|
29
|
+
if (entries.size >= maxSize && !entries.has(key)) {
|
|
30
|
+
// Evict least-recently-used (first key in Map insertion order)
|
|
31
|
+
const oldest = entries.keys().next().value;
|
|
32
|
+
if (oldest !== undefined)
|
|
33
|
+
entries.delete(oldest);
|
|
34
|
+
}
|
|
35
|
+
entries.delete(key);
|
|
36
|
+
entries.set(key, { value, expiresAt: Date.now() + ttlMs });
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** In-memory sliding-window rate-limit store — the withRateLimit default. Single-process only. */
|
|
41
|
+
export function createMemoryRateLimitStore(options = {}) {
|
|
42
|
+
const maxKeys = options.maxKeys ?? 10_000;
|
|
43
|
+
const buckets = new Map();
|
|
44
|
+
function evictStale(now, windowMs) {
|
|
45
|
+
const cutoff = now - windowMs;
|
|
46
|
+
for (const [k, ts] of buckets) {
|
|
47
|
+
const last = ts[ts.length - 1];
|
|
48
|
+
if (last === undefined || last <= cutoff)
|
|
49
|
+
buckets.delete(k);
|
|
50
|
+
}
|
|
51
|
+
// Every key still active — hard-cap by evicting oldest-tracked keys
|
|
52
|
+
if (buckets.size > maxKeys) {
|
|
53
|
+
for (const k of buckets.keys()) {
|
|
54
|
+
buckets.delete(k);
|
|
55
|
+
if (buckets.size <= maxKeys)
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
hit(key, limit, windowMs) {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const windowStart = now - windowMs;
|
|
64
|
+
if (buckets.size > maxKeys)
|
|
65
|
+
evictStale(now, windowMs);
|
|
66
|
+
let timestamps = buckets.get(key) ?? [];
|
|
67
|
+
timestamps = timestamps.filter((t) => t > windowStart);
|
|
68
|
+
if (timestamps.length >= limit) {
|
|
69
|
+
// Oldest timestamp in the window determines when a slot frees up
|
|
70
|
+
const oldestInWindow = timestamps[0] ?? now;
|
|
71
|
+
buckets.set(key, timestamps);
|
|
72
|
+
return { allowed: false, retryAfterMs: oldestInWindow + windowMs - now };
|
|
73
|
+
}
|
|
74
|
+
timestamps.push(now);
|
|
75
|
+
buckets.set(key, timestamps);
|
|
76
|
+
return { allowed: true };
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=stores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.js","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capixjs/core",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.22",
|
|
4
4
|
"description": "Capability-based Node.js server framework — core",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -37,11 +37,13 @@
|
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^20.0.0",
|
|
39
39
|
"typescript": "^5.5.0",
|
|
40
|
-
"vitest": "^1.6.0"
|
|
40
|
+
"vitest": "^1.6.0",
|
|
41
|
+
"@vitest/coverage-v8": "^1.6.1"
|
|
41
42
|
},
|
|
42
43
|
"scripts": {
|
|
43
44
|
"build": "tsc",
|
|
44
45
|
"test": "vitest run",
|
|
46
|
+
"test:coverage": "vitest run --coverage",
|
|
45
47
|
"typecheck": "tsc --noEmit"
|
|
46
48
|
}
|
|
47
49
|
}
|