@yaebal/test 0.1.0 → 0.2.1

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.
Files changed (107) hide show
  1. package/README.md +334 -160
  2. package/lib/api.d.ts +82 -0
  3. package/lib/api.d.ts.map +1 -0
  4. package/lib/api.js +183 -0
  5. package/lib/api.js.map +1 -0
  6. package/lib/api.test.d.ts +2 -0
  7. package/lib/api.test.d.ts.map +1 -0
  8. package/lib/api.test.js +131 -0
  9. package/lib/api.test.js.map +1 -0
  10. package/lib/bot-messages.d.ts +42 -0
  11. package/lib/bot-messages.d.ts.map +1 -0
  12. package/lib/bot-messages.js +72 -0
  13. package/lib/bot-messages.js.map +1 -0
  14. package/lib/chat-actor.d.ts +45 -0
  15. package/lib/chat-actor.d.ts.map +1 -0
  16. package/lib/chat-actor.js +72 -0
  17. package/lib/chat-actor.js.map +1 -0
  18. package/lib/clock.d.ts +22 -0
  19. package/lib/clock.d.ts.map +1 -0
  20. package/lib/clock.js +72 -0
  21. package/lib/clock.js.map +1 -0
  22. package/lib/clock.test.d.ts +2 -0
  23. package/lib/clock.test.d.ts.map +1 -0
  24. package/lib/clock.test.js +69 -0
  25. package/lib/clock.test.js.map +1 -0
  26. package/lib/env.d.ts +100 -0
  27. package/lib/env.d.ts.map +1 -0
  28. package/lib/env.js +164 -0
  29. package/lib/env.js.map +1 -0
  30. package/lib/env.test.d.ts +2 -0
  31. package/lib/env.test.d.ts.map +1 -0
  32. package/lib/env.test.js +302 -0
  33. package/lib/env.test.js.map +1 -0
  34. package/lib/fetch.d.ts +3 -0
  35. package/lib/fetch.d.ts.map +1 -0
  36. package/lib/fetch.js +12 -0
  37. package/lib/fetch.js.map +1 -0
  38. package/lib/index.d.ts +19 -205
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +19 -391
  41. package/lib/index.js.map +1 -1
  42. package/lib/internal.d.ts +25 -0
  43. package/lib/internal.d.ts.map +1 -0
  44. package/lib/internal.js +19 -0
  45. package/lib/internal.js.map +1 -0
  46. package/lib/keyboard.d.ts +15 -0
  47. package/lib/keyboard.d.ts.map +1 -0
  48. package/lib/keyboard.js +25 -0
  49. package/lib/keyboard.js.map +1 -0
  50. package/lib/keyboard.test.d.ts +2 -0
  51. package/lib/keyboard.test.d.ts.map +1 -0
  52. package/lib/keyboard.test.js +31 -0
  53. package/lib/keyboard.test.js.map +1 -0
  54. package/lib/normalize.d.ts +10 -0
  55. package/lib/normalize.d.ts.map +1 -0
  56. package/lib/normalize.js +27 -0
  57. package/lib/normalize.js.map +1 -0
  58. package/lib/reactions.d.ts +3 -0
  59. package/lib/reactions.d.ts.map +1 -0
  60. package/lib/reactions.js +17 -0
  61. package/lib/reactions.js.map +1 -0
  62. package/lib/updates.d.ts +126 -0
  63. package/lib/updates.d.ts.map +1 -0
  64. package/lib/updates.js +200 -0
  65. package/lib/updates.js.map +1 -0
  66. package/lib/updates.test.d.ts +2 -0
  67. package/lib/updates.test.d.ts.map +1 -0
  68. package/lib/updates.test.js +72 -0
  69. package/lib/updates.test.js.map +1 -0
  70. package/lib/user-actor.d.ts +188 -0
  71. package/lib/user-actor.d.ts.map +1 -0
  72. package/lib/user-actor.js +465 -0
  73. package/lib/user-actor.js.map +1 -0
  74. package/lib/webhook.d.ts +18 -0
  75. package/lib/webhook.d.ts.map +1 -0
  76. package/lib/webhook.js +25 -0
  77. package/lib/webhook.js.map +1 -0
  78. package/lib/webhook.test.d.ts +2 -0
  79. package/lib/webhook.test.d.ts.map +1 -0
  80. package/lib/webhook.test.js +36 -0
  81. package/lib/webhook.test.js.map +1 -0
  82. package/package.json +7 -5
  83. package/src/api.test.ts +180 -0
  84. package/src/api.ts +281 -0
  85. package/src/bot-messages.ts +117 -0
  86. package/src/chat-actor.ts +101 -0
  87. package/src/clock.test.ts +80 -0
  88. package/src/clock.ts +118 -0
  89. package/src/env.test.ts +370 -0
  90. package/src/env.ts +235 -0
  91. package/src/fetch.ts +11 -0
  92. package/src/index.ts +79 -630
  93. package/src/internal.ts +43 -0
  94. package/src/keyboard.test.ts +35 -0
  95. package/src/keyboard.ts +38 -0
  96. package/src/normalize.ts +34 -0
  97. package/src/reactions.ts +18 -0
  98. package/src/updates.test.ts +107 -0
  99. package/src/updates.ts +354 -0
  100. package/src/user-actor.ts +702 -0
  101. package/src/webhook.test.ts +54 -0
  102. package/src/webhook.ts +48 -0
  103. package/lib/index.test.d.ts +0 -2
  104. package/lib/index.test.d.ts.map +0 -1
  105. package/lib/index.test.js +0 -213
  106. package/lib/index.test.js.map +0 -1
  107. package/src/index.test.ts +0 -320
package/lib/index.js CHANGED
@@ -1,395 +1,23 @@
1
1
  /**
2
- * @yaebal/test — testing utilities for yaebal bots, with zero dependency on any
3
- * test runner or assertion library. Works with `node:test`, vitest, bun:test,
4
- * jest, ava — anything that can `await` a promise and call `assert`.
2
+ * @yaebal/test — an actor-driven test framework for yaebal bots, with zero dependency on any
3
+ * test runner or assertion library. Works with `node:test`, vitest, bun:test, jest, ava —
4
+ * anything that can `await` a promise and call `assert`.
5
5
  *
6
- * every plugin test used to hand-build fake updates and a mock api. this package
7
- * extracts that boilerplate: {@link mockApi} records every call (and can drive
8
- * real `before`/`after`/`onError` hooks and simulate failures), the `*Update`
9
- * factories produce real {@link Update} shapes for every update kind, and
10
- * {@link createContext} wraps one in a core {@link Context}.
11
- */
12
- import { Context, } from "@yaebal/core";
13
- /** default results for known methods; everything else resolves to `{}`. */
14
- function builtinResult(method, nextMessageId) {
15
- if (method.startsWith("send") || method === "copyMessage" || method === "forwardMessage") {
16
- return { message_id: nextMessageId() };
17
- }
18
- if (method === "answerCallbackQuery")
19
- return true;
20
- if (method === "getMe")
21
- return { id: 1, is_bot: true, first_name: "bot", username: "bot" };
22
- return {};
23
- }
24
- /**
25
- * a fake {@link Api} whose every method records `{ method, params }` into `calls`
26
- * and resolves to a sensible default (auto-incrementing `message_id` for `send*`,
27
- * `true` for `answerCallbackQuery`, `{}` otherwise) — or to whatever `options.results`
28
- * says. `before`/`after`/`onError` are real hook registrars (not no-ops): register
29
- * a hook the same way you would on the production `Api` and it actually runs,
30
- * including retries requested by an `onError` hook. the mock never actually waits
31
- * on a requested `delayMs` — retries settle instantly, so tests stay fast.
32
- */
33
- export function mockApi(options = {}) {
34
- const calls = [];
35
- const overrides = { ...options.results };
36
- const attempts = new Map();
37
- let nextMessageId = 1;
38
- const hooks = {
39
- before: [],
40
- after: [],
41
- onError: [],
42
- };
43
- function resolveResult(method, params) {
44
- const attempt = (attempts.get(method) ?? 0) + 1;
45
- attempts.set(method, attempt);
46
- const override = overrides[method];
47
- if (typeof override === "function") {
48
- return override(params, attempt);
49
- }
50
- return override !== undefined ? override : builtinResult(method, () => nextMessageId++);
51
- }
52
- const call = async (method, params) => {
53
- let p = params;
54
- for (const hook of hooks.before) {
55
- const next = await hook(method, p);
56
- if (next !== undefined)
57
- p = next;
58
- }
59
- for (let attempt = 1;; attempt++) {
60
- calls.push({ method, params: p });
61
- let result;
62
- try {
63
- result = resolveResult(method, p);
64
- if (result instanceof Error)
65
- throw result;
66
- }
67
- catch (error) {
68
- let retry;
69
- for (const hook of hooks.onError) {
70
- const action = await hook(method, error, attempt);
71
- if (action?.retry) {
72
- retry = action;
73
- break;
74
- }
75
- }
76
- if (!retry)
77
- throw error;
78
- continue; // the mock never actually waits on retry.delayMs
79
- }
80
- for (const hook of hooks.after) {
81
- const next = await hook(method, p, result);
82
- if (next !== undefined)
83
- result = next;
84
- }
85
- return result;
86
- }
87
- };
88
- const registrar = {
89
- call: (method, params) => call(method, params),
90
- fileUrl: (filePath) => `https://example.invalid/file/${filePath}`,
91
- before(hook) {
92
- hooks.before.push(hook);
93
- return api;
94
- },
95
- after(hook) {
96
- hooks.after.push(hook);
97
- return api;
98
- },
99
- onError(hook) {
100
- hooks.onError.push(hook);
101
- return api;
102
- },
103
- };
104
- const api = new Proxy(registrar, {
105
- get(obj, prop) {
106
- if (prop in obj)
107
- return obj[prop];
108
- const method = (params) => call(prop, params);
109
- obj[prop] = method;
110
- return method;
111
- },
112
- });
113
- return {
114
- api,
115
- calls,
116
- hooks,
117
- lastCall: (method) => method ? [...calls].reverse().find((c) => c.method === method) : calls.at(-1),
118
- callsTo: (method) => calls.filter((c) => c.method === method),
119
- setResult: (method, result) => {
120
- overrides[method] = result;
121
- },
122
- reset: () => {
123
- calls.length = 0;
124
- attempts.clear();
125
- nextMessageId = 1;
126
- },
127
- };
128
- }
129
- let updateIdCounter = 0;
130
- /** build an {@link Update} from a partial, filling in a fresh `update_id`. */
131
- export function createUpdate(partial = {}) {
132
- return { update_id: ++updateIdCounter, ...partial };
133
- }
134
- const stubUser = (id) => ({ id, is_bot: false, first_name: "u" });
135
- function buildMessage(options, defaultChatType) {
136
- const { text = "", chatId = 1, fromId = chatId, chatType = defaultChatType } = options;
137
- return {
138
- message_id: 1,
139
- date: 0,
140
- chat: { id: chatId, type: chatType },
141
- from: stubUser(fromId),
142
- text,
143
- };
144
- }
145
- /** build a `message` {@link Update}. */
146
- export function messageUpdate(options = {}) {
147
- return createUpdate({ message: buildMessage(options, "private") });
148
- }
149
- /** build an `edited_message` {@link Update}. */
150
- export function editedMessageUpdate(options = {}) {
151
- return createUpdate({ edited_message: buildMessage(options, "private") });
152
- }
153
- /** build a `channel_post` {@link Update}. */
154
- export function channelPostUpdate(options = {}) {
155
- return createUpdate({ channel_post: buildMessage(options, "channel") });
156
- }
157
- /** build an `edited_channel_post` {@link Update}. */
158
- export function editedChannelPostUpdate(options = {}) {
159
- return createUpdate({ edited_channel_post: buildMessage(options, "channel") });
160
- }
161
- /** build a `callback_query` {@link Update}. */
162
- export function callbackUpdate(options = {}) {
163
- const { data = "", chatId = 1, fromId = chatId } = options;
164
- return createUpdate({
165
- callback_query: {
166
- id: "1",
167
- chat_instance: "0",
168
- from: stubUser(fromId),
169
- message: {
170
- message_id: 1,
171
- date: 0,
172
- chat: { id: chatId, type: "private" },
173
- },
174
- data,
175
- },
176
- });
177
- }
178
- /** build an `inline_query` {@link Update}. */
179
- export function inlineQueryUpdate(options = {}) {
180
- const { query = "", fromId = 1, id = "1", offset = "", chatType } = options;
181
- return createUpdate({
182
- inline_query: {
183
- id,
184
- from: stubUser(fromId),
185
- query,
186
- offset,
187
- ...(chatType ? { chat_type: chatType } : {}),
188
- },
189
- });
190
- }
191
- /** build a `chosen_inline_result` {@link Update}. */
192
- export function chosenInlineResultUpdate(options = {}) {
193
- const { resultId = "1", fromId = 1, query = "", inlineMessageId } = options;
194
- return createUpdate({
195
- chosen_inline_result: {
196
- result_id: resultId,
197
- from: stubUser(fromId),
198
- query,
199
- ...(inlineMessageId ? { inline_message_id: inlineMessageId } : {}),
200
- },
201
- });
202
- }
203
- /** build a `shipping_query` {@link Update}. */
204
- export function shippingQueryUpdate(options = {}) {
205
- const { id = "1", fromId = 1, invoicePayload = "", shippingAddress = {} } = options;
206
- return createUpdate({
207
- shipping_query: {
208
- id,
209
- from: stubUser(fromId),
210
- invoice_payload: invoicePayload,
211
- shipping_address: {
212
- country_code: "US",
213
- state: "",
214
- city: "New York",
215
- street_line1: "",
216
- street_line2: "",
217
- post_code: "10001",
218
- ...shippingAddress,
219
- },
220
- },
221
- });
222
- }
223
- /** build a `pre_checkout_query` {@link Update}. */
224
- export function preCheckoutQueryUpdate(options = {}) {
225
- const { id = "1", fromId = 1, currency = "USD", totalAmount = 100, invoicePayload = "", } = options;
226
- return createUpdate({
227
- pre_checkout_query: {
228
- id,
229
- from: stubUser(fromId),
230
- currency,
231
- total_amount: totalAmount,
232
- invoice_payload: invoicePayload,
233
- },
234
- });
235
- }
236
- /** build a `poll` {@link Update}. */
237
- export function pollUpdate(options = {}) {
238
- const { id = "1", question = "", options: choices = ["yes", "no"], isClosed = false } = options;
239
- return createUpdate({
240
- poll: {
241
- id,
242
- question,
243
- options: choices.map((text, i) => ({ persistent_id: String(i), text, voter_count: 0 })),
244
- total_voter_count: 0,
245
- is_closed: isClosed,
246
- is_anonymous: true,
247
- type: "regular",
248
- allows_multiple_answers: false,
249
- allows_revoting: false,
250
- members_only: false,
251
- },
252
- });
253
- }
254
- /** build a `poll_answer` {@link Update}. */
255
- export function pollAnswerUpdate(options = {}) {
256
- const { pollId = "1", fromId = 1, optionIds = [0] } = options;
257
- return createUpdate({
258
- poll_answer: {
259
- poll_id: pollId,
260
- user: stubUser(fromId),
261
- option_ids: optionIds,
262
- option_persistent_ids: optionIds.map(String),
263
- },
264
- });
265
- }
266
- function buildChatMemberUpdate(options) {
267
- const { chatId = 1, fromId = chatId, userId = fromId, oldStatus = "member", newStatus = "member", } = options;
268
- const user = stubUser(userId);
269
- return {
270
- chat: { id: chatId, type: "group" },
271
- from: stubUser(fromId),
272
- date: 0,
273
- old_chat_member: { status: oldStatus, user },
274
- new_chat_member: { status: newStatus, user },
275
- };
276
- }
277
- /** build a `my_chat_member` {@link Update} (the bot's own membership changed). */
278
- export function myChatMemberUpdate(options = {}) {
279
- return createUpdate({ my_chat_member: buildChatMemberUpdate(options) });
280
- }
281
- /** build a `chat_member` {@link Update} (another member's membership changed). */
282
- export function chatMemberUpdate(options = {}) {
283
- return createUpdate({ chat_member: buildChatMemberUpdate(options) });
284
- }
285
- /** build a `chat_join_request` {@link Update}. */
286
- export function chatJoinRequestUpdate(options = {}) {
287
- const { chatId = 1, fromId = 1, userChatId = fromId, bio } = options;
288
- return createUpdate({
289
- chat_join_request: {
290
- chat: { id: chatId, type: "group" },
291
- from: stubUser(fromId),
292
- user_chat_id: userChatId,
293
- date: 0,
294
- ...(bio ? { bio } : {}),
295
- },
296
- });
297
- }
298
- /** infer which payload key an update carries; defaults to `"message"`. */
299
- export function detectUpdateType(update) {
300
- if (update.message)
301
- return "message";
302
- if (update.edited_message)
303
- return "edited_message";
304
- if (update.channel_post)
305
- return "channel_post";
306
- if (update.callback_query)
307
- return "callback_query";
308
- const bag = update;
309
- for (const key of Object.keys(bag)) {
310
- if (key !== "update_id" && bag[key] !== undefined)
311
- return key;
312
- }
313
- return "message";
314
- }
315
- /**
316
- * wrap an {@link Update} in a core {@link Context}. the api defaults to a fresh
317
- * {@link mockApi}; pass `updateType` to override the auto-detected one.
318
- */
319
- export function createContext(update, api, updateType) {
320
- return new Context({
321
- api: api ?? mockApi().api,
322
- update,
323
- updateType: updateType ?? detectUpdateType(update),
324
- });
325
- }
326
- /** shortcut: build a `message` update and wrap it in a {@link Context} in one call. */
327
- export function messageContext(options = {}, api) {
328
- return createContext(messageUpdate(options), api);
329
- }
330
- /** shortcut: build a `callback_query` update and wrap it in a {@link Context} in one call. */
331
- export function callbackContext(options = {}, api) {
332
- return createContext(callbackUpdate(options), api);
333
- }
334
- const noop = async () => { };
335
- /** run a composer's middleware against a context. resolves when the chain settles. */
336
- export async function runMiddleware(composer, ctx) {
337
- await composer.toMiddleware()(ctx, noop);
338
- }
339
- /**
340
- * search an inline keyboard (a `reply_markup`-shaped object, e.g. from a
341
- * recorded `sendMessage` call's params) for a button whose text matches a
342
- * string or regex. returns the button (plus its `row`/`col`) or `undefined`.
6
+ * {@link createTestEnv} wraps your bot and hands you {@link UserActor}/{@link ChatActor} actors
7
+ * that send it real updates messages, commands, media, reactions, button clicks, joins,
8
+ * payments — the way real Telegram users would. every outgoing api call is intercepted and
9
+ * recorded (no real HTTP), with sensible auto-stubs, `onApi`/`apiError` overrides for the rest,
10
+ * a virtual clock for TTL/retry tests, and satellite-plugin test packs.
11
+ *
12
+ * fixture builders ({@link messageUpdate} & co.), {@link webhookRequest}/{@link collectUpdates},
13
+ * and {@link withFetch} remain as the escape hatch beneath the actor api — reach for them when
14
+ * you need a raw update shape or full control.
343
15
  */
344
- export function findButton(markup, match) {
345
- const rows = markup?.inline_keyboard ?? [];
346
- for (let row = 0; row < rows.length; row++) {
347
- const cols = rows[row] ?? [];
348
- for (let col = 0; col < cols.length; col++) {
349
- const button = cols[col];
350
- if (!button)
351
- continue;
352
- const text = typeof button.text === "string" ? button.text : "";
353
- const matches = typeof match === "string" ? text === match : match.test(text);
354
- if (matches)
355
- return { ...button, text, row, col };
356
- }
357
- }
358
- return undefined;
359
- }
360
- /** a minimal {@link UpdateSink} (the `{ handleUpdate }` shape `webhookCallback`/runners expect) that just records. */
361
- export function collectUpdates() {
362
- const updates = [];
363
- return {
364
- sink: {
365
- handleUpdate: async (update) => {
366
- updates.push(update);
367
- },
368
- },
369
- updates,
370
- };
371
- }
372
- /** build a `Request` carrying `update` as JSON, as telegram would POST it to a webhook handler. */
373
- export function webhookRequest(update, options = {}) {
374
- const { url = "https://example.invalid/webhook", method = "POST", secretToken, headers = {}, } = options;
375
- const finalHeaders = { "content-type": "application/json", ...headers };
376
- if (secretToken)
377
- finalHeaders["x-telegram-bot-api-secret-token"] = secretToken;
378
- return new Request(url, {
379
- method,
380
- headers: finalHeaders,
381
- body: method === "GET" || method === "HEAD" ? undefined : JSON.stringify(update),
382
- });
383
- }
384
- /** stub `globalThis.fetch` for the duration of `fn`, restoring the original afterwards (even on throw). */
385
- export async function withFetch(handler, fn) {
386
- const realFetch = globalThis.fetch;
387
- globalThis.fetch = handler;
388
- try {
389
- return await fn();
390
- }
391
- finally {
392
- globalThis.fetch = realFetch;
393
- }
394
- }
16
+ export { mockApi } from "./api.js";
17
+ export { apiError, ChatActor, createTestEnv, installTestClock, isApiErrorSentinel, TestApiError, TestEnv, UserActor, UserInChatScope, UserOnMessageScope, } from "./env.js";
18
+ export { withFetch } from "./fetch.js";
19
+ export { findButton } from "./keyboard.js";
20
+ export { toPlain } from "./normalize.js";
21
+ export { buildUser, callbackUpdate, channelPostUpdate, chatJoinRequestUpdate, chatMemberUpdate, chosenInlineResultUpdate, createUpdate, detectUpdateType, editedChannelPostUpdate, editedMessageUpdate, inlineQueryUpdate, messageUpdate, myChatMemberUpdate, pollAnswerUpdate, pollUpdate, preCheckoutQueryUpdate, shippingQueryUpdate, } from "./updates.js";
22
+ export { collectUpdates, webhookRequest, } from "./webhook.js";
395
23
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAqC,OAAO,EAAE,MAAM,UAAU,CAAC;AACtE,OAAO,EAEN,QAAQ,EAER,SAAS,EAIT,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAOlB,YAAY,EAEZ,OAAO,EAGP,SAAS,EACT,eAAe,EACf,kBAAkB,GAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAoB,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAEN,SAAS,EAKT,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,wBAAwB,EACxB,YAAY,EACZ,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EAEnB,iBAAiB,EAEjB,aAAa,EACb,kBAAkB,EAIlB,gBAAgB,EAChB,UAAU,EACV,sBAAsB,EAEtB,mBAAmB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,cAAc,EAGd,cAAc,GACd,MAAM,cAAc,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { FormatResult, MessageEntity, Update } from "@yaebal/core";
2
+ /**
3
+ * the sliver of {@link TestEnv} actors need — kept as a separate interface (rather than a
4
+ * circular import of the class) so `chat-actor.ts`/`user-actor.ts` don't depend on `env.ts`.
5
+ */
6
+ export interface ActorHost {
7
+ dispatch(update: Update): Promise<void>;
8
+ nextMessageId(): number;
9
+ now(): number;
10
+ /** did the bot answer `answerPreCheckoutQuery` for `preCheckoutQueryId` with `ok: true`? */
11
+ answeredPreCheckoutQuery(preCheckoutQueryId: string): boolean;
12
+ }
13
+ /** text an actor method accepts: a plain string, or a `format`/`fmt` result carrying entities. */
14
+ export type SendText = string | FormatResult;
15
+ /** resolve a `SendText` into `{ text, entities }`, merging in any `extraEntities`. */
16
+ export declare function resolveSendText(text: SendText, extraEntities?: MessageEntity[]): {
17
+ text: string;
18
+ entities: MessageEntity[];
19
+ };
20
+ /** a fake `{ file_id, file_unique_id }` pair — good enough for tests, never a real download. */
21
+ export declare function fakeFile(prefix: string): {
22
+ file_id: string;
23
+ file_unique_id: string;
24
+ };
25
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAExE;;;GAGG;AACH,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,aAAa,IAAI,MAAM,CAAC;IACxB,GAAG,IAAI,MAAM,CAAC;IACd,4FAA4F;IAC5F,wBAAwB,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9D;AAED,kGAAkG;AAClG,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,YAAY,CAAC;AAY7C,sFAAsF;AACtF,wBAAgB,eAAe,CAC9B,IAAI,EAAE,QAAQ,EACd,aAAa,GAAE,aAAa,EAAO,GACjC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,CAG7C;AAID,gGAAgG;AAChG,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAGpF"}
@@ -0,0 +1,19 @@
1
+ function isFormatResult(value) {
2
+ return (typeof value === "object" &&
3
+ value !== null &&
4
+ "text" in value &&
5
+ "entities" in value &&
6
+ Array.isArray(value.entities));
7
+ }
8
+ /** resolve a `SendText` into `{ text, entities }`, merging in any `extraEntities`. */
9
+ export function resolveSendText(text, extraEntities = []) {
10
+ const resolved = isFormatResult(text) ? text : { text, entities: [] };
11
+ return { text: resolved.text, entities: [...resolved.entities, ...extraEntities] };
12
+ }
13
+ let fileCounter = 0;
14
+ /** a fake `{ file_id, file_unique_id }` pair — good enough for tests, never a real download. */
15
+ export function fakeFile(prefix) {
16
+ const n = ++fileCounter;
17
+ return { file_id: `${prefix}_${n}`, file_unique_id: `u${prefix}${n}` };
18
+ }
19
+ //# sourceMappingURL=internal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAiBA,SAAS,cAAc,CAAC,KAAc;IACrC,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,UAAU,IAAI,KAAK;QACnB,KAAK,CAAC,OAAO,CAAE,KAAsB,CAAC,QAAQ,CAAC,CAC/C,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,eAAe,CAC9B,IAAc,EACd,gBAAiC,EAAE;IAEnC,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACtE,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,QAAQ,EAAE,GAAG,aAAa,CAAC,EAAE,CAAC;AACpF,CAAC;AAED,IAAI,WAAW,GAAG,CAAC,CAAC;AAEpB,gGAAgG;AAChG,MAAM,UAAU,QAAQ,CAAC,MAAc;IACtC,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC;IACxB,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC;AACxE,CAAC"}
@@ -0,0 +1,15 @@
1
+ /** an inline keyboard button found by {@link findButton}, with its position. */
2
+ export interface FoundButton {
3
+ text: string;
4
+ row: number;
5
+ col: number;
6
+ [key: string]: unknown;
7
+ }
8
+ /**
9
+ * search an inline keyboard (a `reply_markup`-shaped object — or an `InlineKeyboard`/`Keyboard`
10
+ * builder instance, unwrapped via `toJSON()` automatically — e.g. from a recorded call's params,
11
+ * or a {@link BotMessage}'s `reply_markup`) for a button whose text matches a string or regex.
12
+ * returns the button (plus its `row`/`col`) or `undefined`.
13
+ */
14
+ export declare function findButton(markup: unknown, match: string | RegExp): FoundButton | undefined;
15
+ //# sourceMappingURL=keyboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../src/keyboard.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,CAqB3F"}
@@ -0,0 +1,25 @@
1
+ import { toPlain } from "./normalize.js";
2
+ /**
3
+ * search an inline keyboard (a `reply_markup`-shaped object — or an `InlineKeyboard`/`Keyboard`
4
+ * builder instance, unwrapped via `toJSON()` automatically — e.g. from a recorded call's params,
5
+ * or a {@link BotMessage}'s `reply_markup`) for a button whose text matches a string or regex.
6
+ * returns the button (plus its `row`/`col`) or `undefined`.
7
+ */
8
+ export function findButton(markup, match) {
9
+ const plain = toPlain(markup);
10
+ const rows = plain?.inline_keyboard ?? [];
11
+ for (let row = 0; row < rows.length; row++) {
12
+ const cols = rows[row] ?? [];
13
+ for (let col = 0; col < cols.length; col++) {
14
+ const button = cols[col];
15
+ if (!button)
16
+ continue;
17
+ const text = typeof button.text === "string" ? button.text : "";
18
+ const matches = typeof match === "string" ? text === match : match.test(text);
19
+ if (matches)
20
+ return { ...button, text, row, col };
21
+ }
22
+ }
23
+ return undefined;
24
+ }
25
+ //# sourceMappingURL=keyboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.js","sourceRoot":"","sources":["../src/keyboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAUzC;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe,EAAE,KAAsB;IACjE,MAAM,KAAK,GAAG,OAAO,CACpB,MAAM,CACN,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,EAAE,eAAe,IAAI,EAAE,CAAC;IAE1C,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAE7B,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACzB,IAAI,CAAC,MAAM;gBAAE,SAAS;YAEtB,MAAM,IAAI,GAAG,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,MAAM,OAAO,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE9E,IAAI,OAAO;gBAAE,OAAO,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QACnD,CAAC;IACF,CAAC;IAED,OAAO,SAAS,CAAC;AAClB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=keyboard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.test.d.ts","sourceRoot":"","sources":["../src/keyboard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,31 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { findButton } from "./keyboard.js";
4
+ test("findButton locates a button by text and reports its position", () => {
5
+ const markup = {
6
+ inline_keyboard: [
7
+ [{ text: "a", callback_data: "a" }],
8
+ [
9
+ { text: "b", callback_data: "b" },
10
+ { text: "Next", callback_data: "page:2" },
11
+ ],
12
+ ],
13
+ };
14
+ const found = findButton(markup, "Next");
15
+ assert.equal(found?.callback_data, "page:2");
16
+ assert.equal(found?.row, 1);
17
+ assert.equal(found?.col, 1);
18
+ assert.equal(findButton(markup, /^n/i)?.text, "Next");
19
+ assert.equal(findButton(markup, "missing"), undefined);
20
+ assert.equal(findButton(undefined, "missing"), undefined);
21
+ });
22
+ test("findButton unwraps a builder instance via toJSON()", () => {
23
+ class FakeInlineKeyboard {
24
+ toJSON() {
25
+ return { inline_keyboard: [[{ text: "Restart", callback_data: "restart" }]] };
26
+ }
27
+ }
28
+ const found = findButton(new FakeInlineKeyboard(), "Restart");
29
+ assert.equal(found?.callback_data, "restart");
30
+ });
31
+ //# sourceMappingURL=keyboard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.test.js","sourceRoot":"","sources":["../src/keyboard.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,IAAI,CAAC,8DAA8D,EAAE,GAAG,EAAE;IACzE,MAAM,MAAM,GAAG;QACd,eAAe,EAAE;YAChB,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC;YACnC;gBACC,EAAE,IAAI,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE;gBACjC,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE;aACzC;SACD;KACD,CAAC;IAEF,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAC5B,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAE5B,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACtD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;IACvD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAC/D,MAAM,kBAAkB;QACvB,MAAM;YACL,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC;QAC/E,CAAC;KACD;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,kBAAkB,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9D,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * builder instances (e.g. `InlineKeyboard`/`Keyboard` from `@yaebal/keyboard`) expose a
3
+ * `toJSON()` the same way `Date`/`Map`-like classes do. unwrap them so recorded calls and
4
+ * assertions see plain JSON, never a class instance — no `@yaebal/keyboard` dependency needed,
5
+ * this is just duck typing on the standard `toJSON` convention.
6
+ */
7
+ export declare function toPlain<T = unknown>(value: unknown): T;
8
+ /** shallow-clone `params`, normalizing `reply_markup` (and, for `answerInlineQuery`, each `results[].reply_markup`) via {@link toPlain}. */
9
+ export declare function normalizeParams(params: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
10
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAMtD;AAED,4IAA4I;AAC5I,wBAAgB,eAAe,CAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GACzC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAgBrC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * builder instances (e.g. `InlineKeyboard`/`Keyboard` from `@yaebal/keyboard`) expose a
3
+ * `toJSON()` the same way `Date`/`Map`-like classes do. unwrap them so recorded calls and
4
+ * assertions see plain JSON, never a class instance — no `@yaebal/keyboard` dependency needed,
5
+ * this is just duck typing on the standard `toJSON` convention.
6
+ */
7
+ export function toPlain(value) {
8
+ if (value && typeof value.toJSON === "function") {
9
+ return value.toJSON();
10
+ }
11
+ return value;
12
+ }
13
+ /** shallow-clone `params`, normalizing `reply_markup` (and, for `answerInlineQuery`, each `results[].reply_markup`) via {@link toPlain}. */
14
+ export function normalizeParams(params) {
15
+ if (!params)
16
+ return params;
17
+ const out = { ...params };
18
+ if ("reply_markup" in out)
19
+ out.reply_markup = toPlain(out.reply_markup);
20
+ if (Array.isArray(out.results)) {
21
+ out.results = out.results.map((item) => item && typeof item === "object" && "reply_markup" in item
22
+ ? { ...item, reply_markup: toPlain(item.reply_markup) }
23
+ : item);
24
+ }
25
+ return out;
26
+ }
27
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAc,KAAc;IAClD,IAAI,KAAK,IAAI,OAAQ,KAA8B,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC3E,OAAQ,KAAyB,CAAC,MAAM,EAAE,CAAC;IAC5C,CAAC;IAED,OAAO,KAAU,CAAC;AACnB,CAAC;AAED,4IAA4I;AAC5I,MAAM,UAAU,eAAe,CAC9B,MAA2C;IAE3C,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC;IAE3B,MAAM,GAAG,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;IAE1B,IAAI,cAAc,IAAI,GAAG;QAAE,GAAG,CAAC,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAExE,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACtC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,cAAc,IAAI,IAAI;YACzD,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,OAAO,CAAE,IAAgC,CAAC,YAAY,CAAC,EAAE;YACpF,CAAC,CAAC,IAAI,CACP,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** the emojis a given user currently has on `message` (per-message, per-user reaction state). */
2
+ export declare function reactionsOf(message: object): Map<number, string[]>;
3
+ //# sourceMappingURL=reactions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactions.d.ts","sourceRoot":"","sources":["../src/reactions.ts"],"names":[],"mappings":"AAQA,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAQlE"}