stitchkit 0.95.2 → 0.96.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 +118 -0
- package/dist/agent-runtime/runtime-internals.d.ts +6 -0
- package/dist/agent-runtime/runtime-internals.d.ts.map +1 -1
- package/dist/application/grammy-bot.d.ts +44 -0
- package/dist/application/grammy-bot.d.ts.map +1 -0
- package/dist/application/grammy-update-admission.d.ts +35 -0
- package/dist/application/grammy-update-admission.d.ts.map +1 -0
- package/dist/application/grammy.d.ts +23 -4
- package/dist/application/grammy.d.ts.map +1 -1
- package/dist/application/kernel-admission.d.ts +11 -0
- package/dist/application/kernel-admission.d.ts.map +1 -1
- package/dist/application/kernel-resource-context.d.ts.map +1 -1
- package/dist/application/resource.d.ts +22 -0
- package/dist/application/resource.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/harness.js +1 -1
- package/dist/entrypoints/agent-runtime.js +1 -1
- package/dist/entrypoints/application/grammy.d.ts +2 -1
- package/dist/entrypoints/application/grammy.d.ts.map +1 -1
- package/dist/entrypoints/application/grammy.js +139 -5
- package/dist/entrypoints/application.d.ts +1 -1
- package/dist/entrypoints/application.d.ts.map +1 -1
- package/dist/entrypoints/application.js +1 -1
- package/dist/entrypoints/live.js +6 -1
- package/dist/entrypoints/observability.d.ts +1 -0
- package/dist/entrypoints/observability.d.ts.map +1 -1
- package/dist/entrypoints/observability.js +46 -4
- package/dist/entrypoints/telegram.d.ts +4 -0
- package/dist/entrypoints/telegram.d.ts.map +1 -1
- package/dist/entrypoints/telegram.js +680 -88
- package/dist/entrypoints/testing.js +1 -1
- package/dist/{index-w906953s.js → index-5qn1kx34.js} +48 -0
- package/dist/{index-8zja4pkf.js → index-nts4a2br.js} +5 -1
- package/dist/live/watch-client.d.ts.map +1 -1
- package/dist/observability/json-logger.d.ts +38 -0
- package/dist/observability/json-logger.d.ts.map +1 -0
- package/dist/telegram/bot-api.d.ts +50 -0
- package/dist/telegram/bot-api.d.ts.map +1 -0
- package/dist/telegram/broadcast-state.d.ts +67 -0
- package/dist/telegram/broadcast-state.d.ts.map +1 -0
- package/dist/telegram/broadcast.d.ts +93 -0
- package/dist/telegram/broadcast.d.ts.map +1 -0
- package/dist/telegram/local-files.d.ts +63 -0
- package/dist/telegram/local-files.d.ts.map +1 -0
- package/dist/telegram/operator-channel.d.ts +72 -0
- package/dist/telegram/operator-channel.d.ts.map +1 -0
- package/llms/application-grammy.txt +5 -0
- package/llms/application.2.txt +1 -0
- package/llms/application.txt +51 -13
- package/llms/observability.txt +5 -0
- package/llms/server.4.txt +3 -0
- package/llms/stitchkit.txt +5 -5
- package/llms/telegram.txt +159 -3
- package/llms/{upgrading-0.37.0-0.53.0.txt → upgrading-0.37.0-0.56.0.txt} +130 -3
- package/llms/{upgrading-0.55.0-0.68.0.txt → upgrading-0.57.0-0.69.0.txt} +44 -130
- package/llms/{upgrading-0.69.0-0.95.0.txt → upgrading-0.70.0-0.96.0.txt} +40 -44
- package/llms/upgrading.txt +3 -3
- package/llms-full.txt +265 -23
- package/llms.txt +10 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,124 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.96.0] — 2026-09-24
|
|
19
|
+
|
|
20
|
+
### ⚠️ Breaking changes
|
|
21
|
+
|
|
22
|
+
**Who must act:** only a project whose tests call a managed resource's phase
|
|
23
|
+
with a context object they wrote by hand — it adds `admission`. Every other
|
|
24
|
+
project raises its range; grammY polling admits updates by batch with no edit.
|
|
25
|
+
|
|
26
|
+
- `stitchkit/application` — **`ManagedResourceContext` carries the
|
|
27
|
+
application's admission.** A resource that *fetches* work — a poller, a queue
|
|
28
|
+
consumer — should not fetch while the application is starting or degraded,
|
|
29
|
+
and until now reached the handle through a closure over a variable assigned
|
|
30
|
+
after the graph that contained it. `context.admission`
|
|
31
|
+
(`ManagedResourceAdmission`) offers `acquire()` and
|
|
32
|
+
`acquireWhenAccepting(signal)`, which waits and then holds one operation,
|
|
33
|
+
rejecting on abort and once the application can no longer admit. The kernel
|
|
34
|
+
supplies it to every phase; a context built by hand in a test must add it:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// before
|
|
38
|
+
resource.close?.({ applicationId, signal, now, reportHealth, use })
|
|
39
|
+
// after
|
|
40
|
+
resource.close?.({ applicationId, signal, now, reportHealth, use,
|
|
41
|
+
admission: { acquire: () => null, acquireWhenAccepting: () => new Promise(() => {}) } })
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- `stitchkit/application/grammy` — **`grammyBotResources`**: a long-polling bot
|
|
47
|
+
as `telegram-configuration` (the application's `configure`, e.g.
|
|
48
|
+
`setMyCommands`, run before polling on every start) and `telegram-polling`,
|
|
49
|
+
both after the bot's `dependsOn`, with those stable ids
|
|
50
|
+
(`TELEGRAM_CONFIGURATION_RESOURCE_ID`, `TELEGRAM_POLLING_RESOURCE_ID`).
|
|
51
|
+
Three consuming bots each assembled this pair by hand under their own names.
|
|
52
|
+
- `stitchkit/application/grammy` — `onEnded` on `grammyPollingResource` and
|
|
53
|
+
`grammyBotResources`: called once when polling that was ready ends on its own
|
|
54
|
+
(401, 409, a rethrowing `bot.catch`), never for a requested stop. grammY's
|
|
55
|
+
poller does not recover in-process and the framework does not exit processes,
|
|
56
|
+
so this is the one place an application turns that end into its shutdown.
|
|
57
|
+
- `stitchkit/observability` — **`createJsonLogger`**: the process journal for a
|
|
58
|
+
bot or worker. One JSON line per call in pino's shape (numeric `level`,
|
|
59
|
+
epoch `time`, `msg`) on standard output, through `createBoundedLogger`, so an
|
|
60
|
+
`Error` under any key keeps name, message, stack and cause — a consuming
|
|
61
|
+
bot's startup failure had reached its journal as `"error":{}` — and secrets
|
|
62
|
+
are masked. `level`, static `fields`, injectable `write`; `JSON_LOG_LEVELS`
|
|
63
|
+
names the numbers a line reader matches on (`error` is 50).
|
|
64
|
+
- `stitchkit/telegram` — **`runTelegramBroadcast`**: a broadcast resumable by
|
|
65
|
+
name. The audience is written once, progress is an append-only journal; a
|
|
66
|
+
send in flight at a crash is `uncertain` and not repeated; blocked, gone and
|
|
67
|
+
never-started recipients are `unreachable` and never addressed again; a 429
|
|
68
|
+
waits Telegram's `retry_after`; a server error is retried up to
|
|
69
|
+
`maxAttempts`; a message Telegram cannot parse, or Telegram unreachable,
|
|
70
|
+
halts the run without charging the recipient. Pacing at `ratePerSecond` (25),
|
|
71
|
+
`dryRun`, one runner per name. `telegramBroadcastSender` is the standard
|
|
72
|
+
`send` (`sendMessage` or `copyMessage`). Two consuming bots carried the same
|
|
73
|
+
five-file subsystem with its own refusal phrases.
|
|
74
|
+
- `stitchkit/telegram` — **`createTelegramOperatorChannel`**: the operator's
|
|
75
|
+
chat, apart from the journal. `post` returns at once and never throws; paced
|
|
76
|
+
sends into forum topics, 429 honoured, the oldest message dropped on
|
|
77
|
+
overflow and every drop reported with why; `drain` and `close` for the way
|
|
78
|
+
down. `telegramOperatorSender` is the standard `send`.
|
|
79
|
+
- `stitchkit/telegram` — **`createTelegramLocalFiles`**: a file the local Bot
|
|
80
|
+
API server wrote under `<root>/<token>/`. `resolve` and `remove` stay inside
|
|
81
|
+
the bot's directory — `../`, an absolute path elsewhere and a link pointing
|
|
82
|
+
out are refused on real paths — and a refusal names a reason, never the path
|
|
83
|
+
or the token. `check()` for readiness. The recommended root variable is
|
|
84
|
+
`BOT_API_FILES_ROOT`.
|
|
85
|
+
- `stitchkit/telegram` — `callTelegramBotApi` / `TelegramBotApiError`: one Bot
|
|
86
|
+
API call over `fetch` whose refusal keeps `error_code`, `description` and
|
|
87
|
+
`parameters` for `classifyTelegramSendFailure`, and
|
|
88
|
+
`TELEGRAM_BOT_TOKEN_PATTERN` for a logger's `sensitiveUrlPatterns`.
|
|
89
|
+
- Public-surface ceilings raised for these: `stitchkit/telegram` 9 → 37,
|
|
90
|
+
`stitchkit/application/grammy` 7 → 13, `stitchkit/observability` 61 → 65,
|
|
91
|
+
`stitchkit/application` 277 → 278.
|
|
92
|
+
|
|
93
|
+
### Changed
|
|
94
|
+
|
|
95
|
+
- `stitchkit/application/grammy` — **`grammyPollingResource` admits updates by
|
|
96
|
+
batch.** grammY confirms a batch by asking for the next one, whatever its
|
|
97
|
+
middleware did, so an update that arrived while the application was
|
|
98
|
+
starting, degraded or stopping was refused and then confirmed as handled —
|
|
99
|
+
lost on every restart of two consuming bots. The resource now waits for
|
|
100
|
+
admission before each long poll, returns a batch to grammY only while holding
|
|
101
|
+
one admission lease for it, and on shutdown finishes that batch before
|
|
102
|
+
`bot.stop()` confirms the offset. A batch the application will not admit is
|
|
103
|
+
never returned; Telegram keeps it for the next process.
|
|
104
|
+
- `stitchkit/application/grammy` — `dependsOn` on both grammY resources takes
|
|
105
|
+
resource objects as well as ids, like every other resource.
|
|
106
|
+
|
|
107
|
+
### Fixed
|
|
108
|
+
|
|
109
|
+
- `stitchkit/application/grammy` — a poller that ended on its own is one
|
|
110
|
+
failure, not two. Its `drain` awaited the ended completion again and failed
|
|
111
|
+
with the same error, so the shutdown that followed was `forced` and the
|
|
112
|
+
journal carried a `drain` failure for an end that had already happened. The
|
|
113
|
+
ended completion is now drained quietly, and `onEnded` runs a macrotask later
|
|
114
|
+
so the kernel records it as `completion` before an observer's shutdown can
|
|
115
|
+
mark the application stopping.
|
|
116
|
+
|
|
117
|
+
## [0.95.3] — 2026-09-23
|
|
118
|
+
|
|
119
|
+
### Fixed
|
|
120
|
+
|
|
121
|
+
- `stitchkit/live` — the watch client took a revision as a floor across two
|
|
122
|
+
lives of the hub. Revisions count one hub's life of a key, so after an API
|
|
123
|
+
restart the new hub answered the reopen with a `full` value at revision 1
|
|
124
|
+
and the client dropped it, and every later one, as "no newer" until the new
|
|
125
|
+
counter passed the old — while showing the key live. The first answer to
|
|
126
|
+
each `open` is now taken whatever its revision; within one open the
|
|
127
|
+
counter orders frames as before.
|
|
128
|
+
- `stitchkit/agent-runtime` — a turn total no longer knows more than its
|
|
129
|
+
steps. `mergeModelTotals` took each field from the SDK aggregate, which the
|
|
130
|
+
OpenRouter adapter had already summed with `cached_tokens ?? 0`, so a turn
|
|
131
|
+
whose every request reported no cache figure ended as
|
|
132
|
+
`{ value: 0, provenance: 'computed' }`. A field every step reported as
|
|
133
|
+
`unavailable` now stays `unavailable` in the total; an explicit zero and a
|
|
134
|
+
positive count are kept.
|
|
135
|
+
|
|
18
136
|
## [0.95.2] — 2026-09-23
|
|
19
137
|
|
|
20
138
|
### Added
|
|
@@ -60,6 +60,12 @@ export declare function addUsage(total: AgentUsage | undefined, step: AgentUsage
|
|
|
60
60
|
* handed us. Which loop did the adding is not a difference a caller filtering
|
|
61
61
|
* for a billable figure cares about, and pretending otherwise made one run
|
|
62
62
|
* report `provider-reported` on success and `computed` on abort.
|
|
63
|
+
*
|
|
64
|
+
* A field every step reported as `unavailable` stays `unavailable`: the
|
|
65
|
+
* aggregate is summed from the SDK's normalized usage, where an adapter may
|
|
66
|
+
* already have written `cached_tokens ?? 0`, so its zero is the adapter's word
|
|
67
|
+
* and not the provider's. The OpenRouter adapter did exactly that, and a turn
|
|
68
|
+
* whose every request said "no cache figure" ended as a confident "cache 0".
|
|
63
69
|
*/
|
|
64
70
|
export declare function mergeModelTotals(sdkTotal: AgentUsage, accumulated: AgentUsage | undefined): AgentUsage;
|
|
65
71
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-internals.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/runtime-internals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,IAAI,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAEL,KAAK,gBAAgB,EAGrB,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAChB,MAAM,WAAW,CAAC;AAGnB;;;;;GAKG;AAEH,wBAAgB,OAAO,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,CAI1E;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAG5E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO;;;;cAI9C;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS;;;EAKhF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAQxE;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB,CAK5E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,CAuDpF;AAED
|
|
1
|
+
{"version":3,"file":"runtime-internals.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/runtime-internals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,IAAI,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAEL,KAAK,gBAAgB,EAGrB,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAChB,MAAM,WAAW,CAAC;AAGnB;;;;;GAKG;AAEH,wBAAgB,OAAO,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,CAI1E;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAG5E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO;;;;cAI9C;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS;;;EAKhF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAQxE;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB,CAK5E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,UAAU,CAuDpF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EACpB,WAAW,EAAE,UAAU,GAAG,SAAS,GAClC,UAAU,CAmBZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,CAYrE;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,IAAI,UAAU,CAUzC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,GAAG,UAAU,CAmBvE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS;;iBAEhE,IAAI;;;;;;EAgDxB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Bot, Context } from 'grammy';
|
|
2
|
+
import { type GrammyPollingEnd, type GrammyPollingResourceConfig } from './grammy.js';
|
|
3
|
+
import { type ManagedResource, type ManagedResourceContext, type ManagedResourceDependency } from './resource.js';
|
|
4
|
+
/** The two ids a monitor, a test and a restart address the bot by. */
|
|
5
|
+
export declare const TELEGRAM_CONFIGURATION_RESOURCE_ID = "telegram-configuration";
|
|
6
|
+
export declare const TELEGRAM_POLLING_RESOURCE_ID = "telegram-polling";
|
|
7
|
+
export interface GrammyBotResourcesConfig<C extends Context> {
|
|
8
|
+
readonly bot: Bot<C>;
|
|
9
|
+
/**
|
|
10
|
+
* What the bot needs before it may say anything to Telegram — the database
|
|
11
|
+
* its handlers read, the queues they enqueue into, the HTTP server whose
|
|
12
|
+
* webhooks they answer. Both resources start after all of it.
|
|
13
|
+
*/
|
|
14
|
+
readonly dependsOn?: readonly ManagedResourceDependency[];
|
|
15
|
+
/**
|
|
16
|
+
* Publish what Telegram shows before the first update — the command menu,
|
|
17
|
+
* the description, the menu button. Runs once per start, before polling, so
|
|
18
|
+
* a menu that failed to publish fails the start instead of hiding behind a
|
|
19
|
+
* bot that answers. Omit it and there is no `telegram-configuration`.
|
|
20
|
+
*/
|
|
21
|
+
readonly configure?: (context: ManagedResourceContext) => void | Promise<void>;
|
|
22
|
+
readonly polling?: GrammyPollingResourceConfig<C>['polling'];
|
|
23
|
+
readonly onStart?: GrammyPollingResourceConfig<C>['onStart'];
|
|
24
|
+
readonly onError?: (error: unknown) => void | Promise<void>;
|
|
25
|
+
/** See `GrammyPollingResourceConfig.onEnded` — the one place the end of polling is decided. */
|
|
26
|
+
readonly onEnded?: (end: GrammyPollingEnd) => void | Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export interface GrammyBotResources {
|
|
29
|
+
readonly configuration?: ManagedResource;
|
|
30
|
+
readonly polling: ManagedResource;
|
|
31
|
+
/** Both, in start order, for `createApplication({ resources })`. */
|
|
32
|
+
readonly resources: readonly ManagedResource[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A long-polling Telegram bot as resources of an application.
|
|
36
|
+
*
|
|
37
|
+
* Every bot assembled the same pair by hand — a configuration resource calling
|
|
38
|
+
* `setMyCommands`, then `grammyPollingResource` — under its own names, and only
|
|
39
|
+
* one of them held updates back while the application was not ready. One call
|
|
40
|
+
* now gives the pair with stable ids, updates admitted by batch, and the end of
|
|
41
|
+
* polling reported to one callback.
|
|
42
|
+
*/
|
|
43
|
+
export declare function grammyBotResources<C extends Context>(config: GrammyBotResourcesConfig<C>): GrammyBotResources;
|
|
44
|
+
//# sourceMappingURL=grammy-bot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grammy-bot.d.ts","sourceRoot":"","sources":["../../src/application/grammy-bot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,2BAA2B,EAEjC,MAAM,UAAU,CAAC;AAClB,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AAEpB,sEAAsE;AACtE,eAAO,MAAM,kCAAkC,2BAA2B,CAAC;AAC3E,eAAO,MAAM,4BAA4B,qBAAqB,CAAC;AAE/D,MAAM,WAAW,wBAAwB,CAAC,CAAC,SAAS,OAAO;IACzD,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC1D;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,2BAA2B,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,2BAA2B,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,+FAA+F;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;CAChD;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,OAAO,EAClD,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAClC,kBAAkB,CA0BpB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Bot, Context } from 'grammy';
|
|
2
|
+
import type { ManagedResourceAdmission } from './resource.js';
|
|
3
|
+
/**
|
|
4
|
+
* Updates reach grammY only while the application admits them.
|
|
5
|
+
*
|
|
6
|
+
* grammY's built-in polling acknowledges a batch by asking for the next one
|
|
7
|
+
* with a higher offset, and it asks as soon as the previous batch has been
|
|
8
|
+
* through middleware — whatever the middleware did with it. So an update that
|
|
9
|
+
* arrived while the application was starting, degraded or stopping was handed
|
|
10
|
+
* to a handler that refused it, and the next `getUpdates` confirmed it to
|
|
11
|
+
* Telegram as handled. Nothing retried it; it was simply gone.
|
|
12
|
+
*
|
|
13
|
+
* The gate sits on the one call that moves the offset. Before asking for a
|
|
14
|
+
* batch it waits until the application admits; after Telegram answers with a
|
|
15
|
+
* non-empty batch it takes one admission lease for the whole batch and only
|
|
16
|
+
* then returns it to grammY. grammY handles a batch sequentially and asks for
|
|
17
|
+
* the next one only when it is done, so the next `getUpdates` is also the
|
|
18
|
+
* moment the batch is finished and its lease is released — no middleware, and
|
|
19
|
+
* so no dependence on where in the chain it was registered.
|
|
20
|
+
*
|
|
21
|
+
* A batch that arrives when the application will not admit it is never
|
|
22
|
+
* returned: the request fails with an abort, grammY's offset does not move, and
|
|
23
|
+
* Telegram keeps the updates for the next process.
|
|
24
|
+
*/
|
|
25
|
+
export interface UpdateAdmissionGate {
|
|
26
|
+
/** Admission for the current life of the polling resource; `undefined` passes calls through. */
|
|
27
|
+
bind(admission: ManagedResourceAdmission | undefined): void;
|
|
28
|
+
/** Resolves when no admitted batch is in progress, or when `signal` aborts. */
|
|
29
|
+
whenIdle(signal: AbortSignal): Promise<void>;
|
|
30
|
+
/** Release an admitted batch whose handling ended without a next request. */
|
|
31
|
+
release(): void;
|
|
32
|
+
}
|
|
33
|
+
/** Install the gate once per bot; a restarted resource binds the same gate again. */
|
|
34
|
+
export declare function updateAdmissionGate<C extends Context>(bot: Bot<C>): UpdateAdmissionGate;
|
|
35
|
+
//# sourceMappingURL=grammy-update-admission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grammy-update-admission.d.ts","sourceRoot":"","sources":["../../src/application/grammy-update-admission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,mBAAmB;IAClC,gGAAgG;IAChG,IAAI,CAAC,SAAS,EAAE,wBAAwB,GAAG,SAAS,GAAG,IAAI,CAAC;IAC5D,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,6EAA6E;IAC7E,OAAO,IAAI,IAAI,CAAC;CACjB;AAsDD,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAmDvF"}
|
|
@@ -1,22 +1,41 @@
|
|
|
1
1
|
import { type Bot, Context, type PollingOptions, type WebhookReplyEnvelope } from 'grammy';
|
|
2
2
|
import { AppError } from '../contract/errors.js';
|
|
3
|
-
import { type ManagedResource } from './resource.js';
|
|
3
|
+
import { type ManagedResource, type ManagedResourceDependency } from './resource.js';
|
|
4
4
|
export interface GrammyPollingResourceConfig<C extends Context> {
|
|
5
5
|
readonly id: string;
|
|
6
6
|
readonly bot: Bot<C>;
|
|
7
|
-
readonly dependsOn?: readonly
|
|
7
|
+
readonly dependsOn?: readonly ManagedResourceDependency[];
|
|
8
8
|
readonly required?: boolean;
|
|
9
9
|
readonly polling?: Omit<PollingOptions, 'onStart'>;
|
|
10
10
|
readonly onStart?: PollingOptions['onStart'];
|
|
11
11
|
readonly onError?: (error: unknown) => void | Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Polling ended on its own after it was ready — Telegram refused the token,
|
|
14
|
+
* another process took the same bot (409), or the error handler rethrew.
|
|
15
|
+
* Called once, never for a stop the application asked for. grammY's poller
|
|
16
|
+
* does not recover in-process, and the framework does not exit processes
|
|
17
|
+
* (ADR 0074): this is where an application turns the end into its shutdown.
|
|
18
|
+
*/
|
|
19
|
+
readonly onEnded?: (end: GrammyPollingEnd) => void | Promise<void>;
|
|
12
20
|
}
|
|
13
|
-
/**
|
|
21
|
+
/** How a poller that was ready came to an end; `error` is absent when it simply returned. */
|
|
22
|
+
export interface GrammyPollingEnd {
|
|
23
|
+
readonly error?: unknown;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Lifecycle adapter for grammY's built-in long polling.
|
|
27
|
+
*
|
|
28
|
+
* Updates are admitted by batch (see `grammy-update-admission`): no batch is
|
|
29
|
+
* fetched while the application is not accepting, and the batch in hand is
|
|
30
|
+
* finished before `bot.stop()` confirms the offset, so a stop neither loses an
|
|
31
|
+
* update nor hands the next process one this process already handled.
|
|
32
|
+
*/
|
|
14
33
|
export declare function grammyPollingResource<C extends Context>(config: GrammyPollingResourceConfig<C>): ManagedResource;
|
|
15
34
|
export type GrammyUpdate<C extends Context = Context> = Parameters<Bot<C>['handleUpdate']>[0];
|
|
16
35
|
export interface GrammyWebhookResourceConfig<C extends Context> {
|
|
17
36
|
readonly id: string;
|
|
18
37
|
readonly bot: Bot<C>;
|
|
19
|
-
readonly dependsOn?: readonly
|
|
38
|
+
readonly dependsOn?: readonly ManagedResourceDependency[];
|
|
20
39
|
readonly required?: boolean;
|
|
21
40
|
readonly onError?: (error: unknown) => void | Promise<void>;
|
|
22
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grammy.d.ts","sourceRoot":"","sources":["../../src/application/grammy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"grammy.d.ts","sourceRoot":"","sources":["../../src/application/grammy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AAMpB,MAAM,WAAW,2BAA2B,CAAC,CAAC,SAAS,OAAO;IAC5D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE;AAED,6FAA6F;AAC7F,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAgCD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,OAAO,EACrD,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACrC,eAAe,CA4EjB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9F,MAAM,WAAW,2BAA2B,CAAC,CAAC,SAAS,OAAO;IAC5D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,OAAO;IACtD,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,YAAY,CACV,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EACvB,oBAAoB,CAAC,EAAE,oBAAoB,GAC1C,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAaD;;;;;;;;;;;GAWG;AACH,qBAAa,6BAA8B,SAAQ,QAAQ,CAAC,8BAA8B,CAAC;IACzF,cAOC;CACF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,OAAO,EAC3D,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACrC,qBAAqB,CAAC,CAAC,CAAC,CAiE1B"}
|
|
@@ -5,4 +5,15 @@ export declare function acquire(state: KernelState): ApplicationOperationLease |
|
|
|
5
5
|
export declare function run<T>(state: KernelState, work: () => T | Promise<T>): Promise<T>;
|
|
6
6
|
/** Resolves once every admitted operation has released its lease. */
|
|
7
7
|
export declare function waitForPending(state: KernelState): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Wait until the application admits, then admit one operation.
|
|
10
|
+
*
|
|
11
|
+
* For the resource that brings work in rather than the caller that is handed
|
|
12
|
+
* it: a poller asking Telegram for the next batch has nothing to refuse — the
|
|
13
|
+
* right move while the application is starting or degraded is to not ask yet.
|
|
14
|
+
* Rejects with the signal's reason once it aborts, and with
|
|
15
|
+
* `ApplicationAdmissionError` once the application can no longer admit at all,
|
|
16
|
+
* so a waiter never outlives the lifetime it was waiting on.
|
|
17
|
+
*/
|
|
18
|
+
export declare function acquireWhenAccepting(state: KernelState, signal: AbortSignal): Promise<ApplicationOperationLease>;
|
|
8
19
|
//# sourceMappingURL=kernel-admission.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kernel-admission.d.ts","sourceRoot":"","sources":["../../src/application/kernel-admission.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EAAW,KAAK,WAAW,EAAW,MAAM,gBAAgB,CAAC;AAEpE,gFAAgF;AAChF,wBAAgB,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,yBAAyB,GAAG,IAAI,CAsB5E;AAED,wBAAsB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAQvF;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhE"}
|
|
1
|
+
{"version":3,"file":"kernel-admission.d.ts","sourceRoot":"","sources":["../../src/application/kernel-admission.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EAAW,KAAK,WAAW,EAAW,MAAM,gBAAgB,CAAC;AAEpE,gFAAgF;AAChF,wBAAgB,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,yBAAyB,GAAG,IAAI,CAsB5E;AAED,wBAAsB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAQvF;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,yBAAyB,CAAC,CA+CpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kernel-resource-context.d.ts","sourceRoot":"","sources":["../../src/application/kernel-resource-context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"kernel-resource-context.d.ts","sourceRoot":"","sources":["../../src/application/kernel-resource-context.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,cAAc,EAEpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,YAAY,CAAC;AAEpB,oFAAoF;AACpF,wBAAgB,UAAU,CACxB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,cAAc,EACtB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAO,GACpF,sBAAsB,CAuDxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAC3B,IAAI,CAYN"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ApplicationOperationLease } from './kernel-contract.js';
|
|
1
2
|
import type { ApplicationHealth } from './schemas.js';
|
|
2
3
|
/**
|
|
3
4
|
* What a resource declares it must start after.
|
|
@@ -45,9 +46,30 @@ export type ManagedResourcePublished<TResource> = TResource extends {
|
|
|
45
46
|
}> extends {
|
|
46
47
|
value: infer TValue;
|
|
47
48
|
} ? TValue : ManagedResourcePublishesNoValue : ManagedResourcePublishesNoValue;
|
|
49
|
+
/**
|
|
50
|
+
* The application's admission, as a resource that brings work in sees it.
|
|
51
|
+
*
|
|
52
|
+
* A caller handed work — an HTTP request, a webhook update — asks once and is
|
|
53
|
+
* refused. A resource that *fetches* work — a poller, a queue consumer — has a
|
|
54
|
+
* better move while the application is starting or degraded: not to fetch yet.
|
|
55
|
+
* Without this, such a resource reached for the application handle through a
|
|
56
|
+
* closure over a variable assigned after the graph that contains it.
|
|
57
|
+
*/
|
|
58
|
+
export interface ManagedResourceAdmission {
|
|
59
|
+
/** Admit one operation now, or `null` while the application is not accepting. */
|
|
60
|
+
acquire(): ApplicationOperationLease | null;
|
|
61
|
+
/**
|
|
62
|
+
* Admit one operation as soon as the application accepts. Rejects with the
|
|
63
|
+
* signal's reason on abort, and with `ApplicationAdmissionError` once the
|
|
64
|
+
* application is shutting down or failed, since it will not admit again.
|
|
65
|
+
*/
|
|
66
|
+
acquireWhenAccepting(signal: AbortSignal): Promise<ApplicationOperationLease>;
|
|
67
|
+
}
|
|
48
68
|
export interface ManagedResourceContext {
|
|
49
69
|
readonly applicationId: string;
|
|
50
70
|
readonly signal: AbortSignal;
|
|
71
|
+
/** The application's operation admission — see `ManagedResourceAdmission`. */
|
|
72
|
+
readonly admission: ManagedResourceAdmission;
|
|
51
73
|
readonly deadlineAt?: number;
|
|
52
74
|
readonly forceDeadlineAt?: number;
|
|
53
75
|
now(): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/application/resource.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/application/resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { createGrammyWebhookResource, type GrammyPollingResourceConfig, type GrammyUpdate, type GrammyWebhookResource, type GrammyWebhookResourceConfig, GrammyWebhookUnavailableError, grammyPollingResource, } from '../../application/grammy.js';
|
|
1
|
+
export { createGrammyWebhookResource, type GrammyPollingEnd, type GrammyPollingResourceConfig, type GrammyUpdate, type GrammyWebhookResource, type GrammyWebhookResourceConfig, GrammyWebhookUnavailableError, grammyPollingResource, } from '../../application/grammy.js';
|
|
2
|
+
export { type GrammyBotResources, type GrammyBotResourcesConfig, grammyBotResources, TELEGRAM_CONFIGURATION_RESOURCE_ID, TELEGRAM_POLLING_RESOURCE_ID, } from '../../application/grammy-bot.js';
|
|
2
3
|
//# sourceMappingURL=grammy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grammy.d.ts","sourceRoot":"","sources":["../../../src/entrypoints/application/grammy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,KAAK,2BAA2B,EAChC,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,6BAA6B,EAC7B,qBAAqB,GACtB,MAAM,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"grammy.d.ts","sourceRoot":"","sources":["../../../src/entrypoints/application/grammy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,KAAK,gBAAgB,EACrB,KAAK,2BAA2B,EAChC,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,6BAA6B,EAC7B,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,kBAAkB,EAClB,kCAAkC,EAClC,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC"}
|
|
@@ -7,6 +7,91 @@ import {
|
|
|
7
7
|
|
|
8
8
|
// src/application/grammy.ts
|
|
9
9
|
import { Context } from "grammy";
|
|
10
|
+
|
|
11
|
+
// src/application/grammy-update-admission.ts
|
|
12
|
+
function batchSize(result) {
|
|
13
|
+
return Array.isArray(result) ? result.length : 0;
|
|
14
|
+
}
|
|
15
|
+
function abortError() {
|
|
16
|
+
return new DOMException("Telegram polling stopped", "AbortError");
|
|
17
|
+
}
|
|
18
|
+
function untilAborted(signal) {
|
|
19
|
+
return new Promise((_, reject) => {
|
|
20
|
+
if (signal.aborted)
|
|
21
|
+
reject(abortError());
|
|
22
|
+
else
|
|
23
|
+
signal.addEventListener("abort", () => reject(abortError()), { once: true });
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async function admitted(admission, signal) {
|
|
27
|
+
try {
|
|
28
|
+
return await admission.acquireWhenAccepting(signal);
|
|
29
|
+
} catch {
|
|
30
|
+
if (signal.aborted)
|
|
31
|
+
throw abortError();
|
|
32
|
+
return untilAborted(signal);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function followed(signal) {
|
|
36
|
+
const controller = new AbortController;
|
|
37
|
+
if (signal.aborted)
|
|
38
|
+
controller.abort(abortError());
|
|
39
|
+
else
|
|
40
|
+
signal.addEventListener("abort", () => controller.abort(abortError()));
|
|
41
|
+
return controller.signal;
|
|
42
|
+
}
|
|
43
|
+
var gates = new WeakMap;
|
|
44
|
+
function updateAdmissionGate(bot) {
|
|
45
|
+
const existing = gates.get(bot);
|
|
46
|
+
if (existing)
|
|
47
|
+
return existing;
|
|
48
|
+
let admission;
|
|
49
|
+
let batch;
|
|
50
|
+
const idle = new Set;
|
|
51
|
+
const release = () => {
|
|
52
|
+
batch?.release();
|
|
53
|
+
batch = undefined;
|
|
54
|
+
for (const waiter of idle)
|
|
55
|
+
waiter();
|
|
56
|
+
idle.clear();
|
|
57
|
+
};
|
|
58
|
+
bot.api.config.use(async (previous, method, payload, signal) => {
|
|
59
|
+
if (method !== "getUpdates" || !signal || !admission) {
|
|
60
|
+
return previous(method, payload, signal);
|
|
61
|
+
}
|
|
62
|
+
release();
|
|
63
|
+
const polling = admission;
|
|
64
|
+
const stopped = followed(signal);
|
|
65
|
+
(await admitted(polling, stopped)).release();
|
|
66
|
+
const answer = await previous(method, payload, signal);
|
|
67
|
+
if (answer.ok && batchSize(answer.result) > 0)
|
|
68
|
+
batch = await admitted(polling, stopped);
|
|
69
|
+
return answer;
|
|
70
|
+
});
|
|
71
|
+
const gate = {
|
|
72
|
+
bind(next) {
|
|
73
|
+
admission = next;
|
|
74
|
+
},
|
|
75
|
+
whenIdle(signal) {
|
|
76
|
+
if (!batch || signal.aborted)
|
|
77
|
+
return Promise.resolve();
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
const done = () => {
|
|
80
|
+
signal.removeEventListener("abort", done);
|
|
81
|
+
idle.delete(done);
|
|
82
|
+
resolve();
|
|
83
|
+
};
|
|
84
|
+
idle.add(done);
|
|
85
|
+
signal.addEventListener("abort", done, { once: true });
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
release
|
|
89
|
+
};
|
|
90
|
+
gates.set(bot, gate);
|
|
91
|
+
return gate;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/application/grammy.ts
|
|
10
95
|
if (typeof Context !== "function") {
|
|
11
96
|
throw new TypeError("[stitchkit] stitchkit/application/grammy requires the grammY peer");
|
|
12
97
|
}
|
|
@@ -15,7 +100,15 @@ function reportIsolated(callback, error) {
|
|
|
15
100
|
return;
|
|
16
101
|
Promise.resolve().then(() => callback(error)).catch(() => {});
|
|
17
102
|
}
|
|
103
|
+
function reportEnd(callback, end) {
|
|
104
|
+
if (!callback)
|
|
105
|
+
return;
|
|
106
|
+
setTimeout(() => {
|
|
107
|
+
Promise.resolve().then(() => callback(end)).catch(() => {});
|
|
108
|
+
}, 0);
|
|
109
|
+
}
|
|
18
110
|
function grammyPollingResource(config) {
|
|
111
|
+
const gate = updateAdmissionGate(config.bot);
|
|
19
112
|
let completion;
|
|
20
113
|
let stopPromise;
|
|
21
114
|
const stop = () => {
|
|
@@ -29,7 +122,9 @@ function grammyPollingResource(config) {
|
|
|
29
122
|
id: config.id,
|
|
30
123
|
...config.dependsOn && { dependsOn: config.dependsOn },
|
|
31
124
|
...config.required !== undefined && { required: config.required },
|
|
32
|
-
start() {
|
|
125
|
+
start(context) {
|
|
126
|
+
stopPromise = undefined;
|
|
127
|
+
gate.bind(context.admission);
|
|
33
128
|
let resolveReady = () => {
|
|
34
129
|
return;
|
|
35
130
|
};
|
|
@@ -50,13 +145,18 @@ function grammyPollingResource(config) {
|
|
|
50
145
|
}
|
|
51
146
|
});
|
|
52
147
|
completion = polling.then(() => {
|
|
148
|
+
gate.release();
|
|
53
149
|
if (!becameReady) {
|
|
54
150
|
rejectReady(new Error("[stitchkit] grammY polling stopped before reaching readiness"));
|
|
55
|
-
}
|
|
151
|
+
} else if (!stopPromise)
|
|
152
|
+
reportEnd(config.onEnded, {});
|
|
56
153
|
}, (error) => {
|
|
154
|
+
gate.release();
|
|
57
155
|
if (!becameReady)
|
|
58
156
|
rejectReady(error);
|
|
59
157
|
reportIsolated(config.onError, error);
|
|
158
|
+
if (becameReady && !stopPromise)
|
|
159
|
+
reportEnd(config.onEnded, { error });
|
|
60
160
|
throw error;
|
|
61
161
|
});
|
|
62
162
|
completion.catch(() => {
|
|
@@ -64,11 +164,14 @@ function grammyPollingResource(config) {
|
|
|
64
164
|
});
|
|
65
165
|
return { ready, completion };
|
|
66
166
|
},
|
|
67
|
-
stopAdmission() {
|
|
68
|
-
|
|
167
|
+
async stopAdmission(context) {
|
|
168
|
+
await gate.whenIdle(context.signal);
|
|
169
|
+
await stop();
|
|
69
170
|
},
|
|
70
171
|
async drain() {
|
|
71
|
-
await completion
|
|
172
|
+
await completion?.catch(() => {
|
|
173
|
+
return;
|
|
174
|
+
});
|
|
72
175
|
},
|
|
73
176
|
async close() {
|
|
74
177
|
await stop();
|
|
@@ -162,8 +265,39 @@ function createGrammyWebhookResource(config) {
|
|
|
162
265
|
}
|
|
163
266
|
};
|
|
164
267
|
}
|
|
268
|
+
// src/application/grammy-bot.ts
|
|
269
|
+
var TELEGRAM_CONFIGURATION_RESOURCE_ID = "telegram-configuration";
|
|
270
|
+
var TELEGRAM_POLLING_RESOURCE_ID = "telegram-polling";
|
|
271
|
+
function grammyBotResources(config) {
|
|
272
|
+
const dependsOn = config.dependsOn ?? [];
|
|
273
|
+
const configure = config.configure;
|
|
274
|
+
const configuration = configure ? defineManagedResource({
|
|
275
|
+
id: TELEGRAM_CONFIGURATION_RESOURCE_ID,
|
|
276
|
+
dependsOn,
|
|
277
|
+
async start(context) {
|
|
278
|
+
await configure(context);
|
|
279
|
+
}
|
|
280
|
+
}) : undefined;
|
|
281
|
+
const polling = grammyPollingResource({
|
|
282
|
+
id: TELEGRAM_POLLING_RESOURCE_ID,
|
|
283
|
+
bot: config.bot,
|
|
284
|
+
dependsOn: configuration ? [configuration] : dependsOn,
|
|
285
|
+
...config.polling && { polling: config.polling },
|
|
286
|
+
...config.onStart && { onStart: config.onStart },
|
|
287
|
+
...config.onError && { onError: config.onError },
|
|
288
|
+
...config.onEnded && { onEnded: config.onEnded }
|
|
289
|
+
});
|
|
290
|
+
return {
|
|
291
|
+
...configuration && { configuration },
|
|
292
|
+
polling,
|
|
293
|
+
resources: configuration ? [configuration, polling] : [polling]
|
|
294
|
+
};
|
|
295
|
+
}
|
|
165
296
|
export {
|
|
166
297
|
grammyPollingResource,
|
|
298
|
+
grammyBotResources,
|
|
167
299
|
createGrammyWebhookResource,
|
|
300
|
+
TELEGRAM_POLLING_RESOURCE_ID,
|
|
301
|
+
TELEGRAM_CONFIGURATION_RESOURCE_ID,
|
|
168
302
|
GrammyWebhookUnavailableError
|
|
169
303
|
};
|
|
@@ -13,7 +13,7 @@ export { type ApplicationSnapshotSink, type ApplicationSnapshotSinkConfig, type
|
|
|
13
13
|
export { createNotificationOutbox, type DroppedNotification, type EnqueueNotification, type NotificationFailureClassification, type NotificationOutbox, type NotificationOutboxConfig, type NotificationOutboxItem, type NotificationOutboxReceipt, type NotificationOutboxResource, type NotificationOutboxResourceConfig, type NotificationOutboxState, type NotificationSend, notificationOutboxResource, notificationOutboxStateSchema, } from '../application/notification-outbox.js';
|
|
14
14
|
export { createProcessLifecycleLedger, type LifecycleLedgerResource, type LifecycleLedgerResourceConfig, lifecycleLedgerResource, type ProcessLifecycleLedger, type ProcessLifecycleLedgerConfig, } from '../application/process-lifecycle.js';
|
|
15
15
|
export { type DrainingFact, type LifecycleRun, LifecycleRunSchema, type LifecycleState, LifecycleStateSchema, type LifecycleTermination, LifecycleTerminationSchema, type LifecycleTransition, type PreviousExit, PreviousExitSchema, type ProcessLifecycleFact, type ReadyFact, type SameVersionOverlap, type ShutdownFact, type StartFact, type TransitionRunInput, type TransitionShutdownInput, type TransitionStartInput, transitionProcessDraining, transitionProcessReady, transitionProcessShutdown, transitionProcessStart, } from '../application/process-lifecycle-transitions.js';
|
|
16
|
-
export { defineManagedResource, type ManagedResource, type ManagedResourceContext, type ManagedResourceDependency, type ManagedResourcePublished, type ManagedResourcePublishesNoValue, type ManagedResourceStartResult, managedResourceDependencyId, } from '../application/resource.js';
|
|
16
|
+
export { defineManagedResource, type ManagedResource, type ManagedResourceAdmission, type ManagedResourceContext, type ManagedResourceDependency, type ManagedResourcePublished, type ManagedResourcePublishesNoValue, type ManagedResourceStartResult, managedResourceDependencyId, } from '../application/resource.js';
|
|
17
17
|
export { createRevisionSignal, type RevisionSignal, type RevisionSignalAdvanceResult, RevisionSignalAdvanceResultSchema, type RevisionSignalClock, type RevisionSignalConfig, type RevisionSignalSnapshot, RevisionSignalSnapshotSchema, type RevisionSignalState, RevisionSignalStateSchema, type RevisionSignalTimer, type RevisionSignalWaitOptions, type RevisionSignalWaitOutcome, RevisionSignalWaitOutcomeSchema, type RevisionSignalWaitResult, RevisionSignalWaitResultSchema, } from '../application/revision-signal.js';
|
|
18
18
|
export { createManagedSchedule, type ManagedSchedule, type ManagedScheduleClock, type ManagedScheduleConfig, type ManagedScheduleDescriptor, ManagedScheduleDescriptorSchema, type ManagedScheduleErrorPolicy, ManagedScheduleErrorPolicySchema, type ManagedScheduleOverlap, ManagedScheduleOverlapSchema, type ManagedScheduleRunContext, type ManagedScheduleStatus, ManagedScheduleStatusSchema, type ManagedScheduleTimer, } from '../application/schedule.js';
|
|
19
19
|
export { type ManagedServerResource, type ManagedServerResourceConfig, managedServerResource, } from '../application/server-resource.js';
|