@adaptive-concurrency-toolkit/core 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/LICENSE +21 -0
- package/README.md +227 -0
- package/dist/clock.d.ts +20 -0
- package/dist/clock.js +23 -0
- package/dist/clock.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/limit/aimd-limit.d.ts +51 -0
- package/dist/limit/aimd-limit.js +75 -0
- package/dist/limit/aimd-limit.js.map +1 -0
- package/dist/limit/fixed-limit.d.ts +17 -0
- package/dist/limit/fixed-limit.js +22 -0
- package/dist/limit/fixed-limit.js.map +1 -0
- package/dist/limit/gradient2-limit.d.ts +80 -0
- package/dist/limit/gradient2-limit.js +139 -0
- package/dist/limit/gradient2-limit.js.map +1 -0
- package/dist/limit/index.d.ts +5 -0
- package/dist/limit/index.js +5 -0
- package/dist/limit/index.js.map +1 -0
- package/dist/limit/limit.d.ts +30 -0
- package/dist/limit/limit.js +2 -0
- package/dist/limit/limit.js.map +1 -0
- package/dist/limit/vegas-limit.d.ts +80 -0
- package/dist/limit/vegas-limit.js +137 -0
- package/dist/limit/vegas-limit.js.map +1 -0
- package/dist/limiter/index.d.ts +2 -0
- package/dist/limiter/index.js +2 -0
- package/dist/limiter/index.js.map +1 -0
- package/dist/limiter/limiter.d.ts +18 -0
- package/dist/limiter/limiter.js +2 -0
- package/dist/limiter/limiter.js.map +1 -0
- package/dist/limiter/simple-limiter.d.ts +28 -0
- package/dist/limiter/simple-limiter.js +70 -0
- package/dist/limiter/simple-limiter.js.map +1 -0
- package/dist/types.d.ts +27 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util/ema.d.ts +27 -0
- package/dist/util/ema.js +53 -0
- package/dist/util/ema.js.map +1 -0
- package/dist/util/listeners.d.ts +12 -0
- package/dist/util/listeners.js +49 -0
- package/dist/util/listeners.js.map +1 -0
- package/dist/util/math.d.ts +1 -0
- package/dist/util/math.js +4 -0
- package/dist/util/math.js.map +1 -0
- package/package.json +54 -0
package/dist/util/ema.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exponential moving average with lazy initialization. The first value seeds
|
|
3
|
+
* the average so the EMA does not bias toward zero during warm-up.
|
|
4
|
+
*
|
|
5
|
+
* Update formula (after init):
|
|
6
|
+
* v ← α·x + (1 − α)·v
|
|
7
|
+
*
|
|
8
|
+
* Hot path is one multiply + one fused-multiply-add equivalent; no allocations.
|
|
9
|
+
*/
|
|
10
|
+
export class Ema {
|
|
11
|
+
alpha;
|
|
12
|
+
_value = 0;
|
|
13
|
+
_initialized = false;
|
|
14
|
+
/**
|
|
15
|
+
* @param alpha smoothing factor in (0, 1]. Higher α reacts faster, lower α
|
|
16
|
+
* smooths more. Equivalent time constant ≈ 1/α samples.
|
|
17
|
+
*/
|
|
18
|
+
constructor(alpha) {
|
|
19
|
+
if (!(alpha > 0 && alpha <= 1)) {
|
|
20
|
+
throw new RangeError(`alpha must be in (0, 1], got ${alpha}`);
|
|
21
|
+
}
|
|
22
|
+
this.alpha = alpha;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Construct an EMA whose smoothing factor approximates `n` samples of memory.
|
|
26
|
+
*/
|
|
27
|
+
static withWindow(n) {
|
|
28
|
+
if (!(n >= 1))
|
|
29
|
+
throw new RangeError(`n must be >= 1, got ${n}`);
|
|
30
|
+
return new Ema(2 / (n + 1));
|
|
31
|
+
}
|
|
32
|
+
update(x) {
|
|
33
|
+
if (this._initialized) {
|
|
34
|
+
this._value = this.alpha * x + (1 - this.alpha) * this._value;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this._value = x;
|
|
38
|
+
this._initialized = true;
|
|
39
|
+
}
|
|
40
|
+
return this._value;
|
|
41
|
+
}
|
|
42
|
+
get value() {
|
|
43
|
+
return this._value;
|
|
44
|
+
}
|
|
45
|
+
get initialized() {
|
|
46
|
+
return this._initialized;
|
|
47
|
+
}
|
|
48
|
+
reset() {
|
|
49
|
+
this._value = 0;
|
|
50
|
+
this._initialized = false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=ema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ema.js","sourceRoot":"","sources":["../../src/util/ema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,OAAO,GAAG;IACL,KAAK,CAAS;IACf,MAAM,GAAG,CAAC,CAAC;IACX,YAAY,GAAG,KAAK,CAAC;IAE7B;;;OAGG;IACH,YAAY,KAAa;QACvB,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,UAAU,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,CAAS;QACzB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC;QAChE,OAAO,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,CAAS;QACd,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;YAChB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { LimitChangeListener, Unsubscribe } from '../types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Tiny multi-listener registry. Optimized for the common case of zero or one
|
|
4
|
+
* subscriber — avoids allocating an array in that case.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ChangeListeners {
|
|
7
|
+
private single;
|
|
8
|
+
private many;
|
|
9
|
+
add(listener: LimitChangeListener): Unsubscribe;
|
|
10
|
+
emit(newLimit: number): void;
|
|
11
|
+
private remove;
|
|
12
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny multi-listener registry. Optimized for the common case of zero or one
|
|
3
|
+
* subscriber — avoids allocating an array in that case.
|
|
4
|
+
*/
|
|
5
|
+
export class ChangeListeners {
|
|
6
|
+
single;
|
|
7
|
+
many;
|
|
8
|
+
add(listener) {
|
|
9
|
+
if (this.many !== undefined) {
|
|
10
|
+
this.many.push(listener);
|
|
11
|
+
}
|
|
12
|
+
else if (this.single === undefined) {
|
|
13
|
+
this.single = listener;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
this.many = [this.single, listener];
|
|
17
|
+
this.single = undefined;
|
|
18
|
+
}
|
|
19
|
+
return () => this.remove(listener);
|
|
20
|
+
}
|
|
21
|
+
emit(newLimit) {
|
|
22
|
+
if (this.single !== undefined) {
|
|
23
|
+
this.single(newLimit);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const list = this.many;
|
|
27
|
+
if (list !== undefined) {
|
|
28
|
+
for (let i = 0; i < list.length; i++)
|
|
29
|
+
list[i](newLimit);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
remove(listener) {
|
|
33
|
+
if (this.single === listener) {
|
|
34
|
+
this.single = undefined;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const list = this.many;
|
|
38
|
+
if (list === undefined)
|
|
39
|
+
return;
|
|
40
|
+
const idx = list.indexOf(listener);
|
|
41
|
+
if (idx >= 0)
|
|
42
|
+
list.splice(idx, 1);
|
|
43
|
+
if (list.length === 1) {
|
|
44
|
+
this.single = list[0];
|
|
45
|
+
this.many = undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=listeners.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listeners.js","sourceRoot":"","sources":["../../src/util/listeners.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,OAAO,eAAe;IAClB,MAAM,CAAkC;IACxC,IAAI,CAAoC;IAEhD,GAAG,CAAC,QAA6B;QAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QAC1B,CAAC;QACD,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,QAAgB;QACnB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACtB,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE;gBAAE,IAAI,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAEO,MAAM,CAAC,QAA6B;QAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;YACxB,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,GAAG,IAAI,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAClC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;QACxB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function clamp(x: number, lo: number, hi: number): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.js","sourceRoot":"","sources":["../../src/util/math.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,KAAK,CAAC,CAAS,EAAE,EAAU,EAAE,EAAU;IACrD,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@adaptive-concurrency-toolkit/core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Core adaptive concurrency algorithms (AIMD, Vegas, Gradient2) inspired by Netflix concurrency-limits",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"!dist/**/*.tsbuildinfo",
|
|
9
|
+
"!**/*.test.*"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"./limit": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/limit/index.d.ts",
|
|
23
|
+
"default": "./dist/limit/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"./limiter": {
|
|
27
|
+
"import": {
|
|
28
|
+
"types": "./dist/limiter/index.d.ts",
|
|
29
|
+
"default": "./dist/limiter/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^25.9.0",
|
|
39
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
40
|
+
"rimraf": "^6.0.1",
|
|
41
|
+
"typescript": "^6.0.3",
|
|
42
|
+
"vitest": "^4.1.6"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -b tsconfig.build.json",
|
|
49
|
+
"typecheck": "tsc -b tsconfig.json",
|
|
50
|
+
"test": "vitest run --coverage",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"clean": "rimraf dist .turbo *.tsbuildinfo"
|
|
53
|
+
}
|
|
54
|
+
}
|