@yaebal/core 0.0.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 (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/lib/api.d.ts +66 -0
  4. package/lib/api.d.ts.map +1 -0
  5. package/lib/api.js +176 -0
  6. package/lib/api.js.map +1 -0
  7. package/lib/bot.d.ts +58 -0
  8. package/lib/bot.d.ts.map +1 -0
  9. package/lib/bot.js +125 -0
  10. package/lib/bot.js.map +1 -0
  11. package/lib/composer.d.ts +97 -0
  12. package/lib/composer.d.ts.map +1 -0
  13. package/lib/composer.js +178 -0
  14. package/lib/composer.js.map +1 -0
  15. package/lib/context.d.ts +40 -0
  16. package/lib/context.d.ts.map +1 -0
  17. package/lib/context.js +78 -0
  18. package/lib/context.js.map +1 -0
  19. package/lib/core.test.d.ts +2 -0
  20. package/lib/core.test.d.ts.map +1 -0
  21. package/lib/core.test.js +87 -0
  22. package/lib/core.test.js.map +1 -0
  23. package/lib/filter.test.d.ts +2 -0
  24. package/lib/filter.test.d.ts.map +1 -0
  25. package/lib/filter.test.js +158 -0
  26. package/lib/filter.test.js.map +1 -0
  27. package/lib/format.d.ts +31 -0
  28. package/lib/format.d.ts.map +1 -0
  29. package/lib/format.js +57 -0
  30. package/lib/format.js.map +1 -0
  31. package/lib/index.d.ts +15 -0
  32. package/lib/index.d.ts.map +1 -0
  33. package/lib/index.js +14 -0
  34. package/lib/index.js.map +1 -0
  35. package/lib/media.d.ts +27 -0
  36. package/lib/media.d.ts.map +1 -0
  37. package/lib/media.js +20 -0
  38. package/lib/media.js.map +1 -0
  39. package/lib/telegram-types.d.ts +21 -0
  40. package/lib/telegram-types.d.ts.map +1 -0
  41. package/lib/telegram-types.js +2 -0
  42. package/lib/telegram-types.js.map +1 -0
  43. package/lib/webhook.d.ts +18 -0
  44. package/lib/webhook.d.ts.map +1 -0
  45. package/lib/webhook.js +82 -0
  46. package/lib/webhook.js.map +1 -0
  47. package/package.json +49 -0
  48. package/src/api.ts +276 -0
  49. package/src/bot.ts +168 -0
  50. package/src/composer.ts +280 -0
  51. package/src/context.ts +109 -0
  52. package/src/core.test.ts +108 -0
  53. package/src/filter.test.ts +202 -0
  54. package/src/format.ts +80 -0
  55. package/src/index.ts +53 -0
  56. package/src/media.ts +29 -0
  57. package/src/telegram-types.ts +25 -0
  58. package/src/webhook.ts +117 -0
@@ -0,0 +1,178 @@
1
+ /** koa-style middleware composer with single-`next()` protection. */
2
+ export function compose(middlewares) {
3
+ return function composed(ctx, next) {
4
+ let lastIndex = -1;
5
+ const dispatch = async (i) => {
6
+ if (i <= lastIndex)
7
+ throw new Error("next() called multiple times");
8
+ lastIndex = i;
9
+ const fn = i === middlewares.length ? next : middlewares[i];
10
+ if (!fn)
11
+ return;
12
+ await fn(ctx, () => dispatch(i + 1));
13
+ };
14
+ return dispatch(0);
15
+ };
16
+ }
17
+ function checkField(ctx, field) {
18
+ switch (field) {
19
+ case "text":
20
+ case "caption":
21
+ return typeof ctx.text === "string" && ctx.text.length > 0;
22
+ case "data":
23
+ return Boolean(ctx.callbackQuery?.data);
24
+ case "entities":
25
+ return Boolean(ctx.message?.entities?.length);
26
+ default: {
27
+ const msg = ctx.message;
28
+ return Boolean(msg?.[field]);
29
+ }
30
+ }
31
+ }
32
+ export function matchQuery(ctx, query) {
33
+ const [head, ...rest] = query.split(":");
34
+ if (head && head.length > 0 && ctx.updateType !== head)
35
+ return false;
36
+ for (const field of rest) {
37
+ if (!checkField(ctx, field))
38
+ return false;
39
+ }
40
+ return true;
41
+ }
42
+ /**
43
+ * the chainable middleware pipeline. every context-enriching method returns a
44
+ * composer whose context type carries the new properties — types flow through
45
+ * the whole chain (the GramIO idea). `Composer` is also usable standalone, so
46
+ * feature files can be plain composers with no `Bot` and no token.
47
+ */
48
+ export class Composer {
49
+ middlewares = [];
50
+ /** raw middleware. call `next()` to continue the chain. */
51
+ use(...middleware) {
52
+ this.middlewares.push(...middleware);
53
+ return this;
54
+ }
55
+ /** handle a specific update, optionally narrowed by a filter query. */
56
+ on(query, ...handlers) {
57
+ const handler = compose(handlers);
58
+ this.middlewares.push((ctx, next) => matchQuery(ctx, query) ? handler(ctx, next) : next());
59
+ return this;
60
+ }
61
+ /** handle `/<name>` commands. Strips a trailing `@botname` and parses args. */
62
+ command(name, ...handlers) {
63
+ const handler = compose(handlers);
64
+ this.middlewares.push((ctx, next) => {
65
+ const text = ctx.text;
66
+ if (text === undefined || !text.startsWith("/"))
67
+ return next();
68
+ const [head, ...args] = text.slice(1).split(/\s+/);
69
+ const base = head?.split("@")[0];
70
+ if (base !== name)
71
+ return next();
72
+ Object.assign(ctx, { command: base, args });
73
+ return handler(ctx, next);
74
+ });
75
+ return this;
76
+ }
77
+ /** match message text/caption against a string or regex; exposes `ctx.match`. */
78
+ hears(trigger, ...handlers) {
79
+ const handler = compose(handlers);
80
+ this.middlewares.push((ctx, next) => {
81
+ const text = ctx.text;
82
+ if (text === undefined)
83
+ return next();
84
+ if (typeof trigger === "string") {
85
+ if (text !== trigger)
86
+ return next();
87
+ Object.assign(ctx, { match: text });
88
+ }
89
+ else {
90
+ const m = text.match(trigger);
91
+ if (!m)
92
+ return next();
93
+ Object.assign(ctx, { match: m });
94
+ }
95
+ return handler(ctx, next);
96
+ });
97
+ return this;
98
+ }
99
+ /** match callback-query data against a string or regex; exposes `ctx.match`. */
100
+ callbackQuery(trigger, ...handlers) {
101
+ const handler = compose(handlers);
102
+ this.middlewares.push((ctx, next) => {
103
+ const data = ctx.callbackQuery?.data;
104
+ if (data === undefined)
105
+ return next();
106
+ if (typeof trigger === "string") {
107
+ if (data !== trigger)
108
+ return next();
109
+ Object.assign(ctx, { match: data });
110
+ }
111
+ else {
112
+ const m = data.match(trigger);
113
+ if (!m)
114
+ return next();
115
+ Object.assign(ctx, { match: m });
116
+ }
117
+ return handler(ctx, next);
118
+ });
119
+ return this;
120
+ }
121
+ /** continue only if the predicate holds. */
122
+ guard(predicate) {
123
+ this.middlewares.push(async (ctx, next) => {
124
+ if (await predicate(ctx))
125
+ await next();
126
+ });
127
+ return this;
128
+ }
129
+ /**
130
+ * run `handlers` only when `filter` matches. the filter narrows the context
131
+ * (and may attach data), so handlers see `C & Add`. compose filters with
132
+ * `and` / `or` / `not` from `@yaebal/filters`.
133
+ */
134
+ filter(filter, ...handlers) {
135
+ const handler = compose(handlers);
136
+ this.middlewares.push((ctx, next) => (filter.test(ctx) ? handler(ctx, next) : next()));
137
+ return this;
138
+ }
139
+ /** apply a plugin. its required context (`In`) is checked at compile time. */
140
+ install(plugin) {
141
+ return plugin(this);
142
+ }
143
+ // biome-ignore lint/suspicious/noExplicitAny: overload implementation
144
+ derive(a, b) {
145
+ const scoped = typeof a !== "function";
146
+ const only = scoped ? (Array.isArray(a) ? a : [a]) : null;
147
+ const fn = (scoped ? b : a);
148
+ this.middlewares.push(async (ctx, next) => {
149
+ if (!only || only.includes(ctx.updateType))
150
+ Object.assign(ctx, await fn(ctx));
151
+ await next();
152
+ });
153
+ return this;
154
+ }
155
+ /**
156
+ * static context enrichment. adds `D` to the context type.
157
+ * NOTE: a production build would hoist this out of the per-request path entirely;
158
+ * here it is applied once at the top of the chain for simplicity.
159
+ */
160
+ decorate(value) {
161
+ this.middlewares.push((ctx, next) => {
162
+ Object.assign(ctx, value);
163
+ return next();
164
+ });
165
+ return this;
166
+ }
167
+ /** merge another composer in, inheriting its full context type. */
168
+ extend(other) {
169
+ this.middlewares.push(other.toMiddleware());
170
+ return this;
171
+ }
172
+ /** collapse this composer into a single middleware (used by `extend` and `Bot`). */
173
+ toMiddleware() {
174
+ const composed = compose(this.middlewares);
175
+ return (ctx, next) => composed(ctx, next);
176
+ }
177
+ }
178
+ //# sourceMappingURL=composer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer.js","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { Api } from "./api.js";
2
+ import type { FormatResult } from "./format.js";
3
+ import type { MediaSource } from "./media.js";
4
+ import type { CallbackQuery, Chat, Message, Update, UpdateName, User } from "./telegram-types.js";
5
+ export interface ContextOptions {
6
+ api: Api;
7
+ update: Update;
8
+ updateType: UpdateName;
9
+ }
10
+ type SendText = string | FormatResult;
11
+ /**
12
+ * the base context every update is wrapped in. plugins and `derive`/`decorate`
13
+ * enrich it with extra properties; those extras are tracked at the type level
14
+ * by the Composer, so handlers see them without casting.
15
+ */
16
+ export declare class Context {
17
+ readonly api: Api;
18
+ readonly update: Update;
19
+ readonly updateType: UpdateName;
20
+ constructor(options: ContextOptions);
21
+ get message(): Message | undefined;
22
+ get callbackQuery(): CallbackQuery | undefined;
23
+ get from(): User | undefined;
24
+ get chat(): Chat | undefined;
25
+ get text(): string | undefined;
26
+ /** narrowing helper in the puregram spirit: `if (ctx.is("callback_query"))`. */
27
+ is(updateType: UpdateName): boolean;
28
+ /** send a message to the current chat. accepts a plain string or a `format` result. */
29
+ send(text: SendText, extra?: Record<string, unknown>): Promise<Message>;
30
+ /** reply to the triggering message. */
31
+ reply(text: SendText, extra?: Record<string, unknown>): Promise<Message>;
32
+ /** send a photo. accepts a {@link MediaSource} or a raw file_id / URL string. */
33
+ sendPhoto(photo: MediaSource | string, extra?: Record<string, unknown>): Promise<Message>;
34
+ /** send a document. accepts a {@link MediaSource} or a raw file_id / URL string. */
35
+ sendDocument(document: MediaSource | string, extra?: Record<string, unknown>): Promise<Message>;
36
+ /** answer the current callback query (no-op if there is none). */
37
+ answerCallbackQuery(extra?: Record<string, unknown>): Promise<boolean>;
38
+ }
39
+ export {};
40
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAElG,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;CACvB;AAED,KAAK,QAAQ,GAAG,MAAM,GAAG,YAAY,CAAC;AAOtC;;;;GAIG;AACH,qBAAa,OAAO;IACnB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;gBAEpB,OAAO,EAAE,cAAc;IAMnC,IAAI,OAAO,IAAI,OAAO,GAAG,SAAS,CAEjC;IAED,IAAI,aAAa,IAAI,aAAa,GAAG,SAAS,CAE7C;IAED,IAAI,IAAI,IAAI,IAAI,GAAG,SAAS,CAE3B;IAED,IAAI,IAAI,IAAI,IAAI,GAAG,SAAS,CAE3B;IAED,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED,gFAAgF;IAChF,EAAE,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO;IAInC,uFAAuF;IACvF,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS3E,uCAAuC;IACvC,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS5E,iFAAiF;IACjF,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ7F,oFAAoF;IACpF,YAAY,CACX,QAAQ,EAAE,WAAW,GAAG,MAAM,EAC9B,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACjC,OAAO,CAAC,OAAO,CAAC;IASnB,kEAAkE;IAClE,mBAAmB,CAAC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAM1E"}
package/lib/context.js ADDED
@@ -0,0 +1,78 @@
1
+ function resolveText(text) {
2
+ if (typeof text === "string")
3
+ return { text };
4
+ return { text: text.text, entities: text.entities };
5
+ }
6
+ /**
7
+ * the base context every update is wrapped in. plugins and `derive`/`decorate`
8
+ * enrich it with extra properties; those extras are tracked at the type level
9
+ * by the Composer, so handlers see them without casting.
10
+ */
11
+ export class Context {
12
+ api;
13
+ update;
14
+ updateType;
15
+ constructor(options) {
16
+ this.api = options.api;
17
+ this.update = options.update;
18
+ this.updateType = options.updateType;
19
+ }
20
+ get message() {
21
+ return this.update.message ?? this.update.edited_message ?? this.update.channel_post;
22
+ }
23
+ get callbackQuery() {
24
+ return this.update.callback_query;
25
+ }
26
+ get from() {
27
+ return this.message?.from ?? this.callbackQuery?.from;
28
+ }
29
+ get chat() {
30
+ return this.message?.chat ?? this.callbackQuery?.message?.chat;
31
+ }
32
+ get text() {
33
+ return this.message?.text ?? this.message?.caption;
34
+ }
35
+ /** narrowing helper in the puregram spirit: `if (ctx.is("callback_query"))`. */
36
+ is(updateType) {
37
+ return this.updateType === updateType;
38
+ }
39
+ /** send a message to the current chat. accepts a plain string or a `format` result. */
40
+ send(text, extra = {}) {
41
+ const chatId = this.chat?.id;
42
+ if (chatId === undefined) {
43
+ return Promise.reject(new Error("send(): no chat in this update"));
44
+ }
45
+ return this.api.sendMessage({ chat_id: chatId, ...resolveText(text), ...extra });
46
+ }
47
+ /** reply to the triggering message. */
48
+ reply(text, extra = {}) {
49
+ const replyTo = this.message?.message_id;
50
+ return this.send(text, {
51
+ ...(replyTo !== undefined ? { reply_parameters: { message_id: replyTo } } : {}),
52
+ ...extra,
53
+ });
54
+ }
55
+ /** send a photo. accepts a {@link MediaSource} or a raw file_id / URL string. */
56
+ sendPhoto(photo, extra = {}) {
57
+ const chatId = this.chat?.id;
58
+ if (chatId === undefined)
59
+ return Promise.reject(new Error("sendPhoto(): no chat in this update"));
60
+ return this.api.call("sendPhoto", { chat_id: chatId, photo, ...extra });
61
+ }
62
+ /** send a document. accepts a {@link MediaSource} or a raw file_id / URL string. */
63
+ sendDocument(document, extra = {}) {
64
+ const chatId = this.chat?.id;
65
+ if (chatId === undefined) {
66
+ return Promise.reject(new Error("sendDocument(): no chat in this update"));
67
+ }
68
+ return this.api.call("sendDocument", { chat_id: chatId, document, ...extra });
69
+ }
70
+ /** answer the current callback query (no-op if there is none). */
71
+ answerCallbackQuery(extra = {}) {
72
+ const id = this.callbackQuery?.id;
73
+ if (id === undefined)
74
+ return Promise.resolve(false);
75
+ return this.api.answerCallbackQuery({ callback_query_id: id, ...extra });
76
+ }
77
+ }
78
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=core.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.test.d.ts","sourceRoot":"","sources":["../src/core.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,87 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { encodeRequest } from "./api.js";
4
+ import { Bot } from "./bot.js";
5
+ import { isMediaSource, media } from "./media.js";
6
+ import { webhookCallback } from "./webhook.js";
7
+ test("media helpers are branded and discriminated", () => {
8
+ assert.ok(isMediaSource(media.fileId("AgAC")));
9
+ assert.ok(isMediaSource(media.url("https://x/y.png")));
10
+ assert.equal(isMediaSource({ kind: "fileId", fileId: "x" }), false); // unbranded
11
+ });
12
+ test("encodeRequest sends JSON when there is no upload", async () => {
13
+ const r = await encodeRequest({ chat_id: 1, photo: media.fileId("AgAC") });
14
+ assert.equal(r.contentType, "application/json");
15
+ assert.deepEqual(JSON.parse(r.body), { chat_id: 1, photo: "AgAC" });
16
+ });
17
+ test("encodeRequest inlines a url media to its string", async () => {
18
+ const r = await encodeRequest({ photo: media.url("https://e/p.png") });
19
+ assert.deepEqual(JSON.parse(r.body), { photo: "https://e/p.png" });
20
+ });
21
+ test("encodeRequest builds multipart for a buffer upload", async () => {
22
+ const r = await encodeRequest({
23
+ chat_id: 7,
24
+ photo: media.buffer(new Uint8Array([1, 2, 3]), "pic.png"),
25
+ reply_markup: { inline_keyboard: [] },
26
+ });
27
+ assert.ok(r.body instanceof FormData);
28
+ const form = r.body;
29
+ assert.equal(form.get("photo"), "attach://_file0");
30
+ assert.ok(form.get("_file0") instanceof Blob);
31
+ assert.equal(form.get("chat_id"), "7"); // non-string serialized
32
+ assert.equal(form.get("reply_markup"), '{"inline_keyboard":[]}'); // object → JSON
33
+ });
34
+ test("encodeRequest handles no params", async () => {
35
+ const r = await encodeRequest(undefined);
36
+ assert.equal(r.body, undefined);
37
+ });
38
+ test("encodeRequest rejects nested media (fails loud, no garbage)", async () => {
39
+ await assert.rejects(() => encodeRequest({ media: [{ type: "photo", media: media.path("./a.jpg") }] }), /nested MediaSource/);
40
+ });
41
+ test("encodeRequest throws a helpful error for media.path() without a readFile (edge)", async () => {
42
+ await assert.rejects(() => encodeRequest({ photo: media.path("./a.jpg") }), /needs a filesystem/);
43
+ });
44
+ test("encodeRequest uses an injected readFile for media.path() (runtime-agnostic)", async () => {
45
+ const readFile = async (p) => {
46
+ assert.equal(p, "./pics/cat.jpg"); // path forwarded verbatim
47
+ return new Uint8Array([9, 8, 7]);
48
+ };
49
+ const r = await encodeRequest({ chat_id: 1, photo: media.path("./pics/cat.jpg") }, readFile);
50
+ const form = r.body;
51
+ assert.ok(form.get("_file0") instanceof Blob);
52
+ assert.equal(form.get("photo"), "attach://_file0");
53
+ assert.equal(form.get("_file0").name, "cat.jpg"); // pure-js basename
54
+ });
55
+ test("webhookCallback dispatches a POSTed update and guards method/secret", async () => {
56
+ const seen = [];
57
+ const sink = { handleUpdate: async (u) => void seen.push(u.update_id) };
58
+ const handler = webhookCallback(sink, { secretToken: "s3cret" });
59
+ const body = JSON.stringify({ update_id: 7, message: { text: "hi" } });
60
+ const ok = await handler(new Request("http://x/hook", {
61
+ method: "POST",
62
+ headers: { "x-telegram-bot-api-secret-token": "s3cret" },
63
+ body,
64
+ }));
65
+ assert.equal(ok.status, 200);
66
+ assert.deepEqual(seen, [7]);
67
+ const wrongMethod = await handler(new Request("http://x/hook"));
68
+ assert.equal(wrongMethod.status, 405);
69
+ const wrongSecret = await handler(new Request("http://x/hook", {
70
+ method: "POST",
71
+ headers: { "x-telegram-bot-api-secret-token": "nope" },
72
+ body,
73
+ }));
74
+ assert.equal(wrongSecret.status, 401);
75
+ });
76
+ test("Bot.handleUpdate runs the middleware chain (webhook entry)", async () => {
77
+ let seen = "";
78
+ const bot = new Bot("123:abc").on("message:text", (ctx) => {
79
+ seen = ctx.text;
80
+ });
81
+ await bot.handleUpdate({
82
+ update_id: 1,
83
+ message: { message_id: 1, date: 0, chat: { id: 1, type: "private" }, text: "hi" },
84
+ });
85
+ assert.equal(seen, "hi");
86
+ });
87
+ //# sourceMappingURL=core.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.test.js","sourceRoot":"","sources":["../src/core.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=filter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.test.d.ts","sourceRoot":"","sources":["../src/filter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,158 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { Composer, Context } from "./index.js";
4
+ /** minimal no-op api stub — composer tests never make real api calls. */
5
+ const stubApi = null;
6
+ function makeCtx(update) {
7
+ const keys = Object.keys(update).filter((k) => k !== "update_id");
8
+ const updateType = keys[0];
9
+ return new Context({ api: stubApi, update, updateType });
10
+ }
11
+ function makeMessageCtx(text) {
12
+ return makeCtx({
13
+ update_id: 1,
14
+ message: {
15
+ message_id: 1,
16
+ date: 0,
17
+ chat: { id: 1, type: "private" },
18
+ ...(text !== undefined ? { text } : {}),
19
+ },
20
+ });
21
+ }
22
+ function makeCallbackCtx(data = "btn") {
23
+ return makeCtx({
24
+ update_id: 2,
25
+ callback_query: {
26
+ id: "q1",
27
+ from: { id: 99, is_bot: false, first_name: "U" },
28
+ chat_instance: "ci",
29
+ data,
30
+ },
31
+ });
32
+ }
33
+ /** run `composer.toMiddleware()` against ctx and resolve when the chain ends. */
34
+ async function run(composer, ctx) {
35
+ await composer.toMiddleware()(ctx, async () => { });
36
+ }
37
+ test("filter: runs handlers when filter.test returns true", async () => {
38
+ const alwaysTrue = {
39
+ test(_ctx) {
40
+ return true;
41
+ },
42
+ };
43
+ let called = false;
44
+ const composer = new Composer().filter(alwaysTrue, (_ctx) => {
45
+ called = true;
46
+ });
47
+ await run(composer, makeMessageCtx("hi"));
48
+ assert.ok(called, "handler should have been called");
49
+ });
50
+ test("filter: skips handlers and calls next when filter.test returns false", async () => {
51
+ const alwaysFalse = {
52
+ test(_ctx) {
53
+ return false;
54
+ },
55
+ };
56
+ let handlerCalled = false;
57
+ let nextCalled = false;
58
+ const composer = new Composer()
59
+ .filter(alwaysFalse, (_ctx) => {
60
+ handlerCalled = true;
61
+ })
62
+ .use((_ctx, next) => {
63
+ nextCalled = true;
64
+ return next();
65
+ });
66
+ await run(composer, makeMessageCtx());
67
+ assert.equal(handlerCalled, false, "handler must not run when filter rejects");
68
+ assert.ok(nextCalled, "next middleware must still be reached");
69
+ });
70
+ test("filter: handler sees data attached by filter.test via Object.assign", async () => {
71
+ const regexFilter = {
72
+ test(ctx) {
73
+ const m = ctx.text?.match(/hello (\w+)/);
74
+ if (!m)
75
+ return false;
76
+ Object.assign(ctx, { match: m });
77
+ return true;
78
+ },
79
+ };
80
+ let captured;
81
+ const composer = new Composer().filter(regexFilter, (ctx) => {
82
+ captured = ctx.match;
83
+ });
84
+ await run(composer, makeMessageCtx("hello world"));
85
+ assert.ok(captured, "match should be set on ctx");
86
+ assert.equal(captured[1], "world");
87
+ });
88
+ test("filter: filter.test that attaches data is not called for non-matching ctx", async () => {
89
+ const regexFilter = {
90
+ test(ctx) {
91
+ const m = ctx.text?.match(/hello (\w+)/);
92
+ if (!m)
93
+ return false;
94
+ Object.assign(ctx, { match: m });
95
+ return true;
96
+ },
97
+ };
98
+ let handlerCalled = false;
99
+ const composer = new Composer().filter(regexFilter, (_ctx) => {
100
+ handlerCalled = true;
101
+ });
102
+ // callback_query ctx has no text — regex won't match
103
+ await run(composer, makeCallbackCtx());
104
+ assert.equal(handlerCalled, false);
105
+ });
106
+ test("filter: type guard narrows — handler sees ctx.tag added by filter", async () => {
107
+ const tagFilter = {
108
+ test(ctx) {
109
+ Object.assign(ctx, { tag: 42 });
110
+ return true;
111
+ },
112
+ };
113
+ let seenTag;
114
+ const composer = new Composer().filter(tagFilter, (ctx) => {
115
+ // typescript sees ctx.tag as number here (narrowed by the Filter type)
116
+ seenTag = ctx.tag;
117
+ });
118
+ await run(composer, makeMessageCtx());
119
+ assert.equal(seenTag, 42);
120
+ });
121
+ test("derive scoped: fn runs for the listed update type and field is present", async () => {
122
+ const composer = new Composer().derive("message", (_ctx) => ({ enriched: true }));
123
+ const msgCtx = makeMessageCtx("test");
124
+ await run(composer, msgCtx);
125
+ assert.equal(msgCtx.enriched, true);
126
+ });
127
+ test("derive scoped: fn does NOT run for a different update type", async () => {
128
+ const composer = new Composer().derive("message", (_ctx) => ({ enriched: true }));
129
+ const cbCtx = makeCallbackCtx();
130
+ await run(composer, cbCtx);
131
+ assert.equal(cbCtx.enriched, undefined);
132
+ });
133
+ test("derive scoped: both updateTypes work when given an array", async () => {
134
+ const composer = new Composer().derive(["message", "callback_query"], (_ctx) => ({
135
+ enriched: true,
136
+ }));
137
+ const msgCtx = makeMessageCtx();
138
+ const cbCtx = makeCallbackCtx();
139
+ await run(composer, msgCtx);
140
+ await run(composer, cbCtx);
141
+ assert.equal(msgCtx.enriched, true);
142
+ assert.equal(cbCtx.enriched, true);
143
+ });
144
+ test("derive scoped: unscoped array — non-listed type is not enriched", async () => {
145
+ const composer = new Composer().derive("message", (_ctx) => ({ enriched: true }));
146
+ const cbCtx = makeCallbackCtx();
147
+ await run(composer, cbCtx);
148
+ assert.equal(cbCtx.enriched, undefined);
149
+ });
150
+ test("derive scoped: fn receives the context for async enrichment", async () => {
151
+ const composer = new Composer().derive("message", async (ctx) => ({
152
+ textLen: ctx.text?.length ?? 0,
153
+ }));
154
+ const msgCtx = makeMessageCtx("hello");
155
+ await run(composer, msgCtx);
156
+ assert.equal(msgCtx.textLen, 5);
157
+ });
158
+ //# sourceMappingURL=filter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.test.js","sourceRoot":"","sources":["../src/filter.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { MessageEntity } from "./telegram-types.js";
2
+ /**
3
+ * formatting without `parse_mode`: tagged template literals that build proper
4
+ * `MessageEntity` objects, so there is nothing to escape (the GramIO idea).
5
+ */
6
+ export interface FormatResult {
7
+ text: string;
8
+ entities: MessageEntity[];
9
+ }
10
+ /** a piece of text that carries its own entities, used inside `format`. */
11
+ export declare class Stringable implements FormatResult {
12
+ readonly text: string;
13
+ readonly entities: MessageEntity[];
14
+ constructor(text: string, entities?: MessageEntity[]);
15
+ }
16
+ type Insertable = Stringable | FormatResult | string | number | bigint;
17
+ /** stitches the literal parts and interpolations into one `{ text, entities }`. */
18
+ export declare function format(strings: TemplateStringsArray, ...subs: Insertable[]): FormatResult;
19
+ export declare const bold: (value: string) => Stringable;
20
+ export declare const italic: (value: string) => Stringable;
21
+ export declare const underline: (value: string) => Stringable;
22
+ export declare const strikethrough: (value: string) => Stringable;
23
+ export declare const spoiler: (value: string) => Stringable;
24
+ export declare const code: (value: string) => Stringable;
25
+ export declare const pre: (value: string) => Stringable;
26
+ export declare function link(text: string, url: string): Stringable;
27
+ export declare function mention(text: string, user: {
28
+ id: number;
29
+ }): Stringable;
30
+ export {};
31
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,2EAA2E;AAC3E,qBAAa,UAAW,YAAW,YAAY;IAE7C,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,QAAQ,EAAE,aAAa,EAAE;gBADzB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,aAAa,EAAO;CAExC;AAED,KAAK,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAYvE,mFAAmF;AACnF,wBAAgB,MAAM,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,GAAG,YAAY,CAwBzF;AAOD,eAAO,MAAM,IAAI,UAJD,MAAM,KAAG,UAIO,CAAC;AACjC,eAAO,MAAM,MAAM,UALH,MAAM,KAAG,UAKW,CAAC;AACrC,eAAO,MAAM,SAAS,UANN,MAAM,KAAG,UAMiB,CAAC;AAC3C,eAAO,MAAM,aAAa,UAPV,MAAM,KAAG,UAOyB,CAAC;AACnD,eAAO,MAAM,OAAO,UARJ,MAAM,KAAG,UAQa,CAAC;AACvC,eAAO,MAAM,IAAI,UATD,MAAM,KAAG,UASO,CAAC;AACjC,eAAO,MAAM,GAAG,UAVA,MAAM,KAAG,UAUK,CAAC;AAE/B,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAE1D;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAItE"}