@prajwalshete/telegram-plugin 0.0.0-stage → 0.1.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.
Files changed (295) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +21 -0
  3. package/README.md +109 -2
  4. package/dist/adapters/clock.d.ts +3 -0
  5. package/dist/adapters/clock.js +4 -0
  6. package/dist/adapters/console-log.d.ts +13 -0
  7. package/dist/adapters/console-log.js +41 -0
  8. package/dist/adapters/d1-db.d.ts +5 -0
  9. package/dist/adapters/d1-db.js +37 -0
  10. package/dist/adapters/ids.d.ts +5 -0
  11. package/dist/adapters/ids.js +25 -0
  12. package/dist/adapters/index.d.ts +7 -0
  13. package/dist/adapters/keyring.d.ts +50 -0
  14. package/dist/adapters/keyring.js +174 -0
  15. package/dist/adapters/metrics.d.ts +28 -0
  16. package/dist/adapters/metrics.js +48 -0
  17. package/dist/adapters/r2-files.d.ts +20 -0
  18. package/dist/adapters/r2-files.js +53 -0
  19. package/dist/bin/cli.d.ts +1 -0
  20. package/dist/bin/cli.js +45 -0
  21. package/dist/client/broadcasts.d.ts +35 -0
  22. package/dist/client/commands.d.ts +23 -0
  23. package/dist/client/endpoint.d.ts +34 -0
  24. package/dist/client/events.d.ts +33 -0
  25. package/dist/client/index.d.ts +11 -0
  26. package/dist/client/index.js +3 -0
  27. package/dist/client/schedules.d.ts +42 -0
  28. package/dist/client/schema.d.ts +181 -0
  29. package/dist/client/send.d.ts +15 -0
  30. package/dist/client/templates.d.ts +47 -0
  31. package/dist/contracts/bots.d.ts +129 -0
  32. package/dist/contracts/bots.js +70 -0
  33. package/dist/contracts/broadcasts.d.ts +116 -0
  34. package/dist/contracts/broadcasts.js +63 -0
  35. package/dist/contracts/chats.d.ts +97 -0
  36. package/dist/contracts/chats.js +66 -0
  37. package/dist/contracts/commands.d.ts +52 -0
  38. package/dist/contracts/commands.js +53 -0
  39. package/dist/contracts/common.d.ts +38 -0
  40. package/dist/contracts/common.js +41 -0
  41. package/dist/contracts/deliveries.d.ts +103 -0
  42. package/dist/contracts/deliveries.js +74 -0
  43. package/dist/contracts/destinations.d.ts +24 -0
  44. package/dist/contracts/destinations.js +28 -0
  45. package/dist/contracts/events.d.ts +165 -0
  46. package/dist/contracts/events.js +98 -0
  47. package/dist/contracts/features.d.ts +20 -0
  48. package/dist/contracts/features.js +21 -0
  49. package/dist/contracts/health.d.ts +55 -0
  50. package/dist/contracts/health.js +58 -0
  51. package/dist/contracts/index.d.ts +22 -0
  52. package/dist/contracts/index.js +22 -0
  53. package/dist/contracts/labels.d.ts +38 -0
  54. package/dist/contracts/labels.js +52 -0
  55. package/dist/contracts/links.d.ts +51 -0
  56. package/dist/contracts/links.js +50 -0
  57. package/dist/contracts/meta.d.ts +132 -0
  58. package/dist/contracts/meta.js +85 -0
  59. package/dist/contracts/permissions.d.ts +24 -0
  60. package/dist/contracts/permissions.js +27 -0
  61. package/dist/contracts/problems.d.ts +106 -0
  62. package/dist/contracts/problems.js +101 -0
  63. package/dist/contracts/recipients.d.ts +36 -0
  64. package/dist/contracts/recipients.js +22 -0
  65. package/dist/contracts/schedules.d.ts +276 -0
  66. package/dist/contracts/schedules.js +143 -0
  67. package/dist/contracts/send.d.ts +126 -0
  68. package/dist/contracts/send.js +69 -0
  69. package/dist/contracts/subscriptions.d.ts +38 -0
  70. package/dist/contracts/subscriptions.js +31 -0
  71. package/dist/contracts/templates.d.ts +358 -0
  72. package/dist/contracts/templates.js +174 -0
  73. package/dist/contracts/users.d.ts +15 -0
  74. package/dist/contracts/users.js +19 -0
  75. package/dist/core/guards.d.ts +22 -0
  76. package/dist/core/guards.js +47 -0
  77. package/dist/core/html/escape.d.ts +2 -0
  78. package/dist/core/html/escape.js +36 -0
  79. package/dist/core/html/tokenize.js +65 -0
  80. package/dist/core/index.d.ts +18 -0
  81. package/dist/core/pacing.d.ts +1 -0
  82. package/dist/core/pacing.js +86 -0
  83. package/dist/core/random.d.ts +2 -0
  84. package/dist/core/random.js +38 -0
  85. package/dist/core/recipients.d.ts +1 -0
  86. package/dist/core/recipients.js +86 -0
  87. package/dist/core/schedule/describe.d.ts +3 -0
  88. package/dist/core/schedule/describe.js +74 -0
  89. package/dist/core/schedule/index.d.ts +4 -0
  90. package/dist/core/schedule/policy.d.ts +1 -0
  91. package/dist/core/schedule/policy.js +37 -0
  92. package/dist/core/schedule/rule.d.ts +25 -0
  93. package/dist/core/schedule/rule.js +142 -0
  94. package/dist/core/schedule/tz.d.ts +2 -0
  95. package/dist/core/schedule/tz.js +59 -0
  96. package/dist/core/split/plain.d.ts +2 -0
  97. package/dist/core/split/plain.js +23 -0
  98. package/dist/core/split/split.d.ts +15 -0
  99. package/dist/core/split/split.js +158 -0
  100. package/dist/core/template/ast.d.ts +58 -0
  101. package/dist/core/template/ast.js +5 -0
  102. package/dist/core/template/expr.js +108 -0
  103. package/dist/core/template/filters.d.ts +20 -0
  104. package/dist/core/template/filters.js +127 -0
  105. package/dist/core/template/format.d.ts +17 -0
  106. package/dist/core/template/format.js +88 -0
  107. package/dist/core/template/index.d.ts +20 -0
  108. package/dist/core/template/index.js +29 -0
  109. package/dist/core/template/parse.d.ts +8 -0
  110. package/dist/core/template/parse.js +369 -0
  111. package/dist/core/template/render.d.ts +17 -0
  112. package/dist/core/template/render.js +118 -0
  113. package/dist/core/template/shape.d.ts +28 -0
  114. package/dist/core/template/shape.js +151 -0
  115. package/dist/core/template/tags.js +105 -0
  116. package/dist/core/template/validate.d.ts +10 -0
  117. package/dist/core/template/validate.js +144 -0
  118. package/dist/core/template/variables.d.ts +15 -0
  119. package/dist/core/template/variables.js +41 -0
  120. package/dist/do/index.d.ts +3 -0
  121. package/dist/do/index.js +3 -0
  122. package/dist/do/telegram-bot-do.d.ts +45 -0
  123. package/dist/do/telegram-bot-do.js +219 -0
  124. package/dist/engine/actions/callback.js +256 -0
  125. package/dist/engine/actions/tokens.d.ts +2 -0
  126. package/dist/engine/actions/tokens.js +51 -0
  127. package/dist/engine/bot-api.js +20 -0
  128. package/dist/engine/bot-features.js +39 -0
  129. package/dist/engine/chat-state.js +66 -0
  130. package/dist/engine/content/audience.js +54 -0
  131. package/dist/engine/content/buttons.d.ts +1 -0
  132. package/dist/engine/content/buttons.js +113 -0
  133. package/dist/engine/content/fanout.js +284 -0
  134. package/dist/engine/content/intersect.d.ts +6 -0
  135. package/dist/engine/content/intersect.js +26 -0
  136. package/dist/engine/content/providers.d.ts +6 -0
  137. package/dist/engine/content/providers.js +142 -0
  138. package/dist/engine/content/recipients.js +128 -0
  139. package/dist/engine/content/render.js +138 -0
  140. package/dist/engine/content/reply.js +156 -0
  141. package/dist/engine/content/report-files.js +78 -0
  142. package/dist/engine/content/template-rows.js +74 -0
  143. package/dist/engine/dialogs/dialog.d.ts +2 -0
  144. package/dist/engine/dialogs/dialog.js +375 -0
  145. package/dist/engine/dispatch/builtins.js +198 -0
  146. package/dist/engine/dispatch/commands.js +194 -0
  147. package/dist/engine/dispatch/context.d.ts +2 -0
  148. package/dist/engine/dispatch/context.js +194 -0
  149. package/dist/engine/dispatch/dispatcher.js +66 -0
  150. package/dist/engine/dispatch/handlers.d.ts +1 -0
  151. package/dist/engine/dispatch/handlers.js +103 -0
  152. package/dist/engine/dispatch/lifecycle.js +85 -0
  153. package/dist/engine/dispatch/link-chat.js +31 -0
  154. package/dist/engine/dispatch/linking.js +273 -0
  155. package/dist/engine/errors.d.ts +60 -0
  156. package/dist/engine/errors.js +72 -0
  157. package/dist/engine/events/filters.d.ts +5 -0
  158. package/dist/engine/events/filters.js +82 -0
  159. package/dist/engine/events/kick.js +30 -0
  160. package/dist/engine/events/payload.d.ts +3 -0
  161. package/dist/engine/events/payload.js +16 -0
  162. package/dist/engine/events/tick.js +208 -0
  163. package/dist/engine/i18n.d.ts +12 -0
  164. package/dist/engine/i18n.js +55 -0
  165. package/dist/engine/inbound/files.js +121 -0
  166. package/dist/engine/inbound/handler.js +46 -0
  167. package/dist/engine/jobs.d.ts +32 -0
  168. package/dist/engine/jobs.js +85 -0
  169. package/dist/engine/link-codes.js +46 -0
  170. package/dist/engine/metrics.js +12 -0
  171. package/dist/engine/options.d.ts +369 -0
  172. package/dist/engine/options.js +81 -0
  173. package/dist/engine/outbox/dead-letter.js +78 -0
  174. package/dist/engine/outbox/do-registry.d.ts +10 -0
  175. package/dist/engine/outbox/do-registry.js +10 -0
  176. package/dist/engine/outbox/do-transport.d.ts +33 -0
  177. package/dist/engine/outbox/do-transport.js +73 -0
  178. package/dist/engine/outbox/drain.d.ts +13 -0
  179. package/dist/engine/outbox/drain.js +381 -0
  180. package/dist/engine/outbox/enqueue.d.ts +1 -0
  181. package/dist/engine/outbox/enqueue.js +63 -0
  182. package/dist/engine/outbox/select.js +24 -0
  183. package/dist/engine/outbox/send.js +362 -0
  184. package/dist/engine/outbox/transport.d.ts +35 -0
  185. package/dist/engine/outbox/transport.js +175 -0
  186. package/dist/engine/outbox/wake.js +28 -0
  187. package/dist/engine/registry.d.ts +502 -0
  188. package/dist/engine/registry.js +258 -0
  189. package/dist/engine/rows.d.ts +25 -0
  190. package/dist/engine/rows.js +131 -0
  191. package/dist/engine/runtime.d.ts +95 -0
  192. package/dist/engine/runtime.js +149 -0
  193. package/dist/engine/schedules/fire.js +208 -0
  194. package/dist/engine/schedules/resume.js +75 -0
  195. package/dist/engine/schedules/rows.js +69 -0
  196. package/dist/engine/schedules/tick.js +30 -0
  197. package/dist/engine/schedules/wake.js +50 -0
  198. package/dist/engine/state.d.ts +11 -0
  199. package/dist/engine/state.js +78 -0
  200. package/dist/locales/en.js +339 -0
  201. package/dist/locales/en.json +341 -0
  202. package/dist/locales/mr.js +339 -0
  203. package/dist/locales/mr.json +341 -0
  204. package/dist/ports/db.d.ts +22 -0
  205. package/dist/ports/index.d.ts +4 -0
  206. package/dist/ports/ports.d.ts +257 -0
  207. package/dist/ports/sql.d.ts +39 -0
  208. package/dist/ports/sql.js +76 -0
  209. package/dist/renderers/csv.d.ts +14 -0
  210. package/dist/renderers/csv.js +51 -0
  211. package/dist/renderers/html.d.ts +44 -0
  212. package/dist/renderers/html.js +81 -0
  213. package/dist/renderers/index.d.ts +5 -0
  214. package/dist/renderers/index.js +4 -0
  215. package/dist/renderers/table.d.ts +30 -0
  216. package/dist/renderers/table.js +114 -0
  217. package/dist/routes/admin.js +146 -0
  218. package/dist/routes/app.d.ts +3111 -0
  219. package/dist/routes/app.js +9 -0
  220. package/dist/routes/broadcasts.js +37 -0
  221. package/dist/routes/commands.js +35 -0
  222. package/dist/routes/content.js +12 -0
  223. package/dist/routes/events.js +36 -0
  224. package/dist/routes/http.d.ts +30 -0
  225. package/dist/routes/http.js +223 -0
  226. package/dist/routes/me.js +59 -0
  227. package/dist/routes/schedules.js +62 -0
  228. package/dist/routes/send.js +24 -0
  229. package/dist/routes/templates.js +70 -0
  230. package/dist/routes/webhook.js +147 -0
  231. package/dist/schema/columns.js +307 -0
  232. package/dist/schema/ddl.d.ts +7 -0
  233. package/dist/schema/ddl.js +45 -0
  234. package/dist/schema/index.d.ts +4255 -0
  235. package/dist/schema/index.js +44 -0
  236. package/dist/schema/reference.sql +359 -0
  237. package/dist/schema/tables.d.ts +4243 -0
  238. package/dist/schema/tables.js +327 -0
  239. package/dist/schema/version.d.ts +5 -0
  240. package/dist/schema/version.js +7 -0
  241. package/dist/server/index.d.ts +55 -0
  242. package/dist/server/index.js +46 -0
  243. package/dist/server/plugin.d.ts +81 -0
  244. package/dist/server/plugin.js +247 -0
  245. package/dist/server/version.d.ts +2 -0
  246. package/dist/server/version.js +4 -0
  247. package/dist/services/ai-tool.d.ts +26 -0
  248. package/dist/services/ai-tool.js +148 -0
  249. package/dist/services/bots.js +300 -0
  250. package/dist/services/broadcasts.js +390 -0
  251. package/dist/services/chats.js +83 -0
  252. package/dist/services/cleanup.js +83 -0
  253. package/dist/services/commands.js +140 -0
  254. package/dist/services/deliveries.js +53 -0
  255. package/dist/services/destinations.js +91 -0
  256. package/dist/services/event-rules.js +229 -0
  257. package/dist/services/events.js +20 -0
  258. package/dist/services/idempotency.js +64 -0
  259. package/dist/services/links.js +200 -0
  260. package/dist/services/menu.js +95 -0
  261. package/dist/services/ops.js +90 -0
  262. package/dist/services/rekey.js +63 -0
  263. package/dist/services/reports.js +87 -0
  264. package/dist/services/schedules.js +214 -0
  265. package/dist/services/send.d.ts +14 -0
  266. package/dist/services/send.js +421 -0
  267. package/dist/services/subscriptions.js +28 -0
  268. package/dist/services/templates.js +259 -0
  269. package/dist/telegram/classify.d.ts +30 -0
  270. package/dist/telegram/classify.js +164 -0
  271. package/dist/telegram/client.d.ts +73 -0
  272. package/dist/telegram/client.js +135 -0
  273. package/dist/telegram/errors.d.ts +37 -0
  274. package/dist/telegram/errors.js +62 -0
  275. package/dist/telegram/inbound.d.ts +40 -0
  276. package/dist/telegram/inbound.js +72 -0
  277. package/dist/telegram/index.d.ts +8 -0
  278. package/dist/telegram/multipart.d.ts +5 -0
  279. package/dist/telegram/multipart.js +47 -0
  280. package/dist/telegram/types.d.ts +271 -0
  281. package/dist/telegram/update.d.ts +833 -0
  282. package/dist/telegram/update.js +129 -0
  283. package/dist/testing/fake-html.d.ts +9 -0
  284. package/dist/testing/fake-html.js +83 -0
  285. package/dist/testing/fake-telegram.d.ts +206 -0
  286. package/dist/testing/fake-telegram.js +856 -0
  287. package/dist/testing/index.d.ts +7 -0
  288. package/dist/testing/index.js +7 -0
  289. package/dist/testing/reference-sql.d.ts +4 -0
  290. package/dist/testing/reference-sql.js +8 -0
  291. package/dist/testing/test-plugin.d.ts +126 -0
  292. package/dist/testing/test-plugin.js +182 -0
  293. package/dist/testing/updates.d.ts +100 -0
  294. package/dist/testing/updates.js +212 -0
  295. package/package.json +113 -3
@@ -0,0 +1,108 @@
1
+ // src/core/template/expr.ts
2
+ const PATH_RE = /\.(?![A-Za-z0-9_])|[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z0-9_]+)*/y;
3
+ const NAME_RE = /[A-Za-z][A-Za-z0-9_]*/y;
4
+ const NUMBER_RE = /-?\d+(?:\.\d+)?(?![A-Za-z0-9_.])/y;
5
+ const BARE_RE = /[A-Za-z0-9_.+-]+/y;
6
+ function sticky(re, src, at) {
7
+ re.lastIndex = at;
8
+ const m = re.exec(src);
9
+ return m ? m[0] : null;
10
+ }
11
+ /** Parses a path (`a.b.c` or `.`). */
12
+ function parsePath(src) {
13
+ const s = src.trim();
14
+ const m = sticky(PATH_RE, s, 0);
15
+ if (m === null || m.length !== s.length) return null;
16
+ return m === "." ? [] : m.split(".");
17
+ }
18
+ function parseExpr(src) {
19
+ let i = 0;
20
+ const ws = () => {
21
+ while (i < src.length && /\s/.test(src[i] ?? "")) i++;
22
+ };
23
+ ws();
24
+ const p = sticky(PATH_RE, src, i);
25
+ if (p === null) return {
26
+ offset: i,
27
+ message: "expected a variable name"
28
+ };
29
+ const path = p === "." ? [] : p.split(".");
30
+ i += p.length;
31
+ const filters = [];
32
+ const filterOffsets = [];
33
+ ws();
34
+ while (i < src.length) {
35
+ if (src[i] !== "|") return {
36
+ offset: i,
37
+ message: `unexpected "${src[i]}"; use | to add a filter`
38
+ };
39
+ i++;
40
+ ws();
41
+ const name = sticky(NAME_RE, src, i);
42
+ if (name === null) return {
43
+ offset: i,
44
+ message: "expected a filter name after |"
45
+ };
46
+ filterOffsets.push(i);
47
+ i += name.length;
48
+ ws();
49
+ const args = [];
50
+ if (src[i] === ":") {
51
+ i++;
52
+ for (;;) {
53
+ ws();
54
+ const arg = parseArg(src, i);
55
+ if ("message" in arg) return arg;
56
+ args.push(arg.value);
57
+ i = arg.end;
58
+ ws();
59
+ if (src[i] !== ",") break;
60
+ i++;
61
+ }
62
+ }
63
+ filters.push({
64
+ name,
65
+ args
66
+ });
67
+ }
68
+ return {
69
+ path,
70
+ filters,
71
+ filterOffsets
72
+ };
73
+ }
74
+ function parseArg(src, i) {
75
+ const q = src[i];
76
+ if (q === "\"" || q === "'") {
77
+ let out = "";
78
+ let j = i + 1;
79
+ while (j < src.length && src[j] !== q) {
80
+ if (src[j] === "\\" && j + 1 < src.length) j++;
81
+ out += src[j];
82
+ j++;
83
+ }
84
+ if (j >= src.length) return {
85
+ offset: i,
86
+ message: "unterminated string"
87
+ };
88
+ return {
89
+ value: out,
90
+ end: j + 1
91
+ };
92
+ }
93
+ const num = sticky(NUMBER_RE, src, i);
94
+ if (num !== null) return {
95
+ value: Number(num),
96
+ end: i + num.length
97
+ };
98
+ const bare = sticky(BARE_RE, src, i);
99
+ if (bare !== null) return {
100
+ value: bare === "true" ? true : bare === "false" ? false : bare,
101
+ end: i + bare.length
102
+ };
103
+ return {
104
+ offset: i,
105
+ message: "expected a filter argument"
106
+ };
107
+ }
108
+ export { parseExpr, parsePath };
@@ -0,0 +1,20 @@
1
+ import { FilterArg } from "./ast.js";
2
+ import { FormatContext } from "./format.js";
3
+ /** Formatting context a filter receives (locale, IANA zone, currency…). */
4
+ export type FilterContext = FormatContext;
5
+ /** A filter: raw value + literal args → raw value. The result is stringified and escaped by the renderer. */
6
+ export type FilterFn = (value: unknown, args: readonly FilterArg[], ctx: FilterContext) => unknown;
7
+ /** A filter with its allowed argument count. */
8
+ export interface FilterDef {
9
+ /** Maps the value; must not return markup. */
10
+ apply: FilterFn;
11
+ /** Allowed argument count (checked by `validateTemplate`). Defaults: 0…∞. */
12
+ minArgs?: number;
13
+ maxArgs?: number;
14
+ }
15
+ /** Host filters, by name. A plain function accepts any number of arguments. */
16
+ export type FilterRegistry = Readonly<Record<string, FilterDef | FilterFn>>;
17
+ /** Identity helper that types a {@link FilterDef} for `options.filters`. */
18
+ export declare function defineFilter(def: FilterDef): FilterDef;
19
+ /** Built-in template filters (`money`, `number`, `date`, `truncate`, `yesno`…), by name. Host filters with the same name win. */
20
+ export declare const BUILTIN_FILTERS: Readonly<Record<string, FilterDef>>;
@@ -0,0 +1,127 @@
1
+ import { formatDateTime, formatMoney, formatNumber, formatPercent, intlLocale, isDateStyle, toEpoch, toNumber } from "./format.js";
2
+ // src/core/template/filters.ts
3
+ /** Identity helper that types a {@link FilterDef} for `options.filters`. */
4
+ function defineFilter(def) {
5
+ return def;
6
+ }
7
+ let segmenter;
8
+ function graphemes(s) {
9
+ segmenter ??= new Intl.Segmenter(void 0, { granularity: "grapheme" });
10
+ return Array.from(segmenter.segment(s), (x) => x.segment);
11
+ }
12
+ const str = (v) => v === null || v === void 0 ? "" : String(v);
13
+ const intArg = (a) => typeof a === "number" && Number.isInteger(a) && a >= 0 && a <= 20 ? a : void 0;
14
+ const YES_NO = {
15
+ en: ["Yes", "No"],
16
+ mr: ["होय", "नाही"]
17
+ };
18
+ function dateFilter(defaults) {
19
+ return {
20
+ maxArgs: 1,
21
+ apply: (v, args, ctx) => {
22
+ const t = toEpoch(v);
23
+ if (t === null) return "";
24
+ const style = args[0];
25
+ const date = isDateStyle(style) ? style : "medium";
26
+ const time = isDateStyle(style) ? style : "short";
27
+ return formatDateTime(t, ctx, {
28
+ ...defaults.date ? { dateStyle: date } : {},
29
+ ...defaults.time ? { timeStyle: time } : {}
30
+ });
31
+ }
32
+ };
33
+ }
34
+ /** Built-in template filters (`money`, `number`, `date`, `truncate`, `yesno`…), by name. Host filters with the same name win. */
35
+ const BUILTIN_FILTERS = {
36
+ /** Minor units → currency (`money` or `money:"USD"`). */
37
+ money: {
38
+ maxArgs: 1,
39
+ apply: (v, args, ctx) => {
40
+ const n = toNumber(v);
41
+ const code = typeof args[0] === "string" ? args[0].toUpperCase() : void 0;
42
+ return n === null ? "" : formatMoney(n, ctx, code);
43
+ }
44
+ },
45
+ /** `number` (≤ 2 decimals) or `number:0` (fixed decimals). */
46
+ number: {
47
+ maxArgs: 1,
48
+ apply: (v, args, ctx) => {
49
+ const n = toNumber(v);
50
+ return n === null ? "" : formatNumber(n, ctx, intArg(args[0]));
51
+ }
52
+ },
53
+ /** Ratio → percent (`0.456 | percent` → 45.6%). */
54
+ percent: {
55
+ maxArgs: 1,
56
+ apply: (v, args, ctx) => {
57
+ const n = toNumber(v);
58
+ return n === null ? "" : formatPercent(n, ctx, intArg(args[0]));
59
+ }
60
+ },
61
+ /** Epoch ms → date in the context zone (`date`, `date:"long"`). */
62
+ date: dateFilter({ date: true }),
63
+ time: dateFilter({ time: true }),
64
+ datetime: dateFilter({
65
+ date: true,
66
+ time: true
67
+ }),
68
+ upper: {
69
+ maxArgs: 0,
70
+ apply: (v, _a, ctx) => str(v).toLocaleUpperCase(intlLocale(ctx).split("-u-")[0])
71
+ },
72
+ lower: {
73
+ maxArgs: 0,
74
+ apply: (v, _a, ctx) => str(v).toLocaleLowerCase(intlLocale(ctx).split("-u-")[0])
75
+ },
76
+ /** At most n user-visible characters (grapheme clusters, so Devanagari and emoji stay whole), then `…`. */
77
+ truncate: {
78
+ minArgs: 1,
79
+ maxArgs: 2,
80
+ apply: (v, args) => {
81
+ const n = typeof args[0] === "number" && args[0] >= 1 ? Math.floor(args[0]) : 0;
82
+ const tail = typeof args[1] === "string" ? args[1] : "…";
83
+ const g = graphemes(str(v));
84
+ return n > 0 && g.length > n ? g.slice(0, n).join("").trimEnd() + tail : g.join("");
85
+ }
86
+ },
87
+ default: {
88
+ minArgs: 1,
89
+ maxArgs: 1,
90
+ apply: (v, args) => v === null || v === void 0 || v === "" || Array.isArray(v) && v.length === 0 ? args[0] ?? "" : v
91
+ },
92
+ join: {
93
+ maxArgs: 1,
94
+ apply: (v, args) => Array.isArray(v) ? v.map(str).join(typeof args[0] === "string" ? args[0] : ", ") : v
95
+ },
96
+ count: {
97
+ maxArgs: 0,
98
+ apply: (v) => Array.isArray(v) ? v.length : v && typeof v === "object" ? Object.keys(v).length : typeof v === "string" ? graphemes(v).length : 0
99
+ },
100
+ /** Boolean → Yes/No (localized), or `yesno:"Open","Closed"`. */
101
+ yesno: {
102
+ maxArgs: 2,
103
+ apply: (v, args, ctx) => {
104
+ const [yes, no] = YES_NO[ctx.locale.split("-")[0] ?? ""] ?? YES_NO.en ?? ["Yes", "No"];
105
+ return v ? str(args[0] ?? yes) : str(args[1] ?? no);
106
+ }
107
+ },
108
+ /** Percent-encodes a value for use inside a URL (`href="https://x/{{key | url}}"`). */
109
+ url: {
110
+ maxArgs: 0,
111
+ apply: (v) => encodeURIComponent(str(v))
112
+ },
113
+ /** Epoch ms → Unix seconds (for `<tg-time unix="…">`). */
114
+ unix: {
115
+ maxArgs: 0,
116
+ apply: (v) => {
117
+ const t = toEpoch(v);
118
+ return t === null ? "" : Math.floor(t / 1e3);
119
+ }
120
+ }
121
+ };
122
+ function resolveFilter(name, registry) {
123
+ const f = (registry && Object.hasOwn(registry, name) ? registry[name] : void 0) ?? (Object.hasOwn(BUILTIN_FILTERS, name) ? BUILTIN_FILTERS[name] : void 0);
124
+ if (f === void 0) return void 0;
125
+ return typeof f === "function" ? { apply: f } : f;
126
+ }
127
+ export { BUILTIN_FILTERS, defineFilter, resolveFilter };
@@ -0,0 +1,17 @@
1
+ /** Digit shapes: Latin (`0–9`) or Devanagari (`०–९`). */
2
+ export type Numerals = "latn" | "deva";
3
+ /** Locale-aware formatting context (money in minor units, dates in an IANA zone). */
4
+ export interface FormatContext {
5
+ /** Plugin locale (`en`, `mr`, `pt-BR`). */
6
+ locale: string;
7
+ /** IANA zone for dates. */
8
+ tz: string;
9
+ /** ISO 4217 code for `money` (default `INR`). */
10
+ currency?: string | undefined;
11
+ /** Digits in the currency's minor unit (default 2: paise, cents). */
12
+ minorDigits?: number | undefined;
13
+ /** Digit shapes (default `latn`; `deva` gives Devanagari digits ०–९). */
14
+ numerals?: Numerals | undefined;
15
+ /** Explicit Intl locale (`en-IN`), overriding the derived one. */
16
+ intlLocale?: string | undefined;
17
+ }
@@ -0,0 +1,88 @@
1
+ // src/core/template/format.ts
2
+ const cache = /* @__PURE__ */ new Map();
3
+ /**
4
+ * The Intl locale for a context: `intlLocale`, else `locale` (+ `-IN` when the locale has no region and the
5
+ * currency is INR or the zone is Asia/Kolkata, so `en` groups ₹1,23,456.00), plus the numbering system.
6
+ */
7
+ function intlLocale(ctx) {
8
+ const key = `${ctx.intlLocale ?? ""}|${ctx.locale}|${ctx.tz}|${ctx.currency ?? ""}|${ctx.numerals ?? ""}`;
9
+ const hit = cache.get(key);
10
+ if (hit) return hit;
11
+ let base = ctx.intlLocale ?? ctx.locale;
12
+ if (!ctx.intlLocale && !base.includes("-") && (ctx.currency === "INR" || ctx.tz === "Asia/Kolkata")) base = `${base}-IN`;
13
+ let out = `${base}-u-nu-${ctx.numerals ?? "latn"}`;
14
+ try {
15
+ out = Intl.getCanonicalLocales(out)[0] ?? "en";
16
+ } catch {
17
+ out = "en";
18
+ }
19
+ cache.set(key, out);
20
+ return out;
21
+ }
22
+ /** Epoch ms from a number, a numeric string or an ISO string; null when not a date. */
23
+ function toEpoch(value) {
24
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
25
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.getTime();
26
+ if (typeof value === "string" && value.trim()) {
27
+ const n = /^-?\d+$/.test(value.trim()) ? Number(value) : Date.parse(value);
28
+ return Number.isFinite(n) ? n : null;
29
+ }
30
+ return null;
31
+ }
32
+ function toNumber(value) {
33
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
34
+ if (typeof value === "bigint") return Number(value);
35
+ if (typeof value === "string" && value.trim() !== "") {
36
+ const n = Number(value);
37
+ return Number.isFinite(n) ? n : null;
38
+ }
39
+ return null;
40
+ }
41
+ /** Minor units → currency text (`12345600` paise → `₹1,23,456.00` in en-IN). */
42
+ function formatMoney(minor, ctx, currency) {
43
+ const digits = ctx.minorDigits ?? 2;
44
+ const code = currency ?? ctx.currency ?? "INR";
45
+ return new Intl.NumberFormat(intlLocale({
46
+ ...ctx,
47
+ currency: code
48
+ }), {
49
+ style: "currency",
50
+ currency: code,
51
+ minimumFractionDigits: digits,
52
+ maximumFractionDigits: digits
53
+ }).format(minor / 10 ** digits);
54
+ }
55
+ function formatNumber(n, ctx, digits) {
56
+ const opts = digits === void 0 ? { maximumFractionDigits: 2 } : {
57
+ minimumFractionDigits: digits,
58
+ maximumFractionDigits: digits
59
+ };
60
+ return new Intl.NumberFormat(intlLocale(ctx), opts).format(n);
61
+ }
62
+ /** A ratio as percent (`0.456` → `45.6%`). */
63
+ function formatPercent(ratio, ctx, digits) {
64
+ const opts = digits === void 0 ? { maximumFractionDigits: 1 } : {
65
+ minimumFractionDigits: digits,
66
+ maximumFractionDigits: digits
67
+ };
68
+ return new Intl.NumberFormat(intlLocale(ctx), {
69
+ style: "percent",
70
+ ...opts
71
+ }).format(ratio);
72
+ }
73
+ const DATE_STYLES = [
74
+ "short",
75
+ "medium",
76
+ "long",
77
+ "full"
78
+ ];
79
+ function isDateStyle(v) {
80
+ return typeof v === "string" && DATE_STYLES.includes(v);
81
+ }
82
+ function formatDateTime(epoch, ctx, parts) {
83
+ return new Intl.DateTimeFormat(intlLocale(ctx), {
84
+ ...parts,
85
+ timeZone: ctx.tz
86
+ }).format(epoch);
87
+ }
88
+ export { DATE_STYLES, formatDateTime, formatMoney, formatNumber, formatPercent, intlLocale, isDateStyle, toEpoch, toNumber };
@@ -0,0 +1,20 @@
1
+ import { Attr, AttrPart, CoreTemplateError, ElementNode, FilterArg, FilterCall, Node, Pos, SectionNode, TemplateAst, TemplateErrorCode, TextNode, VarNode } from "./ast.js";
2
+ import { FormatContext, Numerals } from "./format.js";
3
+ import { BUILTIN_FILTERS, FilterContext, FilterDef, FilterFn, FilterRegistry, defineFilter } from "./filters.js";
4
+ import { ValidateOptions, validateTemplate } from "./validate.js";
5
+ import { ParseResult, parseTemplate } from "./parse.js";
6
+ import { RenderContext, RenderResult, renderTemplate } from "./render.js";
7
+ import { Shape, ShapeVariable, shapeVariables, zodToShape } from "./shape.js";
8
+ import { VariableRef, listVariables } from "./variables.js";
9
+ /** Result of {@link compileTemplate}. */
10
+ export type CompileResult = {
11
+ ok: true;
12
+ ast: TemplateAst;
13
+ } | {
14
+ ok: false;
15
+ ast: TemplateAst;
16
+ errors: CoreTemplateError[];
17
+ };
18
+ /** Parse + validate in one step (syntax errors first; variable checks only when the syntax is valid). */
19
+ export declare function compileTemplate(source: string, opts?: ValidateOptions): CompileResult;
20
+ export { Attr, AttrPart, BUILTIN_FILTERS, CoreTemplateError, ElementNode, FilterArg, FilterCall, FilterContext, FilterDef, FilterFn, FilterRegistry, FormatContext, Node, Numerals, ParseResult, Pos, RenderContext, RenderResult, SectionNode, Shape, ShapeVariable, TemplateAst, TemplateErrorCode, TextNode, ValidateOptions, VarNode, VariableRef, defineFilter, listVariables, parseTemplate, renderTemplate, shapeVariables, validateTemplate, zodToShape };
@@ -0,0 +1,29 @@
1
+ import { pathText } from "./ast.js";
2
+ import { ALLOWED_TAGS, SAFE_URL_RE, checkAttrs, isAllowedTag, literalValue, renderedAttrOk } from "./tags.js";
3
+ import { parseTemplate } from "./parse.js";
4
+ import { DATE_STYLES, formatDateTime, formatMoney, formatNumber, formatPercent, intlLocale, isDateStyle, toEpoch, toNumber } from "./format.js";
5
+ import { BUILTIN_FILTERS, defineFilter, resolveFilter } from "./filters.js";
6
+ import { shapeVariables, zodToShape } from "./shape.js";
7
+ import { validateTemplate } from "./validate.js";
8
+ import { isFalsy, renderTemplate } from "./render.js";
9
+ import { listVariables } from "./variables.js";
10
+ // src/core/template/index.ts
11
+ /** Parse + validate in one step (syntax errors first; variable checks only when the syntax is valid). */
12
+ function compileTemplate(source, opts = {}) {
13
+ const { ast, errors } = parseTemplate(source);
14
+ if (errors.length > 0) return {
15
+ ok: false,
16
+ ast,
17
+ errors
18
+ };
19
+ const issues = validateTemplate(ast, opts);
20
+ return issues.length > 0 ? {
21
+ ok: false,
22
+ ast,
23
+ errors: issues
24
+ } : {
25
+ ok: true,
26
+ ast
27
+ };
28
+ }
29
+ export { ALLOWED_TAGS, BUILTIN_FILTERS, DATE_STYLES, SAFE_URL_RE, checkAttrs, compileTemplate, defineFilter, formatDateTime, formatMoney, formatNumber, formatPercent, intlLocale, isAllowedTag, isDateStyle, isFalsy, listVariables, literalValue, parseTemplate, pathText, renderTemplate, renderedAttrOk, resolveFilter, shapeVariables, toEpoch, toNumber, validateTemplate, zodToShape };
@@ -0,0 +1,8 @@
1
+ import { CoreTemplateError, TemplateAst } from "./ast.js";
2
+ /** Result of {@link parseTemplate}: the tree (best effort) plus syntax errors. */
3
+ export interface ParseResult {
4
+ ast: TemplateAst;
5
+ errors: CoreTemplateError[];
6
+ }
7
+ /** Parses a template (`{{…}}` tags inside the allowed Telegram HTML) into an AST. Never throws; errors carry 1-based positions. */
8
+ export declare function parseTemplate(src: string): ParseResult;