easy-ping 0.6.1 → 0.7.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 (56) hide show
  1. package/README.md +20 -4
  2. package/dist/{adapter-D3lTy-4E.d.cts → adapter-BJ8Iejvs.d.cts} +1 -1
  3. package/dist/{adapter-D3lTy-4E.d.ts → adapter-BJ8Iejvs.d.ts} +1 -1
  4. package/dist/adapters/drizzle.d.cts +1 -1
  5. package/dist/adapters/drizzle.d.ts +1 -1
  6. package/dist/adapters/mongodb.d.cts +2 -2
  7. package/dist/adapters/mongodb.d.ts +2 -2
  8. package/dist/adapters/mysql.d.cts +2 -2
  9. package/dist/adapters/mysql.d.ts +2 -2
  10. package/dist/adapters/postgres.d.cts +2 -2
  11. package/dist/adapters/postgres.d.ts +2 -2
  12. package/dist/adapters/sqlite.d.cts +2 -2
  13. package/dist/adapters/sqlite.d.ts +2 -2
  14. package/dist/chunk-R3BE67L7.js +13 -0
  15. package/dist/chunk-R3BE67L7.js.map +1 -0
  16. package/dist/chunk-SSHVZHSJ.js +100 -0
  17. package/dist/chunk-SSHVZHSJ.js.map +1 -0
  18. package/dist/index.d.cts +4 -4
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.js +2 -11
  21. package/dist/index.js.map +1 -1
  22. package/dist/{plugin-Cg9usuUi.d.ts → plugin-CSLKKgXT.d.ts} +1 -1
  23. package/dist/{plugin-CHd2ZiGC.d.cts → plugin-Wwp1nvo0.d.cts} +1 -1
  24. package/dist/plugins/digests.d.cts +2 -2
  25. package/dist/plugins/digests.d.ts +2 -2
  26. package/dist/plugins/preferences-client.d.cts +2 -2
  27. package/dist/plugins/preferences-client.d.ts +2 -2
  28. package/dist/plugins/preferences-react.d.cts +1 -1
  29. package/dist/plugins/preferences-react.d.ts +1 -1
  30. package/dist/plugins/preferences.cjs +1 -1
  31. package/dist/plugins/preferences.cjs.map +1 -1
  32. package/dist/plugins/preferences.d.cts +2 -2
  33. package/dist/plugins/preferences.d.ts +2 -2
  34. package/dist/plugins/preferences.js +1 -1
  35. package/dist/plugins/preferences.js.map +1 -1
  36. package/dist/plugins/push.d.cts +2 -2
  37. package/dist/plugins/push.d.ts +2 -2
  38. package/dist/plugins/telegram.cjs +379 -0
  39. package/dist/plugins/telegram.cjs.map +1 -0
  40. package/dist/plugins/telegram.d.cts +153 -0
  41. package/dist/plugins/telegram.d.ts +153 -0
  42. package/dist/plugins/telegram.js +313 -0
  43. package/dist/plugins/telegram.js.map +1 -0
  44. package/dist/providers/telegram.cjs +103 -0
  45. package/dist/providers/telegram.cjs.map +1 -0
  46. package/dist/providers/telegram.d.cts +83 -0
  47. package/dist/providers/telegram.d.ts +83 -0
  48. package/dist/providers/telegram.js +3 -0
  49. package/dist/providers/telegram.js.map +1 -0
  50. package/dist/providers/web-push.d.cts +2 -2
  51. package/dist/providers/web-push.d.ts +2 -2
  52. package/dist/schema.d.cts +2 -2
  53. package/dist/schema.d.ts +2 -2
  54. package/dist/testing.d.cts +1 -1
  55. package/dist/testing.d.ts +1 -1
  56. package/package.json +13 -2
@@ -0,0 +1,103 @@
1
+ 'use strict';
2
+
3
+ // src/providers/telegram/index.ts
4
+ var telegramStartLink = (botUsername, code) => `https://t.me/${botUsername.replace(/^@/, "")}?start=${encodeURIComponent(code)}`;
5
+ function telegramBot(options) {
6
+ const doFetch = options.fetch ?? globalThis.fetch;
7
+ const base = `${(options.apiBase ?? "https://api.telegram.org").replace(/\/$/, "")}/bot${options.token}`;
8
+ const timeoutMs = options.timeoutMs ?? 1e4;
9
+ const redact = (text) => text.split(options.token).join("<token>");
10
+ async function call(method, body, signal, budgetMs = timeoutMs) {
11
+ const timeout = AbortSignal.timeout(budgetMs);
12
+ const merged = signal ? AbortSignal.any([signal, timeout]) : timeout;
13
+ const response = await doFetch(`${base}/${method}`, {
14
+ method: "POST",
15
+ headers: { "content-type": "application/json" },
16
+ body: JSON.stringify(body),
17
+ signal: merged
18
+ });
19
+ const data = await response.json().catch(() => ({ ok: false }));
20
+ return { status: response.status, data };
21
+ }
22
+ async function must(method, body) {
23
+ const { data } = await call(method, body);
24
+ if (!data.ok)
25
+ throw new Error(redact(`telegram ${method} failed: ${data.description ?? "unknown"}`));
26
+ return data.result;
27
+ }
28
+ return {
29
+ name: "telegram",
30
+ async send(message) {
31
+ const body = {
32
+ chat_id: message.chatId,
33
+ text: message.text,
34
+ parse_mode: "HTML",
35
+ link_preview_options: { is_disabled: true }
36
+ };
37
+ if (message.buttons?.length) {
38
+ body.reply_markup = {
39
+ inline_keyboard: [
40
+ message.buttons.map((button) => ({ text: button.text, url: button.url }))
41
+ ]
42
+ };
43
+ }
44
+ let status;
45
+ let data;
46
+ try {
47
+ ({ status, data } = await call(
48
+ "sendMessage",
49
+ body,
50
+ message.signal
51
+ ));
52
+ } catch (error) {
53
+ const text = error instanceof Error ? error.message : String(error);
54
+ return { ok: false, error: redact(text), retryable: true, gone: false };
55
+ }
56
+ if (data.ok) return { ok: true, messageId: data.result.message_id };
57
+ const description = redact(data.description ?? `HTTP ${status}`);
58
+ const code = data.error_code ?? status;
59
+ if (code === 429) {
60
+ return {
61
+ ok: false,
62
+ error: description,
63
+ retryable: true,
64
+ gone: false,
65
+ retryAfterSeconds: data.parameters?.retry_after
66
+ };
67
+ }
68
+ const gone = code === 403 || code === 400 && /chat not found|user is deactivated|bot was kicked/i.test(description);
69
+ return { ok: false, error: description, retryable: code >= 500, gone };
70
+ },
71
+ async getUpdates({ offset, timeoutSeconds = 25, signal }) {
72
+ const { data } = await call(
73
+ "getUpdates",
74
+ {
75
+ ...offset === void 0 ? {} : { offset },
76
+ timeout: timeoutSeconds,
77
+ allowed_updates: ["message", "my_chat_member"]
78
+ },
79
+ signal,
80
+ (timeoutSeconds + 10) * 1e3
81
+ );
82
+ if (!data.ok)
83
+ throw new Error(redact(`telegram getUpdates failed: ${data.description ?? "unknown"}`));
84
+ return data.result;
85
+ },
86
+ setWebhook: async (url, secretToken) => {
87
+ await must("setWebhook", {
88
+ url,
89
+ secret_token: secretToken,
90
+ allowed_updates: ["message", "my_chat_member"]
91
+ });
92
+ },
93
+ deleteWebhook: async () => {
94
+ await must("deleteWebhook", {});
95
+ },
96
+ getMe: () => must("getMe", {})
97
+ };
98
+ }
99
+
100
+ exports.telegramBot = telegramBot;
101
+ exports.telegramStartLink = telegramStartLink;
102
+ //# sourceMappingURL=telegram.cjs.map
103
+ //# sourceMappingURL=telegram.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/providers/telegram/index.ts"],"names":[],"mappings":"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","file":"telegram.cjs","sourcesContent":["export type TelegramButton = { text: string; url: string };\n\nexport type TelegramMessage = {\n chatId: string;\n /** Telegram HTML: <b>, <i>, <a href>, <code>, <pre>. Escape user text with escapeHtml(). */\n text: string;\n buttons?: readonly TelegramButton[] | undefined;\n signal?: AbortSignal | undefined;\n};\n\nexport type TelegramSendResult =\n | { ok: true; messageId: number }\n | {\n ok: false;\n error: string;\n /** Worth another attempt: rate limit, 5xx, network. */\n retryable: boolean;\n /** The chat can never receive again (user blocked the bot, chat deleted). Prune it. */\n gone: boolean;\n retryAfterSeconds?: number | undefined;\n };\n\n/** The slice of a Bot API update the plugin reads. Everything else passes through untouched. */\nexport type TelegramUpdate = {\n update_id: number;\n message?: {\n text?: string;\n chat: { id: number | string; type: string; username?: string; title?: string };\n from?: { id: number; username?: string; first_name?: string };\n };\n my_chat_member?: {\n chat: { id: number | string; type: string; username?: string; title?: string };\n new_chat_member: { status: string };\n };\n};\n\nexport type TelegramProvider = {\n name: string;\n send(message: TelegramMessage): Promise<TelegramSendResult>;\n /** Long-polls for updates. For development or hosts without a public URL. */\n getUpdates(options: {\n offset?: number | undefined;\n timeoutSeconds?: number | undefined;\n signal?: AbortSignal | undefined;\n }): Promise<TelegramUpdate[]>;\n /** Points Telegram at your webhook route. Run once per deployment URL. */\n setWebhook(url: string, secretToken: string): Promise<void>;\n deleteWebhook(): Promise<void>;\n getMe(): Promise<{ id: number; username: string }>;\n};\n\nexport type TelegramBotOptions = {\n /** From @BotFather. Never appears in errors or logs. */\n token: string;\n fetch?: typeof globalThis.fetch | undefined;\n /** Default https://api.telegram.org. A local fake for tests. */\n apiBase?: string | undefined;\n timeoutMs?: number | undefined;\n};\n\ntype ApiResponse<T> =\n | { ok: true; result: T }\n | { ok: false; description?: string; error_code?: number; parameters?: { retry_after?: number } };\n\n/** Deep link that opens the bot with a start parameter. Codes are [A-Za-z0-9_-], at most 64 chars. */\nexport const telegramStartLink = (botUsername: string, code: string) =>\n `https://t.me/${botUsername.replace(/^@/, \"\")}?start=${encodeURIComponent(code)}`;\n\n/** Bot API over fetch, so it runs on edge runtimes. Failures are classified, never thrown, on send. */\nexport function telegramBot(options: TelegramBotOptions): TelegramProvider {\n const doFetch = options.fetch ?? globalThis.fetch;\n const base = `${(options.apiBase ?? \"https://api.telegram.org\").replace(/\\/$/, \"\")}/bot${options.token}`;\n const timeoutMs = options.timeoutMs ?? 10_000;\n const redact = (text: string) => text.split(options.token).join(\"<token>\");\n\n async function call<T>(\n method: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n budgetMs = timeoutMs,\n ): Promise<{ status: number; data: ApiResponse<T> }> {\n const timeout = AbortSignal.timeout(budgetMs);\n const merged = signal ? AbortSignal.any([signal, timeout]) : timeout;\n const response = await doFetch(`${base}/${method}`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: merged,\n });\n const data = (await response.json().catch(() => ({ ok: false }))) as ApiResponse<T>;\n return { status: response.status, data };\n }\n\n async function must<T>(method: string, body: Record<string, unknown>): Promise<T> {\n const { data } = await call<T>(method, body);\n if (!data.ok)\n throw new Error(redact(`telegram ${method} failed: ${data.description ?? \"unknown\"}`));\n return data.result;\n }\n\n return {\n name: \"telegram\",\n\n async send(message) {\n const body: Record<string, unknown> = {\n chat_id: message.chatId,\n text: message.text,\n parse_mode: \"HTML\",\n link_preview_options: { is_disabled: true },\n };\n if (message.buttons?.length) {\n body.reply_markup = {\n inline_keyboard: [\n message.buttons.map((button) => ({ text: button.text, url: button.url })),\n ],\n };\n }\n\n let status: number;\n let data: ApiResponse<{ message_id: number }>;\n try {\n ({ status, data } = await call<{ message_id: number }>(\n \"sendMessage\",\n body,\n message.signal,\n ));\n } catch (error) {\n const text = error instanceof Error ? error.message : String(error);\n return { ok: false, error: redact(text), retryable: true, gone: false };\n }\n\n if (data.ok) return { ok: true, messageId: data.result.message_id };\n\n const description = redact(data.description ?? `HTTP ${status}`);\n const code = data.error_code ?? status;\n if (code === 429) {\n return {\n ok: false,\n error: description,\n retryable: true,\n gone: false,\n retryAfterSeconds: data.parameters?.retry_after,\n };\n }\n // 403: the user blocked the bot or was deactivated. 400 \"chat not found\": deleted or never existed.\n const gone =\n code === 403 ||\n (code === 400 && /chat not found|user is deactivated|bot was kicked/i.test(description));\n return { ok: false, error: description, retryable: code >= 500, gone };\n },\n\n async getUpdates({ offset, timeoutSeconds = 25, signal }) {\n // Telegram holds the request open for `timeout` seconds; the budget must outlive it.\n const { data } = await call<TelegramUpdate[]>(\n \"getUpdates\",\n {\n ...(offset === undefined ? {} : { offset }),\n timeout: timeoutSeconds,\n allowed_updates: [\"message\", \"my_chat_member\"],\n },\n signal,\n (timeoutSeconds + 10) * 1000,\n );\n if (!data.ok)\n throw new Error(redact(`telegram getUpdates failed: ${data.description ?? \"unknown\"}`));\n return data.result;\n },\n\n setWebhook: async (url, secretToken) => {\n await must(\"setWebhook\", {\n url,\n secret_token: secretToken,\n allowed_updates: [\"message\", \"my_chat_member\"],\n });\n },\n deleteWebhook: async () => {\n await must(\"deleteWebhook\", {});\n },\n getMe: () => must(\"getMe\", {}),\n };\n}\n"]}
@@ -0,0 +1,83 @@
1
+ type TelegramButton = {
2
+ text: string;
3
+ url: string;
4
+ };
5
+ type TelegramMessage = {
6
+ chatId: string;
7
+ /** Telegram HTML: <b>, <i>, <a href>, <code>, <pre>. Escape user text with escapeHtml(). */
8
+ text: string;
9
+ buttons?: readonly TelegramButton[] | undefined;
10
+ signal?: AbortSignal | undefined;
11
+ };
12
+ type TelegramSendResult = {
13
+ ok: true;
14
+ messageId: number;
15
+ } | {
16
+ ok: false;
17
+ error: string;
18
+ /** Worth another attempt: rate limit, 5xx, network. */
19
+ retryable: boolean;
20
+ /** The chat can never receive again (user blocked the bot, chat deleted). Prune it. */
21
+ gone: boolean;
22
+ retryAfterSeconds?: number | undefined;
23
+ };
24
+ /** The slice of a Bot API update the plugin reads. Everything else passes through untouched. */
25
+ type TelegramUpdate = {
26
+ update_id: number;
27
+ message?: {
28
+ text?: string;
29
+ chat: {
30
+ id: number | string;
31
+ type: string;
32
+ username?: string;
33
+ title?: string;
34
+ };
35
+ from?: {
36
+ id: number;
37
+ username?: string;
38
+ first_name?: string;
39
+ };
40
+ };
41
+ my_chat_member?: {
42
+ chat: {
43
+ id: number | string;
44
+ type: string;
45
+ username?: string;
46
+ title?: string;
47
+ };
48
+ new_chat_member: {
49
+ status: string;
50
+ };
51
+ };
52
+ };
53
+ type TelegramProvider = {
54
+ name: string;
55
+ send(message: TelegramMessage): Promise<TelegramSendResult>;
56
+ /** Long-polls for updates. For development or hosts without a public URL. */
57
+ getUpdates(options: {
58
+ offset?: number | undefined;
59
+ timeoutSeconds?: number | undefined;
60
+ signal?: AbortSignal | undefined;
61
+ }): Promise<TelegramUpdate[]>;
62
+ /** Points Telegram at your webhook route. Run once per deployment URL. */
63
+ setWebhook(url: string, secretToken: string): Promise<void>;
64
+ deleteWebhook(): Promise<void>;
65
+ getMe(): Promise<{
66
+ id: number;
67
+ username: string;
68
+ }>;
69
+ };
70
+ type TelegramBotOptions = {
71
+ /** From @BotFather. Never appears in errors or logs. */
72
+ token: string;
73
+ fetch?: typeof globalThis.fetch | undefined;
74
+ /** Default https://api.telegram.org. A local fake for tests. */
75
+ apiBase?: string | undefined;
76
+ timeoutMs?: number | undefined;
77
+ };
78
+ /** Deep link that opens the bot with a start parameter. Codes are [A-Za-z0-9_-], at most 64 chars. */
79
+ declare const telegramStartLink: (botUsername: string, code: string) => string;
80
+ /** Bot API over fetch, so it runs on edge runtimes. Failures are classified, never thrown, on send. */
81
+ declare function telegramBot(options: TelegramBotOptions): TelegramProvider;
82
+
83
+ export { type TelegramBotOptions, type TelegramButton, type TelegramMessage, type TelegramProvider, type TelegramSendResult, type TelegramUpdate, telegramBot, telegramStartLink };
@@ -0,0 +1,83 @@
1
+ type TelegramButton = {
2
+ text: string;
3
+ url: string;
4
+ };
5
+ type TelegramMessage = {
6
+ chatId: string;
7
+ /** Telegram HTML: <b>, <i>, <a href>, <code>, <pre>. Escape user text with escapeHtml(). */
8
+ text: string;
9
+ buttons?: readonly TelegramButton[] | undefined;
10
+ signal?: AbortSignal | undefined;
11
+ };
12
+ type TelegramSendResult = {
13
+ ok: true;
14
+ messageId: number;
15
+ } | {
16
+ ok: false;
17
+ error: string;
18
+ /** Worth another attempt: rate limit, 5xx, network. */
19
+ retryable: boolean;
20
+ /** The chat can never receive again (user blocked the bot, chat deleted). Prune it. */
21
+ gone: boolean;
22
+ retryAfterSeconds?: number | undefined;
23
+ };
24
+ /** The slice of a Bot API update the plugin reads. Everything else passes through untouched. */
25
+ type TelegramUpdate = {
26
+ update_id: number;
27
+ message?: {
28
+ text?: string;
29
+ chat: {
30
+ id: number | string;
31
+ type: string;
32
+ username?: string;
33
+ title?: string;
34
+ };
35
+ from?: {
36
+ id: number;
37
+ username?: string;
38
+ first_name?: string;
39
+ };
40
+ };
41
+ my_chat_member?: {
42
+ chat: {
43
+ id: number | string;
44
+ type: string;
45
+ username?: string;
46
+ title?: string;
47
+ };
48
+ new_chat_member: {
49
+ status: string;
50
+ };
51
+ };
52
+ };
53
+ type TelegramProvider = {
54
+ name: string;
55
+ send(message: TelegramMessage): Promise<TelegramSendResult>;
56
+ /** Long-polls for updates. For development or hosts without a public URL. */
57
+ getUpdates(options: {
58
+ offset?: number | undefined;
59
+ timeoutSeconds?: number | undefined;
60
+ signal?: AbortSignal | undefined;
61
+ }): Promise<TelegramUpdate[]>;
62
+ /** Points Telegram at your webhook route. Run once per deployment URL. */
63
+ setWebhook(url: string, secretToken: string): Promise<void>;
64
+ deleteWebhook(): Promise<void>;
65
+ getMe(): Promise<{
66
+ id: number;
67
+ username: string;
68
+ }>;
69
+ };
70
+ type TelegramBotOptions = {
71
+ /** From @BotFather. Never appears in errors or logs. */
72
+ token: string;
73
+ fetch?: typeof globalThis.fetch | undefined;
74
+ /** Default https://api.telegram.org. A local fake for tests. */
75
+ apiBase?: string | undefined;
76
+ timeoutMs?: number | undefined;
77
+ };
78
+ /** Deep link that opens the bot with a start parameter. Codes are [A-Za-z0-9_-], at most 64 chars. */
79
+ declare const telegramStartLink: (botUsername: string, code: string) => string;
80
+ /** Bot API over fetch, so it runs on edge runtimes. Failures are classified, never thrown, on send. */
81
+ declare function telegramBot(options: TelegramBotOptions): TelegramProvider;
82
+
83
+ export { type TelegramBotOptions, type TelegramButton, type TelegramMessage, type TelegramProvider, type TelegramSendResult, type TelegramUpdate, telegramBot, telegramStartLink };
@@ -0,0 +1,3 @@
1
+ export { telegramBot, telegramStartLink } from '../chunk-SSHVZHSJ.js';
2
+ //# sourceMappingURL=telegram.js.map
3
+ //# sourceMappingURL=telegram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"telegram.js"}
@@ -1,6 +1,6 @@
1
1
  import { PushProvider } from '../plugins/push.cjs';
2
- import '../plugin-CHd2ZiGC.cjs';
3
- import '../adapter-D3lTy-4E.cjs';
2
+ import '../plugin-Wwp1nvo0.cjs';
3
+ import '../adapter-BJ8Iejvs.cjs';
4
4
 
5
5
  /** The subscription itself is unusable. Distinct from a transport failure so the plugin prunes rather than retries. */
6
6
  declare class InvalidSubscriptionError extends Error {
@@ -1,6 +1,6 @@
1
1
  import { PushProvider } from '../plugins/push.js';
2
- import '../plugin-Cg9usuUi.js';
3
- import '../adapter-D3lTy-4E.js';
2
+ import '../plugin-CSLKKgXT.js';
3
+ import '../adapter-BJ8Iejvs.js';
4
4
 
5
5
  /** The subscription itself is unusable. Distinct from a transport failure so the plugin prunes rather than retries. */
6
6
  declare class InvalidSubscriptionError extends Error {
package/dist/schema.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SchemaDeclaration } from './plugin-CHd2ZiGC.cjs';
2
- import './adapter-D3lTy-4E.cjs';
1
+ import { S as SchemaDeclaration } from './plugin-Wwp1nvo0.cjs';
2
+ import './adapter-BJ8Iejvs.cjs';
3
3
 
4
4
  declare const toSnakeCase: (value: string) => string;
5
5
  /** Core tables in the plugin format. A drift test asserts it agrees with the Drizzle schema. */
package/dist/schema.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SchemaDeclaration } from './plugin-Cg9usuUi.js';
2
- import './adapter-D3lTy-4E.js';
1
+ import { S as SchemaDeclaration } from './plugin-CSLKKgXT.js';
2
+ import './adapter-BJ8Iejvs.js';
3
3
 
4
4
  declare const toSnakeCase: (value: string) => string;
5
5
  /** Core tables in the plugin format. A drift test asserts it agrees with the Drizzle schema. */
@@ -1,4 +1,4 @@
1
- import { D as DatabaseAdapter } from './adapter-D3lTy-4E.cjs';
1
+ import { D as DatabaseAdapter } from './adapter-BJ8Iejvs.cjs';
2
2
 
3
3
  type ConformanceContext = {
4
4
  adapter: DatabaseAdapter;
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { D as DatabaseAdapter } from './adapter-D3lTy-4E.js';
1
+ import { D as DatabaseAdapter } from './adapter-BJ8Iejvs.js';
2
2
 
3
3
  type ConformanceContext = {
4
4
  adapter: DatabaseAdapter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-ping",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Self-hosted, type-safe notifications for TypeScript apps. Your database, your users.",
5
5
  "license": "MIT",
6
6
  "author": "Teklu Moges",
@@ -22,6 +22,7 @@
22
22
  "push-notifications",
23
23
  "vapid",
24
24
  "service-worker",
25
+ "telegram",
25
26
  "email",
26
27
  "transactional-email",
27
28
  "resend",
@@ -130,11 +131,21 @@
130
131
  "import": "./dist/plugins/push.js",
131
132
  "require": "./dist/plugins/push.cjs"
132
133
  },
134
+ "./plugins/telegram": {
135
+ "types": "./dist/plugins/telegram.d.ts",
136
+ "import": "./dist/plugins/telegram.js",
137
+ "require": "./dist/plugins/telegram.cjs"
138
+ },
133
139
  "./providers/web-push": {
134
140
  "types": "./dist/providers/web-push.d.ts",
135
141
  "import": "./dist/providers/web-push.js",
136
142
  "require": "./dist/providers/web-push.cjs"
137
143
  },
144
+ "./providers/telegram": {
145
+ "types": "./dist/providers/telegram.d.ts",
146
+ "import": "./dist/providers/telegram.js",
147
+ "require": "./dist/providers/telegram.cjs"
148
+ },
138
149
  "./browser": {
139
150
  "types": "./dist/browser.d.ts",
140
151
  "import": "./dist/browser.js",
@@ -179,7 +190,7 @@
179
190
  "@standard-schema/spec": "^1.1.0"
180
191
  },
181
192
  "scripts": {
182
- "build": "node scripts/clean.mjs && tsup",
193
+ "build": "node scripts/build.mjs",
183
194
  "dev": "tsup --watch",
184
195
  "test": "vitest run",
185
196
  "typecheck": "tsc --noEmit",