@yaebal/again 0.0.1 → 0.0.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/README.md +15 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +13 -2
- package/lib/index.js.map +1 -1
- package/lib/index.test.js +12 -1
- package/lib/index.test.js.map +1 -1
- package/package.json +2 -2
- package/src/index.test.ts +16 -1
- package/src/index.ts +24 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @yaebal/again
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
auto-retry on 429 / flood-wait and transient 5xx errors.
|
|
4
4
|
|
|
5
5
|
## install
|
|
6
6
|
|
|
@@ -8,6 +8,20 @@ max retries after the first attempt. defaults to 3.
|
|
|
8
8
|
pnpm add @yaebal/again
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { autoRetry } from "@yaebal/again";
|
|
15
|
+
|
|
16
|
+
bot.install(autoRetry({ maxRetries: 5 }));
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
the direct api-hook form is still available:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
autoRetry(bot.api, { maxRetries: 5 });
|
|
23
|
+
```
|
|
24
|
+
|
|
11
25
|
---
|
|
12
26
|
|
|
13
27
|
part of [**yaebal**](https://github.com/neverlane/yaebal) — a type-safe, runtime-agnostic Telegram Bot API framework. MIT.
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Api, type ErrorAction } from "@yaebal/core";
|
|
1
|
+
import { type Api, type BotPlugin, type ErrorAction } from "@yaebal/core";
|
|
2
2
|
export interface AutoRetryOptions {
|
|
3
3
|
/** max retries after the first attempt. defaults to 3. */
|
|
4
4
|
maxRetries?: number;
|
|
@@ -12,6 +12,8 @@ export interface AutoRetryOptions {
|
|
|
12
12
|
* pure (no I/O) — exported so the policy is unit-testable on its own.
|
|
13
13
|
*/
|
|
14
14
|
export declare function decideRetry(error: unknown, attempt: number, options?: AutoRetryOptions): ErrorAction | undefined;
|
|
15
|
-
/**
|
|
15
|
+
/** create an installable bot plugin: `bot.install(autoRetry())`. */
|
|
16
|
+
export declare function autoRetry(options?: AutoRetryOptions): BotPlugin;
|
|
17
|
+
/** install auto-retry on a bot's API directly: `autoRetry(bot.api)`. */
|
|
16
18
|
export declare function autoRetry(api: Api, options?: AutoRetryOptions): void;
|
|
17
19
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAiB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAiB,MAAM,cAAc,CAAC;AAEzF,MAAM,WAAW,gBAAgB;IAChC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAC1B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,gBAAqB,GAC5B,WAAW,GAAG,SAAS,CAsBzB;AAUD,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;AACjE,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -23,10 +23,21 @@ export function decideRetry(error, attempt, options = {}) {
|
|
|
23
23
|
}
|
|
24
24
|
return undefined;
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
function isApi(value) {
|
|
27
|
+
return typeof value?.onError === "function";
|
|
28
|
+
}
|
|
29
|
+
function installAutoRetry(api, options = {}) {
|
|
28
30
|
api.onError((_method, error, attempt) => decideRetry(error, attempt, options));
|
|
29
31
|
}
|
|
32
|
+
export function autoRetry(apiOrOptions, options = {}) {
|
|
33
|
+
if (isApi(apiOrOptions))
|
|
34
|
+
return installAutoRetry(apiOrOptions, options);
|
|
35
|
+
const pluginOptions = apiOrOptions ?? {};
|
|
36
|
+
return (bot) => {
|
|
37
|
+
installAutoRetry(bot.api, pluginOptions);
|
|
38
|
+
return bot;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
30
41
|
function parseRetryAfter(message) {
|
|
31
42
|
const m = message.match(/retry after (\d+)/i);
|
|
32
43
|
return m ? Number(m[1]) : undefined;
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,aAAa,EAAE,MAAM,cAAc,CAAC;AAWzF;;;GAGG;AACH,MAAM,UAAU,WAAW,CAC1B,KAAc,EACd,OAAe,EACf,UAA4B,EAAE;IAE9B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC;IAChD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC;IAExD,IAAI,OAAO,GAAG,UAAU;QAAE,OAAO,SAAS,CAAC;IAC3C,IAAI,CAAC,CAAC,KAAK,YAAY,aAAa,CAAC;QAAE,OAAO,SAAS,CAAC;IAExD,qEAAqE;IACrE,IAAI,KAAK,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;QACxB,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,UAAU,IAAI,CAAC,IAAI,OAAO,CAAC;QAE3C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC;IACvE,CAAC;IAED,sCAAsC;IACtC,IAAI,eAAe,IAAI,KAAK,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;QAC1C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,GAAG,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC;IAC5E,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,KAAK,CAAC,KAAyC;IACvD,OAAO,OAAQ,KAAyB,EAAE,OAAO,KAAK,UAAU,CAAC;AAClE,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAQ,EAAE,UAA4B,EAAE;IACjE,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAChF,CAAC;AAMD,MAAM,UAAU,SAAS,CACxB,YAAqC,EACrC,UAA4B,EAAE;IAE9B,IAAI,KAAK,CAAC,YAAY,CAAC;QAAE,OAAO,gBAAgB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAExE,MAAM,aAAa,GAAG,YAAY,IAAI,EAAE,CAAC;IAEzC,OAAO,CAAC,GAAG,EAAE,EAAE;QACd,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QACzC,OAAO,GAAG,CAAC;IACZ,CAAC,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,OAAe;IACvC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAE9C,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACrC,CAAC"}
|
package/lib/index.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import { TelegramError } from "@yaebal/core";
|
|
4
|
-
import { decideRetry } from "./index.js";
|
|
4
|
+
import { autoRetry, decideRetry } from "./index.js";
|
|
5
5
|
test("429 with retry_after waits exactly that long", () => {
|
|
6
6
|
assert.deepEqual(decideRetry(new TelegramError("sendMessage", 429, "Too Many Requests: retry after 7"), 1), { retry: true, delayMs: 7000 });
|
|
7
7
|
});
|
|
@@ -26,4 +26,15 @@ test("maxDelayMs caps the wait", () => {
|
|
|
26
26
|
test("retryOnInternal:false skips 5xx", () => {
|
|
27
27
|
assert.equal(decideRetry(new TelegramError("x", 503, "Service Unavailable"), 1, { retryOnInternal: false }), undefined);
|
|
28
28
|
});
|
|
29
|
+
test("autoRetry can be installed as a bot plugin", () => {
|
|
30
|
+
let hook;
|
|
31
|
+
const api = {
|
|
32
|
+
onError: (h) => {
|
|
33
|
+
hook = h;
|
|
34
|
+
return api;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
autoRetry({ maxRetries: 1 })({ api });
|
|
38
|
+
assert.equal(typeof hook, "function");
|
|
39
|
+
});
|
|
29
40
|
//# sourceMappingURL=index.test.js.map
|
package/lib/index.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEpD,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACzD,MAAM,CAAC,SAAS,CACf,WAAW,CAAC,IAAI,aAAa,CAAC,aAAa,EAAE,GAAG,EAAE,kCAAkC,CAAC,EAAE,CAAC,CAAC,EACzF,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAC9B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;IACjD,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,aAAa,CAAC,aAAa,EAAE,GAAG,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE;QACtF,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,IAAI;KACb,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;IAClD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,aAAa,CAAC,aAAa,EAAE,GAAG,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACjG,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;IAC9C,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,aAAa,CAAC,aAAa,EAAE,GAAG,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAC/F,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qCAAqC,EAAE,GAAG,EAAE;IAChD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACrC,MAAM,CAAC,SAAS,CACf,WAAW,CAAC,IAAI,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,iBAAiB,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,EACpF,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAC9B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC5C,MAAM,CAAC,KAAK,CACX,WAAW,CAAC,IAAI,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,qBAAqB,CAAC,EAAE,CAAC,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,EAC9F,SAAS,CACT,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACvD,IAAI,IAA2B,CAAC;IAChC,MAAM,GAAG,GAAG;QACX,OAAO,EAAE,CAAC,CAAY,EAAE,EAAE;YACzB,IAAI,GAAG,CAAC,CAAC;YACT,OAAO,GAAU,CAAC;QACnB,CAAC;KACM,CAAC;IAET,SAAS,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAW,CAAC,CAAC;IAE/C,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,UAAU,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yaebal/again",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "yaebal auto-retry plugin — retries on 429/flood-wait and transient 5xx.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@yaebal/core": "0.0.
|
|
19
|
+
"@yaebal/core": "0.0.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "latest"
|
package/src/index.test.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
|
+
import type { Api, ErrorHook } from "@yaebal/core";
|
|
3
4
|
import { TelegramError } from "@yaebal/core";
|
|
4
|
-
import { decideRetry } from "./index.js";
|
|
5
|
+
import { autoRetry, decideRetry } from "./index.js";
|
|
5
6
|
|
|
6
7
|
test("429 with retry_after waits exactly that long", () => {
|
|
7
8
|
assert.deepEqual(
|
|
@@ -42,3 +43,17 @@ test("retryOnInternal:false skips 5xx", () => {
|
|
|
42
43
|
undefined,
|
|
43
44
|
);
|
|
44
45
|
});
|
|
46
|
+
|
|
47
|
+
test("autoRetry can be installed as a bot plugin", () => {
|
|
48
|
+
let hook: ErrorHook | undefined;
|
|
49
|
+
const api = {
|
|
50
|
+
onError: (h: ErrorHook) => {
|
|
51
|
+
hook = h;
|
|
52
|
+
return api as Api;
|
|
53
|
+
},
|
|
54
|
+
} as Api;
|
|
55
|
+
|
|
56
|
+
autoRetry({ maxRetries: 1 })({ api } as never);
|
|
57
|
+
|
|
58
|
+
assert.equal(typeof hook, "function");
|
|
59
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Api, type ErrorAction, TelegramError } from "@yaebal/core";
|
|
1
|
+
import { type Api, type BotPlugin, type ErrorAction, TelegramError } from "@yaebal/core";
|
|
2
2
|
|
|
3
3
|
export interface AutoRetryOptions {
|
|
4
4
|
/** max retries after the first attempt. defaults to 3. */
|
|
@@ -41,11 +41,32 @@ export function decideRetry(
|
|
|
41
41
|
return undefined;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
function isApi(value: Api | AutoRetryOptions | undefined): value is Api {
|
|
45
|
+
return typeof (value as Api | undefined)?.onError === "function";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function installAutoRetry(api: Api, options: AutoRetryOptions = {}): void {
|
|
46
49
|
api.onError((_method, error, attempt) => decideRetry(error, attempt, options));
|
|
47
50
|
}
|
|
48
51
|
|
|
52
|
+
/** create an installable bot plugin: `bot.install(autoRetry())`. */
|
|
53
|
+
export function autoRetry(options?: AutoRetryOptions): BotPlugin;
|
|
54
|
+
/** install auto-retry on a bot's API directly: `autoRetry(bot.api)`. */
|
|
55
|
+
export function autoRetry(api: Api, options?: AutoRetryOptions): void;
|
|
56
|
+
export function autoRetry(
|
|
57
|
+
apiOrOptions?: Api | AutoRetryOptions,
|
|
58
|
+
options: AutoRetryOptions = {},
|
|
59
|
+
): BotPlugin | void {
|
|
60
|
+
if (isApi(apiOrOptions)) return installAutoRetry(apiOrOptions, options);
|
|
61
|
+
|
|
62
|
+
const pluginOptions = apiOrOptions ?? {};
|
|
63
|
+
|
|
64
|
+
return (bot) => {
|
|
65
|
+
installAutoRetry(bot.api, pluginOptions);
|
|
66
|
+
return bot;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
49
70
|
function parseRetryAfter(message: string): number | undefined {
|
|
50
71
|
const m = message.match(/retry after (\d+)/i);
|
|
51
72
|
|