stitchkit 0.100.2 → 0.101.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 +153 -0
- package/README.md +2 -1
- package/dist/entrypoints/agent-runtime/browser.js +119 -119
- package/dist/entrypoints/agent-runtime/coding-tools.js +15 -20
- package/dist/entrypoints/agent-runtime/harness.js +55 -63
- package/dist/entrypoints/agent-runtime/openrouter.js +6 -6
- package/dist/entrypoints/agent-runtime/sandbox.js +20 -25
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +11 -13
- package/dist/entrypoints/agent-runtime/sqlite/node.js +11 -13
- package/dist/entrypoints/agent-runtime/testing.js +32 -34
- package/dist/entrypoints/agent-runtime.js +395 -402
- package/dist/entrypoints/application/diagnostic-journal.js +26 -27
- package/dist/entrypoints/application/directory-inbox.js +16 -17
- package/dist/entrypoints/application/grammy.js +13 -13
- package/dist/entrypoints/application/opentelemetry.js +13 -13
- package/dist/entrypoints/application/schemas.js +24 -24
- package/dist/entrypoints/application.js +209 -210
- package/dist/entrypoints/bin/upgrade-cli.js +2 -2
- package/dist/entrypoints/cli.js +81 -84
- package/dist/entrypoints/contract.js +54 -57
- package/dist/entrypoints/declaration.js +22 -22
- package/dist/entrypoints/files.js +18 -21
- package/dist/entrypoints/geo.js +7 -8
- package/dist/entrypoints/google.js +2 -2
- package/dist/entrypoints/index.js +150 -154
- package/dist/entrypoints/live.js +45 -47
- package/dist/entrypoints/node.js +68 -74
- package/dist/entrypoints/oauth.js +6 -6
- package/dist/entrypoints/observability.d.ts +1 -0
- package/dist/entrypoints/observability.d.ts.map +1 -1
- package/dist/entrypoints/observability.js +116 -87
- package/dist/entrypoints/primitives.js +35 -35
- package/dist/entrypoints/react/keyboard.d.ts +4 -0
- package/dist/entrypoints/react/keyboard.d.ts.map +1 -0
- package/dist/entrypoints/react/keyboard.js +397 -0
- package/dist/entrypoints/react.js +10 -10
- package/dist/entrypoints/release.js +7 -7
- package/dist/entrypoints/remote.js +15 -17
- package/dist/entrypoints/server.js +198 -200
- package/dist/entrypoints/telegram/html.d.ts +5 -0
- package/dist/entrypoints/telegram/html.d.ts.map +1 -0
- package/dist/entrypoints/telegram/html.js +26 -0
- package/dist/entrypoints/telegram/screens.d.ts +14 -0
- package/dist/entrypoints/telegram/screens.d.ts.map +1 -0
- package/dist/entrypoints/telegram/screens.js +2028 -0
- package/dist/entrypoints/telegram.d.ts +11 -3
- package/dist/entrypoints/telegram.d.ts.map +1 -1
- package/dist/entrypoints/telegram.js +779 -159
- package/dist/entrypoints/testing.js +75 -79
- package/dist/entrypoints/tools/connections.js +30 -32
- package/dist/entrypoints/tools/contract.js +18 -20
- package/dist/entrypoints/tools/invoker.js +12 -17
- package/dist/entrypoints/tools.js +224 -229
- package/dist/entrypoints/tracking/server.js +6 -6
- package/dist/entrypoints/tracking.js +40 -42
- package/dist/entrypoints/voice.js +3 -3
- package/dist/{index-eaysmdtq.js → index-0ndk4555.js} +56 -56
- package/dist/{index-2bxbyab0.js → index-1bx1xv1k.js} +1 -1
- package/dist/{index-p5r7qpdd.js → index-1eeqpadm.js} +4 -4
- package/dist/{index-hadmp193.js → index-3ct7hhq6.js} +210 -210
- package/dist/{index-e01r054k.js → index-45tdvqa3.js} +26 -26
- package/dist/{index-dce5wxcq.js → index-4s0fep5q.js} +8 -8
- package/dist/index-50ejfz75.js +9 -0
- package/dist/{index-m9zn6cpq.js → index-5107q189.js} +12 -12
- package/dist/{index-sbdmyz75.js → index-5cc6r990.js} +3 -3
- package/dist/{index-c6bxbsnc.js → index-7kxw1awn.js} +2 -2
- package/dist/{index-0r0fbk7z.js → index-7z9ym7t8.js} +28 -28
- package/dist/{index-p7hgdyhb.js → index-814xpr6d.js} +2 -2
- package/dist/{index-gtj3zc53.js → index-8kzct1d6.js} +99 -99
- package/dist/{index-1zbbzgfh.js → index-9pw0a51a.js} +4 -4
- package/dist/{index-79z7m7dt.js → index-a9pd417t.js} +35 -35
- package/dist/{index-ham5sz8m.js → index-aaj97r4y.js} +45 -45
- package/dist/{index-s812ycat.js → index-aan6hqb9.js} +3 -3
- package/dist/{index-10x2hbx0.js → index-adhgd4pe.js} +44 -44
- package/dist/{index-2gxzjvyr.js → index-afjvjyck.js} +23 -23
- package/dist/{index-qmy6zq75.js → index-ahvsksw5.js} +16 -16
- package/dist/{index-hr4tzbwq.js → index-ap3gqe56.js} +3 -3
- package/dist/{index-znfs20ss.js → index-b2bz0kdm.js} +16 -16
- package/dist/{index-r066jx76.js → index-c6aagrbd.js} +148 -120
- package/dist/{index-k30h66yd.js → index-ck71tbcf.js} +9 -9
- package/dist/{index-eqqvgdb3.js → index-ct75c3at.js} +2 -2
- package/dist/{index-gw8bmyaj.js → index-djjhhgjk.js} +41 -41
- package/dist/{index-8qt2m66q.js → index-dvwj1f43.js} +43 -43
- package/dist/index-e05cjgtf.js +144 -0
- package/dist/{index-yw4fzmdz.js → index-e6c2spsd.js} +79 -30
- package/dist/{index-fhwqwxe0.js → index-ej331hah.js} +1 -1
- package/dist/{index-9t3dffv2.js → index-ewbaywk5.js} +5 -5
- package/dist/{index-7p1f0vp2.js → index-fyphpknh.js} +25 -25
- package/dist/{index-83bdg97s.js → index-g149e9pk.js} +1 -1
- package/dist/{index-ngfhvfpd.js → index-g6a3na27.js} +4 -4
- package/dist/{index-qwhynv6x.js → index-h124epcf.js} +20 -20
- package/dist/{index-h6eeys1v.js → index-h97tqqp1.js} +9 -9
- package/dist/{index-zcgf3gqf.js → index-hby4qhax.js} +10 -10
- package/dist/{index-5jrd0yng.js → index-j8aw2c0e.js} +4 -4
- package/dist/{index-d8m8zpd8.js → index-jzczzcsb.js} +14 -14
- package/dist/index-kqnd6a85.js +26 -0
- package/dist/{index-35mh5bhj.js → index-kw1ws0cs.js} +12 -12
- package/dist/{index-4gcnakhx.js → index-m38ej7e5.js} +2 -2
- package/dist/{index-ndddxtyx.js → index-m79yr938.js} +19 -19
- package/dist/{index-5p2m7we1.js → index-m9fndta6.js} +11 -11
- package/dist/index-mnkk1ffz.js +150 -0
- package/dist/index-n15jny5b.js +4 -0
- package/dist/{index-2vtcmrj9.js → index-n53mswt3.js} +20 -20
- package/dist/{index-bt14acb5.js → index-n5yqxd3d.js} +21 -21
- package/dist/{index-bm5rx7n6.js → index-n9tyz8z3.js} +12 -12
- package/dist/index-nmaqxdx7.js +463 -0
- package/dist/{index-mqv3b5rh.js → index-nx6eazt0.js} +30 -30
- package/dist/{index-3z73fh2c.js → index-q0th8q0z.js} +9 -9
- package/dist/{index-v260df89.js → index-qjeggp8w.js} +20 -20
- package/dist/{index-zfrhfn82.js → index-r1z0rd9c.js} +20 -20
- package/dist/{index-wqgjfstn.js → index-r5c4fedv.js} +3 -3
- package/dist/{index-cyktc4bp.js → index-r5hsgkz5.js} +118 -118
- package/dist/{index-0b1ess5e.js → index-r7a7r7ha.js} +1 -1
- package/dist/{index-1j8pm5t0.js → index-rfkh44ss.js} +15 -15
- package/dist/index-rz3z13e9.js +47 -0
- package/dist/{index-k6gpggb9.js → index-s80awwsw.js} +35 -35
- package/dist/{index-vqr7f7v8.js → index-ssmd41kh.js} +124 -124
- package/dist/index-sznqf8fx.js +20 -0
- package/dist/{index-ensd0r74.js → index-v14qqmys.js} +41 -41
- package/dist/{index-paz6sgdp.js → index-v4a2fjtk.js} +36 -36
- package/dist/{index-d9pk3td4.js → index-w0eqdhyd.js} +14 -14
- package/dist/{index-5qn1kx34.js → index-w575evhq.js} +29 -29
- package/dist/{index-3gye9wzb.js → index-w5s3xftz.js} +26 -26
- package/dist/{index-tm88avnj.js → index-wykebpn9.js} +3 -3
- package/dist/{index-sh3s86na.js → index-xkhk5d6g.js} +17 -17
- package/dist/{index-6k1937bx.js → index-xqhede6x.js} +4 -4
- package/dist/{index-f4fx2tza.js → index-zs4ms3th.js} +181 -181
- package/dist/{index-k5hsfc3j.js → index-zsvzhjtk.js} +1 -1
- package/dist/internal/secret-shapes.d.ts +21 -0
- package/dist/internal/secret-shapes.d.ts.map +1 -0
- package/dist/observability/bounded-logger.d.ts +2 -0
- package/dist/observability/bounded-logger.d.ts.map +1 -1
- package/dist/observability/sanitize.d.ts +6 -0
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/observability/secret-values.d.ts +25 -0
- package/dist/observability/secret-values.d.ts.map +1 -0
- package/dist/react/keyboard/hooks.d.ts +71 -0
- package/dist/react/keyboard/hooks.d.ts.map +1 -0
- package/dist/react/keyboard/layers.d.ts +98 -0
- package/dist/react/keyboard/layers.d.ts.map +1 -0
- package/dist/react/keyboard/zone.d.ts +139 -0
- package/dist/react/keyboard/zone.d.ts.map +1 -0
- package/dist/telegram/bot-api.d.ts +7 -4
- package/dist/telegram/bot-api.d.ts.map +1 -1
- package/dist/telegram/crypto.d.ts +16 -0
- package/dist/telegram/crypto.d.ts.map +1 -0
- package/dist/telegram/html/check.d.ts +26 -0
- package/dist/telegram/html/check.d.ts.map +1 -0
- package/dist/telegram/html/nodes.d.ts +70 -0
- package/dist/telegram/html/nodes.d.ts.map +1 -0
- package/dist/telegram/html/parse.d.ts +35 -0
- package/dist/telegram/html/parse.d.ts.map +1 -0
- package/dist/telegram/html/render.d.ts +39 -0
- package/dist/telegram/html/render.d.ts.map +1 -0
- package/dist/telegram/init-data.d.ts +3 -44
- package/dist/telegram/init-data.d.ts.map +1 -1
- package/dist/telegram/operator-channel.d.ts +19 -3
- package/dist/telegram/operator-channel.d.ts.map +1 -1
- package/dist/telegram/operator-dedupe.d.ts +44 -0
- package/dist/telegram/operator-dedupe.d.ts.map +1 -0
- package/dist/telegram/screens/callback-codec.d.ts +34 -0
- package/dist/telegram/screens/callback-codec.d.ts.map +1 -0
- package/dist/telegram/screens/dispatch.d.ts +22 -0
- package/dist/telegram/screens/dispatch.d.ts.map +1 -0
- package/dist/telegram/screens/display.d.ts +70 -0
- package/dist/telegram/screens/display.d.ts.map +1 -0
- package/dist/telegram/screens/execute.d.ts +57 -0
- package/dist/telegram/screens/execute.d.ts.map +1 -0
- package/dist/telegram/screens/html.d.ts +36 -0
- package/dist/telegram/screens/html.d.ts.map +1 -0
- package/dist/telegram/screens/outcome.d.ts +74 -0
- package/dist/telegram/screens/outcome.d.ts.map +1 -0
- package/dist/telegram/screens/path.d.ts +29 -0
- package/dist/telegram/screens/path.d.ts.map +1 -0
- package/dist/telegram/screens/reconcile.d.ts +67 -0
- package/dist/telegram/screens/reconcile.d.ts.map +1 -0
- package/dist/telegram/screens/registry.d.ts +23 -0
- package/dist/telegram/screens/registry.d.ts.map +1 -0
- package/dist/telegram/screens/render.d.ts +70 -0
- package/dist/telegram/screens/render.d.ts.map +1 -0
- package/dist/telegram/screens/runtime.d.ts +133 -0
- package/dist/telegram/screens/runtime.d.ts.map +1 -0
- package/dist/telegram/screens/screen-types.d.ts +130 -0
- package/dist/telegram/screens/screen-types.d.ts.map +1 -0
- package/dist/telegram/screens/screen.d.ts +138 -0
- package/dist/telegram/screens/screen.d.ts.map +1 -0
- package/dist/telegram/screens/state.d.ts +115 -0
- package/dist/telegram/screens/state.d.ts.map +1 -0
- package/dist/telegram/screens/telegram-screens.d.ts +18 -0
- package/dist/telegram/screens/telegram-screens.d.ts.map +1 -0
- package/dist/telegram/screens/test-chat-telegram.d.ts +47 -0
- package/dist/telegram/screens/test-chat-telegram.d.ts.map +1 -0
- package/dist/telegram/screens/test-chat.d.ts +66 -0
- package/dist/telegram/screens/test-chat.d.ts.map +1 -0
- package/dist/telegram/screens/view.d.ts +89 -0
- package/dist/telegram/screens/view.d.ts.map +1 -0
- package/dist/telegram/send-failure.d.ts +25 -0
- package/dist/telegram/send-failure.d.ts.map +1 -1
- package/dist/telegram/update-intake.d.ts +82 -0
- package/dist/telegram/update-intake.d.ts.map +1 -0
- package/dist/telegram/update-store-check.d.ts +22 -0
- package/dist/telegram/update-store-check.d.ts.map +1 -0
- package/dist/telegram/update-store-postgres.d.ts +33 -0
- package/dist/telegram/update-store-postgres.d.ts.map +1 -0
- package/dist/telegram/update-store-sqlite.d.ts +26 -0
- package/dist/telegram/update-store-sqlite.d.ts.map +1 -0
- package/dist/telegram/update-store.d.ts +67 -0
- package/dist/telegram/update-store.d.ts.map +1 -0
- package/dist/telegram/user.d.ts +51 -0
- package/dist/telegram/user.d.ts.map +1 -0
- package/dist/telegram/webhook.d.ts +105 -0
- package/dist/telegram/webhook.d.ts.map +1 -0
- package/dist/tools/cli/args-fields.d.ts +8 -0
- package/dist/tools/cli/args-fields.d.ts.map +1 -1
- package/dist/tools/cli/args-route.d.ts.map +1 -1
- package/dist/tools/cli/args.d.ts.map +1 -1
- package/dist/tools/cli/help.d.ts.map +1 -1
- package/llms/cli.txt +8 -2
- package/llms/observability.txt +15 -2
- package/llms/react-keyboard.txt +193 -0
- package/llms/server.4.txt +16 -0
- package/llms/stitchkit.txt +10 -4
- package/llms/telegram-html.txt +35 -0
- package/llms/telegram-screens.txt +365 -0
- package/llms/telegram.txt +245 -11
- package/llms/{upgrading-0.74.0-0.100.0.txt → upgrading-0.74.0-0.101.0.txt} +39 -3
- package/llms/upgrading.txt +1 -1
- package/llms-full.txt +889 -19
- package/llms.txt +12 -7
- package/package.json +17 -2
- package/dist/index-2k4yrqkc.js +0 -9
- package/dist/index-crykmqkr.js +0 -26
- package/dist/index-y64k3s0h.js +0 -150
- package/dist/index-y91ry9h2.js +0 -4
- /package/dist/{index-hffjpmq9.js → index-9jfdm5tg.js} +0 -0
- /package/dist/{index-zjjyynm3.js → index-d4pa2bgs.js} +0 -0
- /package/dist/{index-rxfy4cq7.js → index-dwfv9a85.js} +0 -0
- /package/dist/{index-5jm36zv1.js → index-gfk7nrht.js} +0 -0
- /package/dist/{index-b9aefrqd.js → index-n1dqr7gf.js} +0 -0
- /package/dist/{index-q3pyb9n0.js → index-sykb5ag7.js} +0 -0
- /package/dist/{index-48ffdxgk.js → index-xhzq2qvj.js} +0 -0
- /package/dist/{index-kzfs85xp.js → index-zsarb5xp.js} +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a chat's screen is, kept where a restart cannot lose it.
|
|
3
|
+
*
|
|
4
|
+
* One record per chat: the view it shows — screen, params, the messages with
|
|
5
|
+
* their fingerprints, the tokens of buttons too long to carry their address —
|
|
6
|
+
* and the remarks waiting to expire. Nothing else: no history, no copy of
|
|
7
|
+
* application data. The record is read through its schema every time, because
|
|
8
|
+
* storage outlives releases and a record written by another version is not
|
|
9
|
+
* evidence of anything.
|
|
10
|
+
*
|
|
11
|
+
* The storage is grammY's own `StorageAdapter`, so any adapter a grammY bot
|
|
12
|
+
* already uses for sessions (`@grammyjs/storage-*`, `MemorySessionStorage` in
|
|
13
|
+
* tests, or ten lines over the application's database) keeps screens too.
|
|
14
|
+
*/
|
|
15
|
+
import type { StorageAdapter } from 'grammy';
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
/**
|
|
18
|
+
* How much one chat's record may hold. A view is checked against these when it
|
|
19
|
+
* is rendered, before anything reaches Telegram: a record refused after the
|
|
20
|
+
* calls would leave the chat showing messages nothing tracks.
|
|
21
|
+
*/
|
|
22
|
+
export declare const RECORD_LIMITS: {
|
|
23
|
+
/** Messages in one view. */
|
|
24
|
+
messages: number;
|
|
25
|
+
/** Characters of a message key. */
|
|
26
|
+
key: number;
|
|
27
|
+
/** Buttons whose address is kept in the record, per view. */
|
|
28
|
+
tokens: number;
|
|
29
|
+
/** Characters of one kept address. */
|
|
30
|
+
tokenBody: number;
|
|
31
|
+
/** Remarks waiting to expire. */
|
|
32
|
+
remarks: number;
|
|
33
|
+
};
|
|
34
|
+
declare const ScreenViewStateSchema: z.ZodObject<{
|
|
35
|
+
screen: z.ZodString;
|
|
36
|
+
params: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
37
|
+
messages: z.ZodArray<z.ZodObject<{
|
|
38
|
+
key: z.ZodString;
|
|
39
|
+
id: z.ZodNumber;
|
|
40
|
+
kind: z.ZodEnum<{
|
|
41
|
+
animation: "animation";
|
|
42
|
+
audio: "audio";
|
|
43
|
+
document: "document";
|
|
44
|
+
photo: "photo";
|
|
45
|
+
rich: "rich";
|
|
46
|
+
text: "text";
|
|
47
|
+
video: "video";
|
|
48
|
+
}>;
|
|
49
|
+
fingerprint: z.ZodNullable<z.ZodObject<{
|
|
50
|
+
content: z.ZodString;
|
|
51
|
+
media: z.ZodString;
|
|
52
|
+
markup: z.ZodString;
|
|
53
|
+
}, z.core.$strict>>;
|
|
54
|
+
}, z.core.$strict>>;
|
|
55
|
+
tokens: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
56
|
+
}, z.core.$strict>;
|
|
57
|
+
export declare const ScreenChatStateSchema: z.ZodObject<{
|
|
58
|
+
version: z.ZodLiteral<1>;
|
|
59
|
+
view: z.ZodNullable<z.ZodObject<{
|
|
60
|
+
screen: z.ZodString;
|
|
61
|
+
params: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
62
|
+
messages: z.ZodArray<z.ZodObject<{
|
|
63
|
+
key: z.ZodString;
|
|
64
|
+
id: z.ZodNumber;
|
|
65
|
+
kind: z.ZodEnum<{
|
|
66
|
+
animation: "animation";
|
|
67
|
+
audio: "audio";
|
|
68
|
+
document: "document";
|
|
69
|
+
photo: "photo";
|
|
70
|
+
rich: "rich";
|
|
71
|
+
text: "text";
|
|
72
|
+
video: "video";
|
|
73
|
+
}>;
|
|
74
|
+
fingerprint: z.ZodNullable<z.ZodObject<{
|
|
75
|
+
content: z.ZodString;
|
|
76
|
+
media: z.ZodString;
|
|
77
|
+
markup: z.ZodString;
|
|
78
|
+
}, z.core.$strict>>;
|
|
79
|
+
}, z.core.$strict>>;
|
|
80
|
+
tokens: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
81
|
+
}, z.core.$strict>>;
|
|
82
|
+
expiring: z.ZodArray<z.ZodObject<{
|
|
83
|
+
id: z.ZodNumber;
|
|
84
|
+
at: z.ZodNumber;
|
|
85
|
+
}, z.core.$strict>>;
|
|
86
|
+
}, z.core.$strict>;
|
|
87
|
+
export type ScreenChatState = z.infer<typeof ScreenChatStateSchema>;
|
|
88
|
+
export type ScreenViewState = z.infer<typeof ScreenViewStateSchema>;
|
|
89
|
+
/** Where chats' screen state survives a restart: grammY's storage contract. */
|
|
90
|
+
export type TelegramScreenStorage = StorageAdapter<ScreenChatState>;
|
|
91
|
+
export declare const emptyChatState: () => ScreenChatState;
|
|
92
|
+
export interface ReadChatState {
|
|
93
|
+
readonly state: ScreenChatState;
|
|
94
|
+
/** The stored record did not match the schema and was set aside. */
|
|
95
|
+
readonly discarded: boolean;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The chat's record. A storage that fails to answer fails the update — reading
|
|
99
|
+
* "nothing" would send a message meant as input to whatever handles text next.
|
|
100
|
+
* A record that does not match this release's schema is set aside and
|
|
101
|
+
* reported: refusing it forever would leave the chat without screens.
|
|
102
|
+
*/
|
|
103
|
+
export declare function readChatState(storage: TelegramScreenStorage, key: string): Promise<ReadChatState>;
|
|
104
|
+
export declare function writeChatState(storage: TelegramScreenStorage, key: string, state: ScreenChatState): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* One chat at a time, in arrival order. Two presses in one chat are two
|
|
107
|
+
* read-render-write passes over one record; interleaved, the second would draw
|
|
108
|
+
* over messages the first is deleting. A chat's queue is dropped once it
|
|
109
|
+
* drains, and bounded while it has work: a chat that sends faster than
|
|
110
|
+
* Telegram answers is refused rather than queued without end.
|
|
111
|
+
*/
|
|
112
|
+
export type ChatQueue = <T>(key: string, task: () => Promise<T>) => Promise<T>;
|
|
113
|
+
export declare function createChatQueue(maxPending: number): ChatQueue;
|
|
114
|
+
export {};
|
|
115
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/telegram/screens/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;GAIG;AACH,eAAO,MAAM,aAAa;IACxB,4BAA4B;IAC5B,QAAQ;IACR,mCAAmC;IACnC,GAAG;IACH,6DAA6D;IAC7D,MAAM;IACN,sCAAsC;IACtC,SAAS;IACT,iCAAiC;IACjC,OAAO;CACR,CAAC;AAeF,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;kBAUhB,CAAC;AAMZ,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAOvB,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,+EAA+E;AAC/E,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,eAAe,CAAC,CAAC;AAEpE,eAAO,MAAM,cAAc,QAAO,eAIhC,CAAC;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,qBAAqB,EAC9B,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,aAAa,CAAC,CAOxB;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,EAC9B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAE/E,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAiB7D"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `telegramScreens<Ctx>()` — where a bot's screens are declared and composed.
|
|
3
|
+
*
|
|
4
|
+
* The bot's grammY context type is named once; every screen, group and handler
|
|
5
|
+
* declared from here sees it. `create` checks the declaration as a whole — ids,
|
|
6
|
+
* paths, that every screen with params has a load above it — and returns the
|
|
7
|
+
* middleware.
|
|
8
|
+
*/
|
|
9
|
+
import type { Context } from 'grammy';
|
|
10
|
+
import { type TelegramScreens, type TelegramScreensConfig } from './runtime.js';
|
|
11
|
+
import { ScreenScope } from './screen.js';
|
|
12
|
+
export declare class TelegramScreensRoot<C extends Context> extends ScreenScope<C, Record<never, never>, undefined> {
|
|
13
|
+
/** Compose the declared screens into grammY middleware. */
|
|
14
|
+
create(config: TelegramScreensConfig<C>): TelegramScreens<C>;
|
|
15
|
+
}
|
|
16
|
+
/** Declare a bot's screens against its grammY context type. */
|
|
17
|
+
export declare function telegramScreens<C extends Context>(): TelegramScreensRoot<C>;
|
|
18
|
+
//# sourceMappingURL=telegram-screens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram-screens.d.ts","sourceRoot":"","sources":["../../../src/telegram/screens/telegram-screens.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAKtC,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAiB,WAAW,EAAE,MAAM,UAAU,CAAC;AAEtD,qBAAa,mBAAmB,CAAC,CAAC,SAAS,OAAO,CAAE,SAAQ,WAAW,CACrE,CAAC,EACD,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACpB,SAAS,CACV;IACC,2DAA2D;IAC3D,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAE3D;CACF;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,CAAC,SAAS,OAAO,KAAK,mBAAmB,CAAC,CAAC,CAAC,CAE3E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Telegram side of the test chat: the messages it keeps and how it answers
|
|
3
|
+
* the Bot API calls screens make.
|
|
4
|
+
*
|
|
5
|
+
* It keeps the rules screens depend on: an edit that changes nothing is
|
|
6
|
+
* refused as "message is not modified", a deleted message cannot be edited or
|
|
7
|
+
* deleted again, a text message has no caption to edit, a media message no
|
|
8
|
+
* text.
|
|
9
|
+
*/
|
|
10
|
+
import type { Chat, InlineKeyboardMarkup, Message, User } from 'grammy/types';
|
|
11
|
+
export type TestMessageKind = 'text' | 'rich' | 'photo' | 'video' | 'animation' | 'document' | 'audio';
|
|
12
|
+
export interface StoredMessage {
|
|
13
|
+
id: number;
|
|
14
|
+
from: 'bot' | 'user';
|
|
15
|
+
kind: TestMessageKind;
|
|
16
|
+
text: string;
|
|
17
|
+
media: string | undefined;
|
|
18
|
+
rich: unknown;
|
|
19
|
+
markup: InlineKeyboardMarkup | undefined;
|
|
20
|
+
deleted: boolean;
|
|
21
|
+
}
|
|
22
|
+
export type TestChatPeer = Chat.PrivateChat | Chat.GroupChat | Chat.SupergroupChat;
|
|
23
|
+
export interface FakeTelegramIdentity {
|
|
24
|
+
readonly chat: TestChatPeer;
|
|
25
|
+
readonly botUser: User;
|
|
26
|
+
readonly user: User;
|
|
27
|
+
}
|
|
28
|
+
export interface FakeTelegramRefusal {
|
|
29
|
+
readonly ok: false;
|
|
30
|
+
readonly error_code: number;
|
|
31
|
+
readonly description: string;
|
|
32
|
+
}
|
|
33
|
+
export interface FakeTelegram {
|
|
34
|
+
/** Every message the chat has had, deleted ones marked. */
|
|
35
|
+
readonly stored: StoredMessage[];
|
|
36
|
+
/** Press answers — the toasts — in order. */
|
|
37
|
+
readonly answers: string[];
|
|
38
|
+
/** A message still in the chat. */
|
|
39
|
+
find(id: unknown): StoredMessage | undefined;
|
|
40
|
+
asTelegram(message: StoredMessage): Message;
|
|
41
|
+
/** A Bot API call answered the way Telegram would. */
|
|
42
|
+
answer(method: string, payload: Readonly<Record<string, unknown>>): unknown;
|
|
43
|
+
refuse(description: string, errorCode?: number): FakeTelegramRefusal;
|
|
44
|
+
nextMessageId(): number;
|
|
45
|
+
}
|
|
46
|
+
export declare function createFakeTelegram({ chat, botUser, user, }: FakeTelegramIdentity): FakeTelegram;
|
|
47
|
+
//# sourceMappingURL=test-chat-telegram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-chat-telegram.d.ts","sourceRoot":"","sources":["../../../src/telegram/screens/test-chat-telegram.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAK9E,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,MAAM,GACN,OAAO,GACP,OAAO,GACP,WAAW,GACX,UAAU,GACV,OAAO,CAAC;AAEZ,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IACrB,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC;AAEnF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC;IACjC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,mCAAmC;IACnC,IAAI,CAAC,EAAE,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAAC;IAC7C,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC;IAC5C,sDAAsD;IACtD,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC;IAC5E,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACrE,aAAa,IAAI,MAAM,CAAC;CACzB;AAkID,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,IAAI,GACL,EAAE,oBAAoB,GAAG,YAAY,CAgHrC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A chat with the application's own bot, and no Telegram.
|
|
3
|
+
*
|
|
4
|
+
* `createScreenTestChat(bot)` takes the `Bot` the application composes — its
|
|
5
|
+
* middleware, commands and error boundary, in their real order — and answers
|
|
6
|
+
* its Bot API calls from memory through `bot.api.config.use`. Updates enter
|
|
7
|
+
* through `bot.handleUpdate`, so what a test proves is what the bot does:
|
|
8
|
+
* that `/support` is not swallowed by a screen waiting for text, that a press
|
|
9
|
+
* edits the message it came from.
|
|
10
|
+
*
|
|
11
|
+
* The Telegram it talks to is `test-chat-telegram.ts`, which keeps the rules
|
|
12
|
+
* screens depend on. Nothing here imports grammY at run time; the bot brings it.
|
|
13
|
+
*/
|
|
14
|
+
import type { Bot, Context } from 'grammy';
|
|
15
|
+
import type { InlineKeyboardMarkup } from 'grammy/types';
|
|
16
|
+
import { type TestMessageKind } from './test-chat-telegram.js';
|
|
17
|
+
/** A message as the user sees it. */
|
|
18
|
+
export interface TestChatMessage {
|
|
19
|
+
readonly id: number;
|
|
20
|
+
readonly from: 'bot' | 'user';
|
|
21
|
+
readonly kind: TestMessageKind;
|
|
22
|
+
/** The text, or the caption, as sent — Telegram HTML for the bot's messages. */
|
|
23
|
+
readonly text: string;
|
|
24
|
+
readonly media: string | undefined;
|
|
25
|
+
readonly rich: unknown;
|
|
26
|
+
/** Button labels, row by row. */
|
|
27
|
+
readonly buttons: readonly (readonly string[])[];
|
|
28
|
+
readonly markup: InlineKeyboardMarkup | undefined;
|
|
29
|
+
}
|
|
30
|
+
export interface TestChatCall {
|
|
31
|
+
readonly method: string;
|
|
32
|
+
readonly payload: Readonly<Record<string, unknown>>;
|
|
33
|
+
}
|
|
34
|
+
export interface TestChatOptions {
|
|
35
|
+
readonly chatId?: number;
|
|
36
|
+
readonly chatType?: 'private' | 'group' | 'supergroup';
|
|
37
|
+
readonly userId?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface ScreenTestChat<C extends Context = Context> {
|
|
40
|
+
/** What the chat shows, top to bottom. */
|
|
41
|
+
readonly messages: readonly TestChatMessage[];
|
|
42
|
+
/** Every Bot API call the bot made, in order. */
|
|
43
|
+
readonly calls: readonly TestChatCall[];
|
|
44
|
+
/** Press answers — the toasts — in order; `''` for a silent answer. */
|
|
45
|
+
readonly answers: readonly string[];
|
|
46
|
+
/** The user sends text; a leading `/word` is a command. */
|
|
47
|
+
send(text: string): Promise<void>;
|
|
48
|
+
/** The user sends a photo, with an optional caption. */
|
|
49
|
+
sendPhoto(fileId: string, caption?: string): Promise<void>;
|
|
50
|
+
/** The user presses the lowest button with this label. */
|
|
51
|
+
press(label: string): Promise<void>;
|
|
52
|
+
/** A press carrying arbitrary `callback_data` on a message — what a forged press looks like. */
|
|
53
|
+
pressData(messageId: number, data: string): Promise<void>;
|
|
54
|
+
/** Refuse the next call of `method` the way Telegram would. */
|
|
55
|
+
failNext(method: string, description: string, errorCode?: number): void;
|
|
56
|
+
/** The user deletes a message. */
|
|
57
|
+
deleteByUser(messageId: number): void;
|
|
58
|
+
/**
|
|
59
|
+
* The process restarted: the same chat, as Telegram keeps it, now answered by
|
|
60
|
+
* a new bot — built over the same storage, the way the application builds it.
|
|
61
|
+
*/
|
|
62
|
+
restart(bot: Bot<C>): void;
|
|
63
|
+
clearCalls(): void;
|
|
64
|
+
}
|
|
65
|
+
export declare function createScreenTestChat<C extends Context>(first: Bot<C>, options?: TestChatOptions): ScreenTestChat<C>;
|
|
66
|
+
//# sourceMappingURL=test-chat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-chat.d.ts","sourceRoot":"","sources":["../../../src/telegram/screens/test-chat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,GAAG,EAAY,OAAO,EAAE,MAAM,QAAQ,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAkC,MAAM,cAAc,CAAC;AAEzF,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAE9B,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,CAAC;CACnD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,YAAY,CAAC;IACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IACzD,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IACxC,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,2DAA2D;IAC3D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,wDAAwD;IACxD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,0DAA0D;IAC1D,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,gGAAgG;IAChG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxE,kCAAkC;IAClC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC;;;OAGG;IACH,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC3B,UAAU,IAAI,IAAI,CAAC;CACpB;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,OAAO,EACpD,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EACb,OAAO,GAAE,eAAoB,GAC5B,cAAc,CAAC,CAAC,CAAC,CAgLnB"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a screen shows: messages as plain objects.
|
|
3
|
+
*
|
|
4
|
+
* A view is data, so it can be compared. `{ text }` is a text message,
|
|
5
|
+
* `{ rich }` a Bot API rich message (Markdown, HTML or blocks, without file
|
|
6
|
+
* uploads) and `{ photo }` (or `video`, `animation`, `document`, `audio`) a
|
|
7
|
+
* media message with an optional caption; exactly one of those keys is
|
|
8
|
+
* present, and the type says so. A keyboard is rows of buttons where a row or a button may be `false`,
|
|
9
|
+
* `null` or `undefined` — a conditional row is written in the literal, not
|
|
10
|
+
* around it. Buttons are either screen buttons (`link`, `back`, an action) or
|
|
11
|
+
* any Telegram inline button that carries no `callback_data`, as Telegram
|
|
12
|
+
* spells it.
|
|
13
|
+
*/
|
|
14
|
+
import type { InlineKeyboardButton, InputRichMessageWithoutUpload } from 'grammy/types';
|
|
15
|
+
import type { ActionValue } from './callback-codec.js';
|
|
16
|
+
import type { TelegramText } from './html.js';
|
|
17
|
+
import type { AnyTelegramScreen, ScreenLinkArgs } from './screen-types.js';
|
|
18
|
+
export type ScreenMediaKind = 'photo' | 'video' | 'animation' | 'document' | 'audio';
|
|
19
|
+
export declare const SCREEN_MEDIA_KINDS: readonly ScreenMediaKind[];
|
|
20
|
+
/** A button label: its text, or its text with Telegram's style and custom-emoji icon. */
|
|
21
|
+
export type ButtonLabel = string | Omit<InlineKeyboardButton.CallbackButton, 'callback_data'>;
|
|
22
|
+
type ButtonTarget = {
|
|
23
|
+
readonly kind: 'link';
|
|
24
|
+
readonly screen: AnyTelegramScreen;
|
|
25
|
+
readonly params: Readonly<Record<string, ActionValue>>;
|
|
26
|
+
} | {
|
|
27
|
+
readonly kind: 'back';
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: 'action';
|
|
30
|
+
readonly screen: AnyTelegramScreen;
|
|
31
|
+
readonly params: Readonly<Record<string, ActionValue>>;
|
|
32
|
+
readonly action: string;
|
|
33
|
+
readonly input: Readonly<Record<string, ActionValue | undefined>> | undefined;
|
|
34
|
+
};
|
|
35
|
+
/** A button the screens runtime answers: it becomes `callback_data` when rendered. */
|
|
36
|
+
export declare class ScreenButton {
|
|
37
|
+
readonly label: ButtonLabel;
|
|
38
|
+
readonly target: ButtonTarget;
|
|
39
|
+
constructor(label: ButtonLabel, target: ButtonTarget);
|
|
40
|
+
}
|
|
41
|
+
/** Any inline button Telegram answers itself — a URL, a Mini App, copy text, … */
|
|
42
|
+
export type PlainKeyboardButton = Exclude<InlineKeyboardButton, InlineKeyboardButton.CallbackButton>;
|
|
43
|
+
export type KeyboardButton = ScreenButton | PlainKeyboardButton;
|
|
44
|
+
type Absent = false | null | undefined;
|
|
45
|
+
export type KeyboardRow = readonly (KeyboardButton | Absent)[];
|
|
46
|
+
export type Keyboard = readonly (KeyboardRow | Absent)[];
|
|
47
|
+
interface ViewMessageCommon {
|
|
48
|
+
/**
|
|
49
|
+
* The message's identity across renders: two renders put the same key in the
|
|
50
|
+
* same Telegram message when they can. The default is `'main'` for the last
|
|
51
|
+
* message and `'s0'`, `'s1'`, … for those above it, so the message that
|
|
52
|
+
* carries a screen's buttons is the one the next screen edits.
|
|
53
|
+
*/
|
|
54
|
+
readonly key?: string;
|
|
55
|
+
readonly keyboard?: Keyboard;
|
|
56
|
+
}
|
|
57
|
+
type ContentKey = 'text' | 'rich' | ScreenMediaKind;
|
|
58
|
+
type Without<TKeys extends string> = {
|
|
59
|
+
readonly [K in TKeys]?: never;
|
|
60
|
+
};
|
|
61
|
+
export interface TextViewMessage extends ViewMessageCommon, Without<Exclude<ContentKey, 'text'> | 'caption'> {
|
|
62
|
+
readonly text: TelegramText;
|
|
63
|
+
/** `false` hides the preview of the first link. Default: Telegram's. */
|
|
64
|
+
readonly linkPreview?: boolean;
|
|
65
|
+
}
|
|
66
|
+
export interface RichViewMessage extends ViewMessageCommon, Without<Exclude<ContentKey, 'rich'> | 'caption' | 'linkPreview'> {
|
|
67
|
+
/** A rich message as the Bot API takes it; files by `file_id` or URL only. */
|
|
68
|
+
readonly rich: InputRichMessageWithoutUpload;
|
|
69
|
+
}
|
|
70
|
+
type MediaViewMessageOf<TKind extends ScreenMediaKind> = ViewMessageCommon & Without<Exclude<ContentKey, TKind> | 'linkPreview'> & {
|
|
71
|
+
/** A Telegram `file_id` or an HTTP URL Telegram can fetch. */
|
|
72
|
+
readonly [K in TKind]: string;
|
|
73
|
+
} & {
|
|
74
|
+
readonly caption?: TelegramText;
|
|
75
|
+
};
|
|
76
|
+
export type PhotoViewMessage = MediaViewMessageOf<'photo'>;
|
|
77
|
+
export type VideoViewMessage = MediaViewMessageOf<'video'>;
|
|
78
|
+
export type AnimationViewMessage = MediaViewMessageOf<'animation'>;
|
|
79
|
+
export type DocumentViewMessage = MediaViewMessageOf<'document'>;
|
|
80
|
+
export type AudioViewMessage = MediaViewMessageOf<'audio'>;
|
|
81
|
+
export type ViewMessage = TextViewMessage | RichViewMessage | PhotoViewMessage | VideoViewMessage | AnimationViewMessage | DocumentViewMessage | AudioViewMessage;
|
|
82
|
+
/** What `view` returns: one message, or several shown top to bottom. */
|
|
83
|
+
export type ScreenViewResult = ViewMessage | readonly ViewMessage[];
|
|
84
|
+
/** A link to another screen; its params are required exactly when its path has any. */
|
|
85
|
+
export declare function link<TScreen extends AnyTelegramScreen>(label: ButtonLabel, screen: TScreen, ...params: ScreenLinkArgs<TScreen>): ScreenButton;
|
|
86
|
+
/** A link to the parent screen — the nearest declared screen above this path. */
|
|
87
|
+
export declare function back(label: ButtonLabel): ScreenButton;
|
|
88
|
+
export {};
|
|
89
|
+
//# sourceMappingURL=view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../src/telegram/screens/view.ts"],"names":[],"mappings":"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"}
|
|
@@ -63,6 +63,12 @@ export interface TelegramSendFailure {
|
|
|
63
63
|
*/
|
|
64
64
|
evidence: 'parameters' | 'status' | 'description' | 'none';
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Every reason, for a consumer that maps each one to its own code or text. A
|
|
68
|
+
* `Record<TelegramSendFailureReason, …>` beside it fails to compile when a
|
|
69
|
+
* release adds a reason, instead of falling through to a default at run time.
|
|
70
|
+
*/
|
|
71
|
+
export declare const TELEGRAM_SEND_FAILURE_REASONS: readonly TelegramSendFailureReason[];
|
|
66
72
|
/**
|
|
67
73
|
* Classify one refused send.
|
|
68
74
|
*
|
|
@@ -70,4 +76,23 @@ export interface TelegramSendFailure {
|
|
|
70
76
|
* which asserts nothing about the recipient and permits no retry.
|
|
71
77
|
*/
|
|
72
78
|
export declare function classifyTelegramSendFailure(error: unknown): TelegramSendFailure;
|
|
79
|
+
/**
|
|
80
|
+
* What a refused edit, deletion or press answer means for the chat — the
|
|
81
|
+
* questions a screen asks after the message exists, which a send never does.
|
|
82
|
+
*
|
|
83
|
+
* - `not-modified`: the edit asked for what is already there. Success.
|
|
84
|
+
* - `message-gone`: the message is not there — the user deleted it, or the id
|
|
85
|
+
* is not a message at all. A deletion of it has nothing left to do.
|
|
86
|
+
* - `message-locked`: the message is there and cannot be changed that way — too
|
|
87
|
+
* old to delete, not the bot's to edit, no text or caption to edit. An edit
|
|
88
|
+
* has to send it anew; a deletion leaves it in the chat.
|
|
89
|
+
* - `query-expired`: the press waited too long to be answered; nothing to do.
|
|
90
|
+
* - `other`: everything else, including a malformed payload — the caller's to
|
|
91
|
+
* raise.
|
|
92
|
+
*
|
|
93
|
+
* Internal to the screens: it reads Telegram's prose the same way
|
|
94
|
+
* {@link classifyTelegramSendFailure} does and is not a public vocabulary.
|
|
95
|
+
*/
|
|
96
|
+
export type TelegramEditRefusal = 'not-modified' | 'message-gone' | 'message-locked' | 'query-expired' | 'other';
|
|
97
|
+
export declare function classifyTelegramEditRefusal(error: unknown): TelegramEditRefusal;
|
|
73
98
|
//# sourceMappingURL=send-failure.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-failure.d.ts","sourceRoot":"","sources":["../../src/telegram/send-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH;;;;;;;;;GASG;AACH,MAAM,MAAM,yBAAyB,GACjC,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,yBAAyB,CAAC;IAClC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;;;OAQG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,EAAE,YAAY,GAAG,QAAQ,GAAG,aAAa,GAAG,MAAM,CAAC;CAC5D;
|
|
1
|
+
{"version":3,"file":"send-failure.d.ts","sourceRoot":"","sources":["../../src/telegram/send-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH;;;;;;;;;GASG;AACH,MAAM,MAAM,yBAAyB,GACjC,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,yBAAyB,CAAC;IAClC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;;;OAQG;IACH,oBAAoB,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,EAAE,YAAY,GAAG,QAAQ,GAAG,aAAa,GAAG,MAAM,CAAC;CAC5D;AA6CD;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,EAAE,SAAS,yBAAyB,EAC7B,CAAC;AAyClD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAyD/E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,eAAe,GACf,OAAO,CAAC;AAeZ,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAM/E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook updates received durably: recorded before Telegram is answered,
|
|
3
|
+
* handled after, and never lost to a restart or a slow handler.
|
|
4
|
+
*
|
|
5
|
+
* Answering Telegram only after an update is handled breaks twice. A long
|
|
6
|
+
* handler — a file of gigabytes arriving from a local Bot API server — holds
|
|
7
|
+
* the connection past Telegram's patience, it gives up and sends the update
|
|
8
|
+
* again while the first attempt still runs; and a process that dies after
|
|
9
|
+
* handling but before answering loses nothing Telegram knows about, while one
|
|
10
|
+
* that dies before handling loses the update for good once it has answered.
|
|
11
|
+
* So the body is written first and Telegram answered at once; handling runs
|
|
12
|
+
* here, outside the request.
|
|
13
|
+
*
|
|
14
|
+
* Updates of one chat run one after another, in `update_id` order; different
|
|
15
|
+
* chats run side by side up to `maxConcurrent`. An attempt holds a lease that
|
|
16
|
+
* is renewed while its handler lives; a sweep takes over what was left —
|
|
17
|
+
* `pending` whose process never got to it, `failed` whose retry is due,
|
|
18
|
+
* `processing` whose lease nobody renewed. Delivery is at least once: a
|
|
19
|
+
* handler that finished but could not be recorded as finished runs again.
|
|
20
|
+
*/
|
|
21
|
+
import type { TelegramUpdateStore } from './update-store.js';
|
|
22
|
+
import type { TelegramUpdateAcceptance } from './webhook.js';
|
|
23
|
+
/** An update as Telegram sends it; name grammY's `Update` to get its full type. */
|
|
24
|
+
export interface TelegramUpdateEnvelope {
|
|
25
|
+
readonly update_id: number;
|
|
26
|
+
}
|
|
27
|
+
export interface TelegramUpdateFailure {
|
|
28
|
+
readonly updateId: number;
|
|
29
|
+
readonly attempt: number;
|
|
30
|
+
readonly error: unknown;
|
|
31
|
+
/** `false` when the update was abandoned — no attempt follows. */
|
|
32
|
+
readonly retrying: boolean;
|
|
33
|
+
}
|
|
34
|
+
export type TelegramUpdateStoreStep = 'claim' | 'renew' | 'settle' | 'sweep';
|
|
35
|
+
export interface TelegramUpdateIntakeConfig<TUpdate extends TelegramUpdateEnvelope> {
|
|
36
|
+
readonly store: TelegramUpdateStore;
|
|
37
|
+
/**
|
|
38
|
+
* Handle one update — with grammY, `(update) => bot.handleUpdate(update)`.
|
|
39
|
+
* A grammY `BotError` is unwrapped: `retry`, `onFailure` and the stored
|
|
40
|
+
* error see what the middleware threw.
|
|
41
|
+
*/
|
|
42
|
+
readonly handle: (update: TUpdate) => unknown;
|
|
43
|
+
/**
|
|
44
|
+
* After a failed attempt: milliseconds until the next, or `false` to
|
|
45
|
+
* abandon the update. Default: abandon what Telegram said repeating cannot
|
|
46
|
+
* fix (a refused message, a blocked user), wait Telegram's `retry_after`
|
|
47
|
+
* when it gave one, otherwise back off 5 s, 20 s, 80 s … up to 15 minutes.
|
|
48
|
+
*/
|
|
49
|
+
readonly retry?: (error: unknown, attempt: number) => number | false;
|
|
50
|
+
/** Default 5. */
|
|
51
|
+
readonly maxAttempts?: number;
|
|
52
|
+
/** How long an attempt holds its update without renewing. Default 120 000; renewed every quarter. */
|
|
53
|
+
readonly leaseMs?: number;
|
|
54
|
+
/** How long a `pending` update waits for this process before a sweep takes it. Default 60 000. */
|
|
55
|
+
readonly pendingGraceMs?: number;
|
|
56
|
+
/** Default 30 000. */
|
|
57
|
+
readonly sweepEveryMs?: number;
|
|
58
|
+
/** How long finished updates are remembered to refuse Telegram's repeats. Default 24 h. */
|
|
59
|
+
readonly retainMs?: number;
|
|
60
|
+
/** Chats handled at once. Default 32. */
|
|
61
|
+
readonly maxConcurrent?: number;
|
|
62
|
+
/** Every failed attempt. */
|
|
63
|
+
readonly onFailure?: (failure: TelegramUpdateFailure) => void;
|
|
64
|
+
/** The store refused a step; the update is retried when its lease lapses. */
|
|
65
|
+
readonly onStoreError?: (error: unknown, step: TelegramUpdateStoreStep) => void;
|
|
66
|
+
/** Default `Date.now`. */
|
|
67
|
+
readonly now?: () => number;
|
|
68
|
+
}
|
|
69
|
+
export interface TelegramUpdateIntake {
|
|
70
|
+
/** Record one webhook body; handling starts after, once the intake is started. */
|
|
71
|
+
accept(body: string): Promise<TelegramUpdateAcceptance>;
|
|
72
|
+
/** Start handling, beginning with everything recorded before. */
|
|
73
|
+
start(): Promise<void>;
|
|
74
|
+
/** Take over what is due now; how many updates were scheduled. */
|
|
75
|
+
sweep(): Promise<number>;
|
|
76
|
+
/** Resolves once nothing is scheduled. */
|
|
77
|
+
idle(): Promise<void>;
|
|
78
|
+
/** Stop scheduling and wait for the handlers in flight. */
|
|
79
|
+
close(): Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
export declare function createTelegramUpdateIntake<TUpdate extends TelegramUpdateEnvelope>(config: TelegramUpdateIntakeConfig<TUpdate>): TelegramUpdateIntake;
|
|
82
|
+
//# sourceMappingURL=update-intake.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-intake.d.ts","sourceRoot":"","sources":["../../src/telegram/update-intake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAE1D,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE7E,MAAM,WAAW,0BAA0B,CAAC,OAAO,SAAS,sBAAsB;IAChF,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAC9C;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,KAAK,CAAC;IACrE,iBAAiB;IACjB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,qGAAqG;IACrG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kGAAkG;IAClG,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,sBAAsB;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,4BAA4B;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC9D,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAChF,0BAA0B;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,kFAAkF;IAClF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACxD,iEAAiE;IACjE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,kEAAkE;IAClE,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,0CAA0C;IAC1C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,2DAA2D;IAC3D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAsED,wBAAgB,0BAA0B,CAAC,OAAO,SAAS,sBAAsB,EAC/E,MAAM,EAAE,0BAA0B,CAAC,OAAO,CAAC,GAC1C,oBAAoB,CAiJtB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rules of `TelegramUpdateStore`, as a check any store can be put
|
|
3
|
+
* through — the ones shipped here and an application's own over its ORM.
|
|
4
|
+
*
|
|
5
|
+
* A store is five conditional statements, and each is easy to write almost
|
|
6
|
+
* right: a claim that does not check the lease, a settle that does not check
|
|
7
|
+
* the attempt, a claim read and then written in two statements so that two
|
|
8
|
+
* processes both take one update. Nothing fails in a test with one process;
|
|
9
|
+
* the update simply runs twice in production. So the check runs every rule on
|
|
10
|
+
* a fresh store, and races claims against each other.
|
|
11
|
+
*
|
|
12
|
+
* It does not depend on a test framework: it returns what was violated, and
|
|
13
|
+
* an empty list is a store that keeps the rules.
|
|
14
|
+
*/
|
|
15
|
+
import type { TelegramUpdateStore } from './update-store.js';
|
|
16
|
+
/**
|
|
17
|
+
* Put a store through every rule, each on a fresh store from `make`. Resolves
|
|
18
|
+
* with the violations — `"<rule>: <what happened>"` — empty when the store
|
|
19
|
+
* keeps them all. A store that throws violates the rule it threw in.
|
|
20
|
+
*/
|
|
21
|
+
export declare function checkTelegramUpdateStore(make: () => TelegramUpdateStore | Promise<TelegramUpdateStore>): Promise<string[]>;
|
|
22
|
+
//# sourceMappingURL=update-store-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-store-check.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAqG1D;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,GAC7D,OAAO,CAAC,MAAM,EAAE,CAAC,CAYnB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The update store in Postgres, through whatever client the application
|
|
3
|
+
* already holds — one function that runs a statement with `$n` parameters and
|
|
4
|
+
* returns its rows:
|
|
5
|
+
*
|
|
6
|
+
* - Bun: `(text, parameters) => sql.unsafe(text, [...parameters])`
|
|
7
|
+
* - Prisma: `(text, parameters) => prisma.$queryRawUnsafe(text, ...parameters)`
|
|
8
|
+
* - `pg`: `(text, parameters) => pool.query(text, [...parameters]).then((result) => result.rows)`
|
|
9
|
+
*
|
|
10
|
+
* Each rule of `TelegramUpdateStore` is one conditional statement, so a claim
|
|
11
|
+
* is atomic across every process sharing the table; every statement returns
|
|
12
|
+
* rows, so a client that has only a "query" call needs nothing else.
|
|
13
|
+
* Parameters carry explicit casts: a driver that sends a JS number as a
|
|
14
|
+
* double still lands in a `bigint` column.
|
|
15
|
+
*/
|
|
16
|
+
import type { TelegramUpdateStore } from './update-store.js';
|
|
17
|
+
/** Run one statement with `$1…$n` parameters; resolve with the rows it returned. */
|
|
18
|
+
export type TelegramPostgresQuery = (text: string, parameters: readonly (string | number | null)[]) => Promise<readonly unknown[]>;
|
|
19
|
+
export interface PostgresTelegramUpdateStoreConfig {
|
|
20
|
+
readonly query: TelegramPostgresQuery;
|
|
21
|
+
/** Default `telegram_updates`. */
|
|
22
|
+
readonly table?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Create the table on first use when it is missing. Default `true`; set
|
|
25
|
+
* `false` when migrations own the schema — `postgresTelegramUpdateStoreSchema`
|
|
26
|
+
* is the statement to put in one.
|
|
27
|
+
*/
|
|
28
|
+
readonly createTable?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** The table and its index, for a migration. Safe to run twice. */
|
|
31
|
+
export declare function postgresTelegramUpdateStoreSchema(table?: string): string;
|
|
32
|
+
export declare function postgresTelegramUpdateStore(config: PostgresTelegramUpdateStoreConfig): TelegramUpdateStore;
|
|
33
|
+
//# sourceMappingURL=update-store-postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-store-postgres.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAA4B,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEpF,oFAAoF;AACpF,MAAM,MAAM,qBAAqB,GAAG,CAClC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,KAC5C,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;AAEjC,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,kCAAkC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAaD,mEAAmE;AACnE,wBAAgB,iCAAiC,CAAC,KAAK,SAAqB,GAAG,MAAM,CAapF;AAqBD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,iCAAiC,GACxC,mBAAmB,CAyGrB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The update store in SQLite, through the database handle the application
|
|
3
|
+
* already opened — `bun:sqlite`'s `Database` or `node:sqlite`'s `DatabaseSync`
|
|
4
|
+
* as it is. The module imports neither, so it runs wherever its caller does.
|
|
5
|
+
*
|
|
6
|
+
* Each rule of `TelegramUpdateStore` is one conditional statement, so a claim
|
|
7
|
+
* is atomic across processes sharing the file.
|
|
8
|
+
*/
|
|
9
|
+
import type { TelegramUpdateStore } from './update-store.js';
|
|
10
|
+
/** What this store needs of a SQLite handle; both runtimes' handles have it. */
|
|
11
|
+
export interface TelegramSqliteDatabase {
|
|
12
|
+
exec(sql: string): unknown;
|
|
13
|
+
prepare(sql: string): {
|
|
14
|
+
run(...parameters: (string | number | null)[]): {
|
|
15
|
+
readonly changes: number | bigint;
|
|
16
|
+
};
|
|
17
|
+
all(...parameters: (string | number | null)[]): unknown[];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export interface SqliteTelegramUpdateStoreConfig {
|
|
21
|
+
readonly database: TelegramSqliteDatabase;
|
|
22
|
+
/** Default `telegram_updates`. Created when missing. */
|
|
23
|
+
readonly table?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function sqliteTelegramUpdateStore(config: SqliteTelegramUpdateStoreConfig): TelegramUpdateStore;
|
|
26
|
+
//# sourceMappingURL=update-store-sqlite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-store-sqlite.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store-sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAGV,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAExB,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QACpB,GAAG,CAAC,GAAG,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG;YAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QACtF,GAAG,CAAC,GAAG,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;KAC3D,CAAC;CACH;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAiBD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,+BAA+B,GACtC,mBAAmB,CAkFrB"}
|