@adriangalilea/utils 0.25.0 → 0.27.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/README.md CHANGED
@@ -77,30 +77,26 @@ currency.formatBasisPoints(50) // "50 bps"
77
77
 
78
78
  ### Format
79
79
 
80
- Number and currency formatting utilities:
80
+ Pure number formatting — named exports, tree-shakeable, zero currency baggage
81
+ (money formatting lives in the currency module, which owns the symbol/decimals
82
+ knowledge and its crypto-symbol dataset):
81
83
 
82
84
  ```typescript
83
- import { format } from '@adriangalilea/utils/format'
84
-
85
- // Number formatting
86
- format.number(1234.567, 2) // "1234.57"
87
- format.withCommas(1234567) // "1,234,567"
88
- format.withCommas(1234.567, 2) // "1,234.57"
89
-
90
- // Compact notation
91
- format.compact(1234567) // "1.2M"
92
- format.compact(1234) // "1.2K"
93
-
94
- // Currency formatting
95
- format.usd(1234.56) // "$1,234.56"
96
- format.btc(0.00123456) // "0.001235 ₿"
97
- format.eth(1.23456789) // "1.234568 Ξ"
98
- format.auto(100, 'EUR') // "€100.00"
99
-
100
- // Percentages
101
- format.percentage(12.3456) // "12.3%"
102
- format.percentage(0.05) // "0.05%"
103
- format.percentage(123.456) // "123%"
85
+ import { compact, percentage, withCommas } from '@adriangalilea/utils/format'
86
+
87
+ withCommas(1234567) // "1,234,567"
88
+ withCommas(1234.567, 2) // "1,234.57"
89
+ compact(1234567) // "1.2M"
90
+ compact(1234) // "1.2K"
91
+ percentage(12.3456) // "12.3%"
92
+ percentage(0.05) // "0.05%"
93
+ percentage(123.456) // "123%"
94
+
95
+ // Money — from currency, not format:
96
+ import { usd, btc, money } from '@adriangalilea/utils/currency'
97
+ usd(1234.56) // "$1234.56"
98
+ btc(0.00001234) // "0.00001234 ₿"
99
+ money(100, 'EUR') // "€100.00"
104
100
  ```
105
101
 
106
102
  ### CLI presentation (`cli`)
@@ -389,7 +385,9 @@ Then `pnpm install`. Every `ctx.send` / `ctx.sendDocument` / `ctx.reply` / etc.
389
385
  |---|---|
390
386
  | `@adriangalilea/utils/bot/ctx` | Structural ctx types (`BotMessageCtx`, `BotCallbackCtx`, …) and the `narrow<T>(ctx)` cast helper. Pure typing — no runtime state. |
391
387
  | `@adriangalilea/utils/bot/keys` | The bot-id key namespace, a **persisted contract**: `botId(ctx)`, `botStorageKey(ctx, userId)` → `bot-<id>:<userId>`, `botSubKey(ctx, sub)` → `bot-<id>:<sub>`. Every storage key the library writes derives from here, and those keys live in YOUR Redis/D1 rows — the shape never changes. Pure functions of `ctx.bot.info.id` (no env/fs), which is what keeps the whole bot surface Worker-safe. |
392
- | `@adriangalilea/utils/bot/kit` | `gracefulStart(bot, opts?)` — SIGINT/SIGTERM → `bot.stop()` → exit; force-kills if shutdown hangs. DMs the admin `@<bot> started.` / `@<bot> shutting down.` by default when `KEV.TELEGRAM_ADMIN_ID` is set (graceful only — crashes don't trigger `onStop`); pass `notifyAdmin: false` to disable or `notifyAdmin: 12345` for an explicit chat id.<br>`adminContext({ adminId? })` — reads `TELEGRAM_ADMIN_ID` from `kev` (with optional hardcoded fallback), decorates `ctx.adminId` + `ctx.isAdmin`.<br>`botSession(opts)` — **drop-in replacement for `@gramio/session`'s `session()`** that auto-namespaces every key as `bot-<id>:<senderId>` using `ctx.bot.info.id` (populated by `getMe()` at startup). Use this instead of `session()` — full stop. Multiple bots sharing one Redis stay isolated by construction; every plugin in this package derives the same prefix internally via `botStorageKey(ctx, userId)` / `botSubKey(ctx, sub)` (from `bot/keys`). No regex, no manual prefix argument, no way to forget.<br>`prefixStorage(storage, prefix)` — escape hatch for adding a top-level prefix on top of the bot-id namespace; almost never needed. |
388
+ | `@adriangalilea/utils/bot/kit` | The Node-only corner. `gracefulStart(bot, opts?)` — SIGINT/SIGTERM → `bot.stop()` → exit; force-kills if shutdown hangs. DMs the admin `@<bot> started.` / `@<bot> shutting down.` by default when `KEV.TELEGRAM_ADMIN_ID` is set (graceful only — crashes don't trigger `onStop`); pass `notifyAdmin: false` to disable or `notifyAdmin: 12345` for an explicit chat id.<br>`adminContext({ adminId? })` — reads `TELEGRAM_ADMIN_ID` from `kev` (with optional hardcoded fallback), decorates `ctx.adminId` + `ctx.isAdmin`.<br>Also re-exports `botSession` / `prefixStorage` from `bot/session` so Node consumers keep one import. |
389
+ | `@adriangalilea/utils/bot/session` | `botSession(opts)` — **drop-in replacement for `@gramio/session`'s `session()`** that auto-namespaces every key as `bot-<id>:<senderId>` using `ctx.bot.info.id` (populated by `getMe()` at startup). Use this instead of `session()` — full stop. Multiple bots sharing one Redis/D1 stay isolated by construction; every plugin in this package derives the same prefix internally via `botStorageKey(ctx, userId)` / `botSubKey(ctx, sub)` (from `bot/keys`). No regex, no manual prefix argument, no way to forget.<br>`prefixStorage(storage, prefix)` — escape hatch for adding a top-level prefix on top of the bot-id namespace; almost never needed. Worker-safe (used to live in `bot/kit`, which forced Worker bots to hand-roll the keying). |
390
+ | `@adriangalilea/utils/bot/notify` | Best-effort admin DMs, worker-safe (you pass the ids; no env, no process). `notifyAdmins(bot, adminIds, text, extra?)` — DM each admin, failures logged and swallowed (a notification must never take the bot down). `alertAdminError(bot, adminIds, label, error, throttle?)` — truncated `🚨 label\nName: message`, rate-limited through a caller-owned `alertThrottle(ms?)` so a failure storm sends one alert per window. `gracefulStart`'s start/stop DMs are built on this. |
393
391
  | `@adriangalilea/utils/bot/access-control` | Personal-bot ACL — gates non-admin/non-default users; admin gets DM with `[✅ Aprobar][❌ Denegar]` on first attempt; `/access` opens a persistent menu (revoke / reapprove / list pending). Backed by `@gramio/session` per-user + a small index. **Native alternative**: BotFather → Bot Settings → Access → "Restrict bot usage" — flat allow-list at Telegram. Use this plugin when you want in-bot approval flow instead of a BotFather round-trip; both can coexist. |
394
392
  | `@adriangalilea/utils/bot/allow-list` | Static allow-list by **id and/or @username** — stateless, no session/storage. `allowList({ ids?, usernames? })` is a plugin that decorates `ctx.allowed` (boolean); gate in your handlers (`if (!ctx.allowed) return`). `makeAllowList(...)` is the pure framework-free predicate. The light counterpart to `access-control` (which adds an approve/deny flow + revocable store, needing session+storage). Username caveat: a `@username` is optional and mutable, and the Bot API can't resolve username→id ahead of time — prefer `ids` when known, `usernames` is the pragmatic fallback. |
395
393
  | `@adriangalilea/utils/bot/coalesce` | Joins client-split inbound messages back into one. When a user pastes >4096 chars, Telegram clients fragment it into separate `message` updates with no marker. Middleware detects the burst and emits one combined event. |
@@ -402,7 +400,8 @@ Standard wiring:
402
400
  ```typescript
403
401
  import { Bot } from 'gramio'
404
402
  import { redisStorage } from '@gramio/storage-redis'
405
- import { adminContext, botSession, gracefulStart } from '@adriangalilea/utils/bot/kit'
403
+ import { adminContext, gracefulStart } from '@adriangalilea/utils/bot/kit'
404
+ import { botSession } from '@adriangalilea/utils/bot/session'
406
405
  import { accessControl } from '@adriangalilea/utils/bot/access-control'
407
406
  import { llmStream, llmHistory } from '@adriangalilea/utils/bot/llm'
408
407
 
@@ -20,6 +20,7 @@ export * from "./kit.js";
20
20
  export * from "./language.js";
21
21
  export * from "./llm.js";
22
22
  export * from "./menu.js";
23
+ export * from "./notify.js";
23
24
  export * from "./payments/index.js";
24
25
  export * from "./storage.js";
25
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
package/dist/bot/index.js CHANGED
@@ -20,6 +20,7 @@ export * from "./kit.js";
20
20
  export * from "./language.js";
21
21
  export * from "./llm.js";
22
22
  export * from "./menu.js";
23
+ export * from "./notify.js";
23
24
  export * from "./payments/index.js";
24
25
  export * from "./storage.js";
25
26
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC"}
package/dist/bot/kit.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Foundational helpers every bot wants.
2
+ * Foundational helpers for NODE bots — the library's deliberately
3
+ * Node-only corner (process signals, KEV env resolution). Worker bots
4
+ * use the worker-safe subpaths instead: `bot/session` (namespaced
5
+ * sessions) and `bot/notify` (admin DMs) hold the pieces that used to
6
+ * live here but never needed an OS.
3
7
  *
4
8
  * `gracefulStart(bot, opts?)` — wires SIGINT/SIGTERM to bot.stop(),
5
9
  * runs an optional shutdown hook, force-kills if it hangs. DMs the
@@ -12,15 +16,9 @@
12
16
  * every context with `ctx.adminId` (number) and `ctx.isAdmin`
13
17
  * (boolean). Throws at startup if neither source provides an id.
14
18
  *
15
- * `botSession(opts)` — drop-in replacement for `@gramio/session`'s
16
- * `session()` that AUTOMATICALLY namespaces every key with the
17
- * calling bot's id (parsed from `ctx.bot.options.token`). When
18
- * several bots share one Redis instance, each writes to a disjoint
19
- * keyspace (`bot-<id>:<senderId>`) with no manual wiring. Every
20
- * plugin in this package (`accessControl`, `botMenu`,
21
- * `llmHistory`, …) derives the SAME prefix from `ctx.bot` for its
22
- * own storage access, so the whole package stays isolated by
23
- * construction. Use this instead of `session()` — full stop.
19
+ * `botSession` / `prefixStorage` — re-exported from `bot/session` so
20
+ * Node consumers keep one import; the implementation is worker-safe
21
+ * and lives there.
24
22
  *
25
23
  * Peer deps: `gramio`, `@gramio/session`, `@gramio/storage`.
26
24
  *
@@ -39,10 +37,9 @@
39
37
  *
40
38
  * await gracefulStart(bot, { onShutdown: () => db.end() })
41
39
  */
42
- import { type SessionOptions, session } from "@gramio/session";
43
- import type { Storage } from "@gramio/storage";
44
40
  import type { Plugin as PluginType } from "gramio";
45
41
  import { type AnyBot } from "gramio";
42
+ export { botSession, prefixStorage } from "./session.js";
46
43
  export type GracefulStartOptions = {
47
44
  /** Runs after `bot.stop()` resolves, before `process.exit`. Close DBs, flush logs. */
48
45
  onShutdown?: () => Promise<void> | void;
@@ -88,52 +85,5 @@ export declare const adminContext: (opts?: AdminContextOptions) => PluginType<{}
88
85
  isAdmin: boolean;
89
86
  };
90
87
  }, {}>;
91
- /**
92
- * Drop-in replacement for `@gramio/session`'s `session()` that forces
93
- * the storage key to include the calling bot's id. Equivalent to
94
- * passing `getSessionKey: (ctx) => bot-${id}:${senderId}` explicitly,
95
- * but baked in so no consumer can forget.
96
- *
97
- * Returns a plain `@gramio/session` plugin — same shape, same derives,
98
- * fully interchangeable in `.extend(userSession)` chains and plugin
99
- * options like `accessControl({ session: userSession, … })`.
100
- *
101
- * `getSessionKey` can still be overridden for advanced cases (e.g. a
102
- * per-chat session in groups); the override is wrapped so it still
103
- * receives the `bot-<id>:` prefix. Don't override unless you know what
104
- * you're doing — every plugin in this package expects the
105
- * `bot-<id>:<userId>` shape for cross-user storage reads.
106
- *
107
- * @example
108
- * import { redisStorage } from '@gramio/storage-redis'
109
- * import { botSession } from '@adriangalilea/utils/bot/kit'
110
- *
111
- * const storage = redisStorage() // shared across N bots, safe
112
- *
113
- * const userSession = botSession({
114
- * storage,
115
- * key: 'session',
116
- * initial: () => ({}),
117
- * })
118
- *
119
- * bot.extend(userSession)
120
- */
121
- export declare const botSession: <Data = unknown, Key extends string = "session", Lazy extends boolean = false>(opts: SessionOptions<Data, Key, Lazy>) => ReturnType<typeof session<Data, Key, Lazy>>;
122
- /**
123
- * Wraps any `@gramio/storage` adapter so every key gets a fixed
124
- * `${prefix}` prepended.
125
- *
126
- * **You almost never need this.** The library's plugins are isolated
127
- * by bot id automatically when you wire `botSession` (see above) —
128
- * `prefixStorage` exists for edge cases:
129
- *
130
- * - Sharing a Redis instance with a NON-bot system, and you want a
131
- * top-level prefix on top of the bot-id namespace.
132
- * - Migrating from an older deployment that used a custom prefix.
133
- *
134
- * In those cases the wrapper composes cleanly with `botSession`'s
135
- * internal prefix: final keys look like `${prefix}bot-<id>:<userId>`.
136
- */
137
- export declare const prefixStorage: (storage: Storage, prefix: string) => Storage;
138
88
  export type { PluginType };
139
89
  //# sourceMappingURL=kit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../../src/bot/kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,EAAE,KAAK,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,QAAQ,CAAC;AAM7C,MAAM,MAAM,oBAAoB,GAAG;IAClC,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC;IACtC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,aAAa,GACzB,KAAK,MAAM,EACX,OAAM,oBAAyB,KAC7B,OAAO,CAAC,IAAI,CAiFd,CAAC;AAIF,MAAM,MAAM,mBAAmB,GAAG;IACjC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAM,mBAAwB;;;;;;;;MAqB1D,CAAC;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,UAAU,GACtB,IAAI,GAAG,OAAO,EACd,GAAG,SAAS,MAAM,GAAG,SAAS,EAC9B,IAAI,SAAS,OAAO,GAAG,KAAK,EAE5B,MAAM,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,KACnC,UAAU,CAAC,OAAO,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CA0B5C,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,OAAO,EAAE,QAAQ,MAAM,KAAG,OAShE,CAAC;AAIF,YAAY,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../../src/bot/kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,QAAQ,CAAC;AAI7C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIzD,MAAM,MAAM,oBAAoB,GAAG;IAClC,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC;IACtC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,aAAa,GACzB,KAAK,MAAM,EACX,OAAM,oBAAyB,KAC7B,OAAO,CAAC,IAAI,CAmEd,CAAC;AAIF,MAAM,MAAM,mBAAmB,GAAG;IACjC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAM,mBAAwB;;;;;;;;MAqB1D,CAAC;AAIF,YAAY,EAAE,UAAU,EAAE,CAAC"}
package/dist/bot/kit.js CHANGED
@@ -1,48 +1,7 @@
1
- /**
2
- * Foundational helpers every bot wants.
3
- *
4
- * `gracefulStart(bot, opts?)` — wires SIGINT/SIGTERM to bot.stop(),
5
- * runs an optional shutdown hook, force-kills if it hangs. DMs the
6
- * admin on start/stop by default (`@<bot> started.` /
7
- * `@<bot> shutting down.`) when `KEV.TELEGRAM_ADMIN_ID` is set —
8
- * pass `notifyAdmin: false` to disable.
9
- *
10
- * `adminContext({ adminId? })` — reads admin Telegram id from KEV
11
- * (`TELEGRAM_ADMIN_ID`) with optional hardcoded fallback. Decorates
12
- * every context with `ctx.adminId` (number) and `ctx.isAdmin`
13
- * (boolean). Throws at startup if neither source provides an id.
14
- *
15
- * `botSession(opts)` — drop-in replacement for `@gramio/session`'s
16
- * `session()` that AUTOMATICALLY namespaces every key with the
17
- * calling bot's id (parsed from `ctx.bot.options.token`). When
18
- * several bots share one Redis instance, each writes to a disjoint
19
- * keyspace (`bot-<id>:<senderId>`) with no manual wiring. Every
20
- * plugin in this package (`accessControl`, `botMenu`,
21
- * `llmHistory`, …) derives the SAME prefix from `ctx.bot` for its
22
- * own storage access, so the whole package stays isolated by
23
- * construction. Use this instead of `session()` — full stop.
24
- *
25
- * Peer deps: `gramio`, `@gramio/session`, `@gramio/storage`.
26
- *
27
- * @example
28
- * import { Bot } from 'gramio'
29
- * import { redisStorage } from '@gramio/storage-redis'
30
- * import { adminContext, gracefulStart, botSession } from '@adriangalilea/utils/bot/kit'
31
- *
32
- * const storage = redisStorage() // share across bots, safe
33
- * const userSession = botSession({ storage, initial: () => ({}) })
34
- *
35
- * const bot = new Bot(process.env.BOT_TOKEN!)
36
- * .extend(adminContext({ adminId: 190202471 })) // KEV wins, 190… is fallback
37
- * .extend(userSession)
38
- * .command('whoami', (ctx) => ctx.send(`admin? ${ctx.isAdmin}`))
39
- *
40
- * await gracefulStart(bot, { onShutdown: () => db.end() })
41
- */
42
- import { session } from "@gramio/session";
43
1
  import { Plugin } from "gramio";
44
2
  import { kev } from "../platform/kev.js";
45
- import { botId } from "./keys.js";
3
+ import { notifyAdmins } from "./notify.js";
4
+ export { botSession, prefixStorage } from "./session.js";
46
5
  export const gracefulStart = async (bot, opts = {}) => {
47
6
  const log = opts.log === false ? () => { } : (opts.log ?? ((m) => console.log(m)));
48
7
  const forceMs = opts.forceExitAfterMs ?? 10_000;
@@ -93,28 +52,8 @@ export const gracefulStart = async (bot, opts = {}) => {
93
52
  "Pass a number explicitly, set the env var, or remove notifyAdmin.");
94
53
  }
95
54
  if (adminId) {
96
- bot.onStart(async ({ info }) => {
97
- try {
98
- await bot.api.sendMessage({
99
- chat_id: adminId,
100
- text: `@${info.username} started.`,
101
- });
102
- }
103
- catch (e) {
104
- console.error("[bot] notifyAdmin (start) failed", e);
105
- }
106
- });
107
- bot.onStop(async ({ info }) => {
108
- try {
109
- await bot.api.sendMessage({
110
- chat_id: adminId,
111
- text: `@${info.username} shutting down.`,
112
- });
113
- }
114
- catch (e) {
115
- console.error("[bot] notifyAdmin (stop) failed", e);
116
- }
117
- });
55
+ bot.onStart(({ info }) => notifyAdmins(bot, [adminId], `@${info.username} started.`));
56
+ bot.onStop(({ info }) => notifyAdmins(bot, [adminId], `@${info.username} shutting down.`));
118
57
  }
119
58
  await bot.start();
120
59
  };
@@ -136,81 +75,4 @@ export const adminContext = (opts = {}) => {
136
75
  isAdmin: "senderId" in ctx && ctx.senderId === adminId,
137
76
  }));
138
77
  };
139
- // ─── botSession ────────────────────────────────────────────────────
140
- /**
141
- * Drop-in replacement for `@gramio/session`'s `session()` that forces
142
- * the storage key to include the calling bot's id. Equivalent to
143
- * passing `getSessionKey: (ctx) => bot-${id}:${senderId}` explicitly,
144
- * but baked in so no consumer can forget.
145
- *
146
- * Returns a plain `@gramio/session` plugin — same shape, same derives,
147
- * fully interchangeable in `.extend(userSession)` chains and plugin
148
- * options like `accessControl({ session: userSession, … })`.
149
- *
150
- * `getSessionKey` can still be overridden for advanced cases (e.g. a
151
- * per-chat session in groups); the override is wrapped so it still
152
- * receives the `bot-<id>:` prefix. Don't override unless you know what
153
- * you're doing — every plugin in this package expects the
154
- * `bot-<id>:<userId>` shape for cross-user storage reads.
155
- *
156
- * @example
157
- * import { redisStorage } from '@gramio/storage-redis'
158
- * import { botSession } from '@adriangalilea/utils/bot/kit'
159
- *
160
- * const storage = redisStorage() // shared across N bots, safe
161
- *
162
- * const userSession = botSession({
163
- * storage,
164
- * key: 'session',
165
- * initial: () => ({}),
166
- * })
167
- *
168
- * bot.extend(userSession)
169
- */
170
- export const botSession = (opts) => {
171
- const userKeyer = opts.getSessionKey;
172
- return session({
173
- ...opts,
174
- // The session plugin types `getSessionKey` against `ContextType<BotLike, Events>`
175
- // — too narrow for our generic wrapper. We only need `bot.info.id` +
176
- // `senderId`, both of which gramio guarantees on every event the session
177
- // plugin processes (bot.info is populated by bot.start / bot.init before
178
- // any handler fires). Structural cast at the boundary keeps the public
179
- // API generic-friendly without forcing consumers to specify Event unions.
180
- getSessionKey: (async (ctxRaw) => {
181
- const ctx = ctxRaw;
182
- const prefix = `bot-${botId(ctx)}:`;
183
- if (userKeyer) {
184
- const inner = await userKeyer(ctx);
185
- return `${prefix}${inner}`;
186
- }
187
- return `${prefix}${ctx.senderId ?? ""}`;
188
- }),
189
- });
190
- };
191
- // ─── prefixStorage (escape hatch) ──────────────────────────────────
192
- /**
193
- * Wraps any `@gramio/storage` adapter so every key gets a fixed
194
- * `${prefix}` prepended.
195
- *
196
- * **You almost never need this.** The library's plugins are isolated
197
- * by bot id automatically when you wire `botSession` (see above) —
198
- * `prefixStorage` exists for edge cases:
199
- *
200
- * - Sharing a Redis instance with a NON-bot system, and you want a
201
- * top-level prefix on top of the bot-id namespace.
202
- * - Migrating from an older deployment that used a custom prefix.
203
- *
204
- * In those cases the wrapper composes cleanly with `botSession`'s
205
- * internal prefix: final keys look like `${prefix}bot-<id>:<userId>`.
206
- */
207
- export const prefixStorage = (storage, prefix) => {
208
- const k = (key) => `${prefix}${String(key)}`;
209
- return {
210
- get: (key) => storage.get(k(key)),
211
- has: (key) => storage.has(k(key)),
212
- set: (key, value) => storage.set(k(key), value),
213
- delete: (key) => storage.delete(k(key)),
214
- };
215
- };
216
78
  //# sourceMappingURL=kit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"kit.js","sourceRoot":"","sources":["../../src/bot/kit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kit.js","sourceRoot":"","sources":["../../src/bot/kit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Admin notifications — best-effort DMs to the bot's operator(s).
3
+ * Worker-safe: no env, no process; the caller passes the admin ids
4
+ * (from wherever its runtime keeps them: KEV on Node, D1/env on a
5
+ * Worker).
6
+ *
7
+ * Everything here is FIRE-AND-FORGET BY CONTRACT: a notification must
8
+ * never take the bot down, so failures are logged (console.error —
9
+ * visible in a Worker tail and a Node terminal alike) and swallowed.
10
+ *
11
+ * `notifyAdmins(bot, adminIds, text, extra?)` — DM each admin.
12
+ * `alertAdminError(bot, adminIds, label, error, throttle?)` — DM a
13
+ * truncated `🚨 label\nName: message`, rate-limited through a
14
+ * caller-owned throttle so a failure storm can't flood anyone.
15
+ * `alertThrottle(ms?)` — make that throttle (per-process on Node,
16
+ * per-isolate on a Worker; own one per subsystem if you want
17
+ * independent budgets).
18
+ *
19
+ * @example
20
+ * import { alertAdminError, alertThrottle, notifyAdmins } from '@adriangalilea/utils/bot/notify'
21
+ *
22
+ * const errorAlerts = alertThrottle() // 60s budget, module-level
23
+ * bot.onError(({ kind, error }) => void alertAdminError(bot, adminIds, `error [${kind}]`, error, errorAlerts))
24
+ * await notifyAdmins(bot, adminIds, `@${bot.info.username} deployed.`)
25
+ */
26
+ /** The one API surface a notification needs — satisfied by any gramio `Bot`. */
27
+ export type NotifyBot = {
28
+ api: {
29
+ sendMessage: (params: {
30
+ chat_id: number;
31
+ text: string;
32
+ }) => Promise<unknown>;
33
+ };
34
+ };
35
+ /**
36
+ * DM `text` to every admin id, best-effort: each failure is logged and
37
+ * swallowed (one unreachable admin must not block the rest, and a
38
+ * notification must never throw into the calling flow). `extra` merges
39
+ * into the sendMessage params (reply_markup, link_preview_options, …).
40
+ */
41
+ export declare function notifyAdmins(bot: NotifyBot, adminIds: readonly number[], text: string, extra?: Record<string, unknown>): Promise<void>;
42
+ /**
43
+ * Rate-limit state for {@link alertAdminError}, owned by the CALLER so
44
+ * scope is explicit (per-process on Node, per-isolate on a Worker) and
45
+ * independent subsystems can hold independent budgets.
46
+ */
47
+ export type AlertThrottle = {
48
+ ms: number;
49
+ lastAt: number;
50
+ };
51
+ /** A fresh throttle: at most one alert per `ms` (default 60s). */
52
+ export declare const alertThrottle: (ms?: number) => AlertThrottle;
53
+ /**
54
+ * DM the admins a truncated error (`🚨 label\nName: message`), gated by
55
+ * `throttle` so a failure storm sends one alert per window, not one per
56
+ * failure. No throttle = every call sends.
57
+ */
58
+ export declare function alertAdminError(bot: NotifyBot, adminIds: readonly number[], label: string, error: unknown, throttle?: AlertThrottle): Promise<void>;
59
+ //# sourceMappingURL=notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/bot/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG;IACvB,GAAG,EAAE;QACJ,WAAW,EAAE,CAAC,MAAM,EAAE;YACrB,OAAO,EAAE,MAAM,CAAC;YAChB,IAAI,EAAE,MAAM,CAAC;SACb,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KACvB,CAAC;CACF,CAAC;AAKF;;;;;GAKG;AACH,wBAAsB,YAAY,CACjC,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,kEAAkE;AAClE,eAAO,MAAM,aAAa,GAAI,WAAW,KAAG,aAG1C,CAAC;AAEH;;;;GAIG;AACH,wBAAsB,eAAe,CACpC,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,OAAO,EACd,QAAQ,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Admin notifications — best-effort DMs to the bot's operator(s).
3
+ * Worker-safe: no env, no process; the caller passes the admin ids
4
+ * (from wherever its runtime keeps them: KEV on Node, D1/env on a
5
+ * Worker).
6
+ *
7
+ * Everything here is FIRE-AND-FORGET BY CONTRACT: a notification must
8
+ * never take the bot down, so failures are logged (console.error —
9
+ * visible in a Worker tail and a Node terminal alike) and swallowed.
10
+ *
11
+ * `notifyAdmins(bot, adminIds, text, extra?)` — DM each admin.
12
+ * `alertAdminError(bot, adminIds, label, error, throttle?)` — DM a
13
+ * truncated `🚨 label\nName: message`, rate-limited through a
14
+ * caller-owned throttle so a failure storm can't flood anyone.
15
+ * `alertThrottle(ms?)` — make that throttle (per-process on Node,
16
+ * per-isolate on a Worker; own one per subsystem if you want
17
+ * independent budgets).
18
+ *
19
+ * @example
20
+ * import { alertAdminError, alertThrottle, notifyAdmins } from '@adriangalilea/utils/bot/notify'
21
+ *
22
+ * const errorAlerts = alertThrottle() // 60s budget, module-level
23
+ * bot.onError(({ kind, error }) => void alertAdminError(bot, adminIds, `error [${kind}]`, error, errorAlerts))
24
+ * await notifyAdmins(bot, adminIds, `@${bot.info.username} deployed.`)
25
+ */
26
+ /** Telegram's hard per-message limit; longer texts are truncated, never split. */
27
+ const MAX_CHARS = 4096;
28
+ /**
29
+ * DM `text` to every admin id, best-effort: each failure is logged and
30
+ * swallowed (one unreachable admin must not block the rest, and a
31
+ * notification must never throw into the calling flow). `extra` merges
32
+ * into the sendMessage params (reply_markup, link_preview_options, …).
33
+ */
34
+ export async function notifyAdmins(bot, adminIds, text, extra) {
35
+ const body = text.slice(0, MAX_CHARS);
36
+ await Promise.all(adminIds
37
+ .filter((id) => id > 0)
38
+ .map((id) => bot.api
39
+ .sendMessage({ ...extra, chat_id: id, text: body })
40
+ .catch((e) => console.error(`[bot/notify] admin ${id} failed:`, e))));
41
+ }
42
+ /** A fresh throttle: at most one alert per `ms` (default 60s). */
43
+ export const alertThrottle = (ms = 60_000) => ({
44
+ ms,
45
+ lastAt: 0,
46
+ });
47
+ /**
48
+ * DM the admins a truncated error (`🚨 label\nName: message`), gated by
49
+ * `throttle` so a failure storm sends one alert per window, not one per
50
+ * failure. No throttle = every call sends.
51
+ */
52
+ export async function alertAdminError(bot, adminIds, label, error, throttle) {
53
+ if (throttle) {
54
+ const now = Date.now();
55
+ if (now - throttle.lastAt < throttle.ms)
56
+ return;
57
+ throttle.lastAt = now;
58
+ }
59
+ const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
60
+ await notifyAdmins(bot, adminIds, `🚨 ${label}\n${message}`);
61
+ }
62
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/bot/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAYH,kFAAkF;AAClF,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,GAAc,EACd,QAA2B,EAC3B,IAAY,EACZ,KAA+B;IAE/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtC,MAAM,OAAO,CAAC,GAAG,CAChB,QAAQ;SACN,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;SACtB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CACX,GAAG,CAAC,GAAG;SACL,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SAClD,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CACpE,CACF,CAAC;AACH,CAAC;AASD,kEAAkE;AAClE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAE,GAAG,MAAM,EAAiB,EAAE,CAAC,CAAC;IAC7D,EAAE;IACF,MAAM,EAAE,CAAC;CACT,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,GAAc,EACd,QAA2B,EAC3B,KAAa,EACb,KAAc,EACd,QAAwB;IAExB,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,EAAE;YAAE,OAAO;QAChD,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC;IACvB,CAAC;IACD,MAAM,OAAO,GACZ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5E,MAAM,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,KAAK,OAAO,EAAE,CAAC,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Bot-namespaced session storage — worker-safe (no env, no process, no
3
+ * signals; runs identically on Node and Cloudflare Workers).
4
+ *
5
+ * `botSession(opts)` — drop-in replacement for `@gramio/session`'s
6
+ * `session()` that AUTOMATICALLY namespaces every key with the
7
+ * calling bot's id (parsed from `ctx.bot.options.token`). When
8
+ * several bots share one storage instance, each writes to a
9
+ * disjoint keyspace (`bot-<id>:<senderId>`) with no manual wiring.
10
+ * Every plugin in this package (`accessControl`, `botMenu`,
11
+ * `llmHistory`, …) derives the SAME prefix from `ctx.bot` for its
12
+ * own storage access, so the whole package stays isolated by
13
+ * construction. Use this instead of `session()` — full stop.
14
+ *
15
+ * `prefixStorage(storage, prefix)` — escape hatch that prepends a
16
+ * fixed prefix to every key of any `@gramio/storage` adapter.
17
+ *
18
+ * Peer deps: `gramio`, `@gramio/session`, `@gramio/storage`.
19
+ *
20
+ * @example
21
+ * import { redisStorage } from '@gramio/storage-redis'
22
+ * import { botSession } from '@adriangalilea/utils/bot/session'
23
+ *
24
+ * const storage = redisStorage() // share across bots, safe
25
+ * const userSession = botSession({ storage, initial: () => ({}) })
26
+ * bot.extend(userSession)
27
+ */
28
+ import { type SessionOptions, session } from "@gramio/session";
29
+ import type { Storage } from "@gramio/storage";
30
+ /**
31
+ * Drop-in replacement for `@gramio/session`'s `session()` that forces
32
+ * the storage key to include the calling bot's id. Equivalent to
33
+ * passing `getSessionKey: (ctx) => bot-${id}:${senderId}` explicitly,
34
+ * but baked in so no consumer can forget.
35
+ *
36
+ * Returns a plain `@gramio/session` plugin — same shape, same derives,
37
+ * fully interchangeable in `.extend(userSession)` chains and plugin
38
+ * options like `accessControl({ session: userSession, … })`.
39
+ *
40
+ * `getSessionKey` can still be overridden for advanced cases (e.g. a
41
+ * per-chat session in groups); the override is wrapped so it still
42
+ * receives the `bot-<id>:` prefix. Don't override unless you know what
43
+ * you're doing — every plugin in this package expects the
44
+ * `bot-<id>:<userId>` shape for cross-user storage reads.
45
+ */
46
+ export declare const botSession: <Data = unknown, Key extends string = "session", Lazy extends boolean = false>(opts: SessionOptions<Data, Key, Lazy>) => ReturnType<typeof session<Data, Key, Lazy>>;
47
+ /**
48
+ * Wraps any `@gramio/storage` adapter so every key gets a fixed
49
+ * `${prefix}` prepended.
50
+ *
51
+ * **You almost never need this.** The library's plugins are isolated
52
+ * by bot id automatically when you wire `botSession` (see above) —
53
+ * `prefixStorage` exists for edge cases:
54
+ *
55
+ * - Sharing a Redis instance with a NON-bot system, and you want a
56
+ * top-level prefix on top of the bot-id namespace.
57
+ * - Migrating from an older deployment that used a custom prefix.
58
+ *
59
+ * In those cases the wrapper composes cleanly with `botSession`'s
60
+ * internal prefix: final keys look like `${prefix}bot-<id>:<userId>`.
61
+ */
62
+ export declare const prefixStorage: (storage: Storage, prefix: string) => Storage;
63
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/bot/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,KAAK,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAK/C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,GACtB,IAAI,GAAG,OAAO,EACd,GAAG,SAAS,MAAM,GAAG,SAAS,EAC9B,IAAI,SAAS,OAAO,GAAG,KAAK,EAE5B,MAAM,cAAc,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,KACnC,UAAU,CAAC,OAAO,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CA0B5C,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,OAAO,EAAE,QAAQ,MAAM,KAAG,OAShE,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Bot-namespaced session storage — worker-safe (no env, no process, no
3
+ * signals; runs identically on Node and Cloudflare Workers).
4
+ *
5
+ * `botSession(opts)` — drop-in replacement for `@gramio/session`'s
6
+ * `session()` that AUTOMATICALLY namespaces every key with the
7
+ * calling bot's id (parsed from `ctx.bot.options.token`). When
8
+ * several bots share one storage instance, each writes to a
9
+ * disjoint keyspace (`bot-<id>:<senderId>`) with no manual wiring.
10
+ * Every plugin in this package (`accessControl`, `botMenu`,
11
+ * `llmHistory`, …) derives the SAME prefix from `ctx.bot` for its
12
+ * own storage access, so the whole package stays isolated by
13
+ * construction. Use this instead of `session()` — full stop.
14
+ *
15
+ * `prefixStorage(storage, prefix)` — escape hatch that prepends a
16
+ * fixed prefix to every key of any `@gramio/storage` adapter.
17
+ *
18
+ * Peer deps: `gramio`, `@gramio/session`, `@gramio/storage`.
19
+ *
20
+ * @example
21
+ * import { redisStorage } from '@gramio/storage-redis'
22
+ * import { botSession } from '@adriangalilea/utils/bot/session'
23
+ *
24
+ * const storage = redisStorage() // share across bots, safe
25
+ * const userSession = botSession({ storage, initial: () => ({}) })
26
+ * bot.extend(userSession)
27
+ */
28
+ import { session } from "@gramio/session";
29
+ import { botId } from "./keys.js";
30
+ // ─── botSession ────────────────────────────────────────────────────
31
+ /**
32
+ * Drop-in replacement for `@gramio/session`'s `session()` that forces
33
+ * the storage key to include the calling bot's id. Equivalent to
34
+ * passing `getSessionKey: (ctx) => bot-${id}:${senderId}` explicitly,
35
+ * but baked in so no consumer can forget.
36
+ *
37
+ * Returns a plain `@gramio/session` plugin — same shape, same derives,
38
+ * fully interchangeable in `.extend(userSession)` chains and plugin
39
+ * options like `accessControl({ session: userSession, … })`.
40
+ *
41
+ * `getSessionKey` can still be overridden for advanced cases (e.g. a
42
+ * per-chat session in groups); the override is wrapped so it still
43
+ * receives the `bot-<id>:` prefix. Don't override unless you know what
44
+ * you're doing — every plugin in this package expects the
45
+ * `bot-<id>:<userId>` shape for cross-user storage reads.
46
+ */
47
+ export const botSession = (opts) => {
48
+ const userKeyer = opts.getSessionKey;
49
+ return session({
50
+ ...opts,
51
+ // The session plugin types `getSessionKey` against `ContextType<BotLike, Events>`
52
+ // — too narrow for our generic wrapper. We only need `bot.info.id` +
53
+ // `senderId`, both of which gramio guarantees on every event the session
54
+ // plugin processes (bot.info is populated by bot.start / bot.init before
55
+ // any handler fires). Structural cast at the boundary keeps the public
56
+ // API generic-friendly without forcing consumers to specify Event unions.
57
+ getSessionKey: (async (ctxRaw) => {
58
+ const ctx = ctxRaw;
59
+ const prefix = `bot-${botId(ctx)}:`;
60
+ if (userKeyer) {
61
+ const inner = await userKeyer(ctx);
62
+ return `${prefix}${inner}`;
63
+ }
64
+ return `${prefix}${ctx.senderId ?? ""}`;
65
+ }),
66
+ });
67
+ };
68
+ // ─── prefixStorage (escape hatch) ──────────────────────────────────
69
+ /**
70
+ * Wraps any `@gramio/storage` adapter so every key gets a fixed
71
+ * `${prefix}` prepended.
72
+ *
73
+ * **You almost never need this.** The library's plugins are isolated
74
+ * by bot id automatically when you wire `botSession` (see above) —
75
+ * `prefixStorage` exists for edge cases:
76
+ *
77
+ * - Sharing a Redis instance with a NON-bot system, and you want a
78
+ * top-level prefix on top of the bot-id namespace.
79
+ * - Migrating from an older deployment that used a custom prefix.
80
+ *
81
+ * In those cases the wrapper composes cleanly with `botSession`'s
82
+ * internal prefix: final keys look like `${prefix}bot-<id>:<userId>`.
83
+ */
84
+ export const prefixStorage = (storage, prefix) => {
85
+ const k = (key) => `${prefix}${String(key)}`;
86
+ return {
87
+ get: (key) => storage.get(k(key)),
88
+ has: (key) => storage.has(k(key)),
89
+ set: (key, value) => storage.set(k(key), value),
90
+ delete: (key) => storage.delete(k(key)),
91
+ };
92
+ };
93
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/bot/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAuB,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAElC,sEAAsE;AAEtE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAKzB,IAAqC,EACS,EAAE;IAChD,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC;IAKrC,OAAO,OAAO,CAAkB;QAC/B,GAAG,IAAI;QACP,kFAAkF;QAClF,qEAAqE;QACrE,yEAAyE;QACzE,yEAAyE;QACzE,uEAAuE;QACvE,0EAA0E;QAC1E,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YAChC,MAAM,GAAG,GAAG,MAAgB,CAAC;YAC7B,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;YACpC,IAAI,SAAS,EAAE,CAAC;gBACf,MAAM,KAAK,GAAG,MACb,SACA,CAAC,GAAG,CAAC,CAAC;gBACP,OAAO,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC;YAC5B,CAAC;YACD,OAAO,GAAG,MAAM,GAAG,GAAG,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACzC,CAAC,CAAqD;KACtD,CAAgD,CAAC;AACnD,CAAC,CAAC;AAEF,sEAAsE;AAEtE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAgB,EAAE,MAAc,EAAW,EAAE;IAC1E,MAAM,CAAC,GAAG,CAAC,GAA6B,EAAU,EAAE,CACnD,GAAG,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3B,OAAO;QACN,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACjC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACjC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;QAC/C,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;KACvC,CAAC;AACH,CAAC,CAAC"}
package/dist/browser.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * These utilities do not depend on Node.js APIs and can run in the browser.
4
4
  *
5
5
  * Usage in Next.js client components:
6
- * import { log, format, currency } from '@adriangalilea/utils/browser'
6
+ * import { log, compact, currency } from '@adriangalilea/utils/browser'
7
7
  */
8
8
  export * from "./offensive.js";
9
9
  export type { RuntimeCapabilities } from "./runtime.js";
package/dist/browser.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * These utilities do not depend on Node.js APIs and can run in the browser.
4
4
  *
5
5
  * Usage in Next.js client components:
6
- * import { log, format, currency } from '@adriangalilea/utils/browser'
6
+ * import { log, compact, currency } from '@adriangalilea/utils/browser'
7
7
  */
8
8
  // Offensive programming - adapted for browser (throws instead of process.exit)
9
9
  export * from "./offensive.js";
@@ -1,19 +1,11 @@
1
1
  /**
2
- * Set of all known cryptocurrency symbols
3
- * Contains ${symbolCount} symbols from CoinGecko API
4
- * Last updated: ${lastUpdated}
5
- * Run 'npm run update-crypto' to refresh
6
- */
7
- export declare const cryptoSymbols: Set<string>;
8
- /**
9
- * Check if a symbol is a known cryptocurrency
10
- * @param symbol - The symbol to check (case-insensitive)
11
- * @returns true if the symbol is in the crypto list
2
+ * Check if a symbol is a known cryptocurrency (case-insensitive).
3
+ * Backed by the CoinGecko list in crypto-symbols-data.ts —
4
+ * run 'npm run update-crypto' to refresh.
12
5
  *
13
6
  * @example
14
7
  * isCryptoSymbol("BTC") // true
15
8
  * isCryptoSymbol("btc") // true
16
- * isCryptoSymbol("ETH") // true
17
9
  * isCryptoSymbol("DOGE") // true
18
10
  */
19
11
  export declare function isCryptoSymbol(symbol: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"crypto-symbols.d.ts","sourceRoot":"","sources":["../../../src/universal/currency/crypto-symbols.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,aAAa,aAA6B,CAAC;AAExD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEtD"}
1
+ {"version":3,"file":"crypto-symbols.d.ts","sourceRoot":"","sources":["../../../src/universal/currency/crypto-symbols.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGtD"}
@@ -1,23 +1,20 @@
1
1
  import { cryptoSymbolsData } from "./crypto-symbols-data.js";
2
+ // The Set builds on FIRST LOOKUP, not at import: the dataset is ~133KB and
3
+ // most consumers of the currency module never ask "is this crypto?". Laziness
4
+ // costs one `??=`; eagerness costs every import a startup parse of 8k symbols.
5
+ let cryptoSymbols;
2
6
  /**
3
- * Set of all known cryptocurrency symbols
4
- * Contains ${symbolCount} symbols from CoinGecko API
5
- * Last updated: ${lastUpdated}
6
- * Run 'npm run update-crypto' to refresh
7
- */
8
- export const cryptoSymbols = new Set(cryptoSymbolsData);
9
- /**
10
- * Check if a symbol is a known cryptocurrency
11
- * @param symbol - The symbol to check (case-insensitive)
12
- * @returns true if the symbol is in the crypto list
7
+ * Check if a symbol is a known cryptocurrency (case-insensitive).
8
+ * Backed by the CoinGecko list in crypto-symbols-data.ts —
9
+ * run 'npm run update-crypto' to refresh.
13
10
  *
14
11
  * @example
15
12
  * isCryptoSymbol("BTC") // true
16
13
  * isCryptoSymbol("btc") // true
17
- * isCryptoSymbol("ETH") // true
18
14
  * isCryptoSymbol("DOGE") // true
19
15
  */
20
16
  export function isCryptoSymbol(symbol) {
17
+ cryptoSymbols ??= new Set(cryptoSymbolsData);
21
18
  return cryptoSymbols.has(symbol.toUpperCase());
22
19
  }
23
20
  //# sourceMappingURL=crypto-symbols.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"crypto-symbols.js","sourceRoot":"","sources":["../../../src/universal/currency/crypto-symbols.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAExD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC5C,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"crypto-symbols.js","sourceRoot":"","sources":["../../../src/universal/currency/crypto-symbols.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,2EAA2E;AAC3E,8EAA8E;AAC9E,+EAA+E;AAC/E,IAAI,aAAsC,CAAC;AAE3C;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC5C,aAAa,KAAK,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC7C,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;AAChD,CAAC"}
@@ -42,6 +42,17 @@ export declare function percentageDiff(a: number, b: number): number;
42
42
  export declare function basisPointsToPercent(bps: number): number;
43
43
  export declare function percentToBasisPoints(percent: number): number;
44
44
  export declare function formatBasisPoints(bps: number): string;
45
+ /** $1234.56 with optimal decimals; sign before the $. */
46
+ export declare function usd(value: number): string;
47
+ /** Bitcoin with optimal decimals: 0.00001234 ₿ */
48
+ export declare function btc(value: number): string;
49
+ /** Ethereum with optimal decimals: 0.123 Ξ */
50
+ export declare function eth(value: number): string;
51
+ /**
52
+ * Format a value in any currency: symbol before for fiat/stablecoins
53
+ * (`$12.34`), after for crypto (`0.5 ₿`), optimal decimals either way.
54
+ */
55
+ export declare function money(value: number, currencyCode: string): string;
45
56
  export declare const currency: {
46
57
  getSymbol: typeof getSymbol;
47
58
  getOptimalDecimals: typeof getOptimalDecimals;
@@ -57,6 +68,10 @@ export declare const currency: {
57
68
  basisPointsToPercent: typeof basisPointsToPercent;
58
69
  percentToBasisPoints: typeof percentToBasisPoints;
59
70
  formatBasisPoints: typeof formatBasisPoints;
71
+ usd: typeof usd;
72
+ btc: typeof btc;
73
+ eth: typeof eth;
74
+ money: typeof money;
60
75
  symbols: {
61
76
  readonly BTC: "₿";
62
77
  readonly XBT: "₿";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/universal/currency/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BlB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,eAAe,GAAG,MAAM,CAAC;AAEjE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GAClB,MAAM,CAgER;AA4CD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA2BhD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAatD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmB9C;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAclD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkC5C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM3E;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgBpB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/universal/currency/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BlB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,eAAe,GAAG,MAAM,CAAC;AAEjE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GAClB,MAAM,CAgER;AA4CD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA2BhD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAatD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmB9C;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAclD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkC5C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM3E;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAOD,yDAAyD;AACzD,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIzC;AAED,kDAAkD;AAClD,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,8CAA8C;AAC9C,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAQjE;AAED,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBpB,CAAC"}
@@ -293,6 +293,37 @@ export function percentToBasisPoints(percent) {
293
293
  export function formatBasisPoints(bps) {
294
294
  return `${bps} bps`;
295
295
  }
296
+ // ── money formatting ───────────────────────────────────────────────
297
+ // Lives HERE, not in universal/format: formatting money needs the symbol +
298
+ // optimal-decimals knowledge (and, transitively, the crypto dataset), and the
299
+ // pure format module must stay free of that weight.
300
+ /** $1234.56 with optimal decimals; sign before the $. */
301
+ export function usd(value) {
302
+ const decimals = getOptimalDecimals(value, "USD");
303
+ const formatted = Math.abs(value).toFixed(decimals);
304
+ return value < 0 ? `-$${formatted}` : `$${formatted}`;
305
+ }
306
+ /** Bitcoin with optimal decimals: 0.00001234 ₿ */
307
+ export function btc(value) {
308
+ return `${value.toFixed(getOptimalDecimals(value, "BTC"))} ₿`;
309
+ }
310
+ /** Ethereum with optimal decimals: 0.123 Ξ */
311
+ export function eth(value) {
312
+ return `${value.toFixed(getOptimalDecimals(value, "ETH"))} Ξ`;
313
+ }
314
+ /**
315
+ * Format a value in any currency: symbol before for fiat/stablecoins
316
+ * (`$12.34`), after for crypto (`0.5 ₿`), optimal decimals either way.
317
+ */
318
+ export function money(value, currencyCode) {
319
+ const decimals = getOptimalDecimals(value, currencyCode);
320
+ const symbol = getSymbol(currencyCode);
321
+ const formatted = Math.abs(value).toFixed(decimals);
322
+ if (isFiat(currencyCode) || isStablecoin(currencyCode)) {
323
+ return value < 0 ? `-${symbol}${formatted}` : `${symbol}${formatted}`;
324
+ }
325
+ return value < 0 ? `-${formatted} ${symbol}` : `${formatted} ${symbol}`;
326
+ }
296
327
  export const currency = {
297
328
  getSymbol,
298
329
  getOptimalDecimals,
@@ -308,6 +339,10 @@ export const currency = {
308
339
  basisPointsToPercent,
309
340
  percentToBasisPoints,
310
341
  formatBasisPoints,
342
+ usd,
343
+ btc,
344
+ eth,
345
+ money,
311
346
  symbols: CurrencySymbols,
312
347
  };
313
348
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/universal/currency/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/universal/currency/index.ts"],"names":[],"mappings":"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"}
@@ -1,40 +1,16 @@
1
1
  /**
2
- * Format utilities for numbers and currencies
2
+ * Pure number formatting. No currency coupling — money formatting (usd, btc,
3
+ * money(value, code), …) lives in ./currency, which owns the symbol/decimals
4
+ * knowledge and its 133KB crypto-symbol dataset. Keeping this module pure is
5
+ * what lets a browser bundle import `compact` without shipping that dataset.
6
+ *
7
+ * Named exports only (no namespace object): a namespace keeps every function
8
+ * — and its imports — alive in the bundle; named imports tree-shake.
3
9
  */
4
- declare class FormatOps {
5
- /**
6
- * Format a number with specified decimal places
7
- */
8
- number(value: number, decimals: number): string;
9
- /**
10
- * Format a value as USD with optimal decimals
11
- */
12
- usd(value: number): string;
13
- /**
14
- * Format a value as Bitcoin with optimal decimals
15
- */
16
- btc(value: number): string;
17
- /**
18
- * Format a value as Ethereum with optimal decimals
19
- */
20
- eth(value: number): string;
21
- /**
22
- * Auto format a value with the appropriate currency symbol and decimals
23
- */
24
- auto(value: number, currencyCode: string): string;
25
- /**
26
- * Format a percentage with smart decimal places
27
- */
28
- percentage(value: number): string;
29
- /**
30
- * Format a value with thousands separators
31
- */
32
- withCommas(value: number, decimals?: number): string;
33
- /**
34
- * Format a value in compact notation (1.2K, 3.4M, etc)
35
- */
36
- compact(value: number): string;
37
- }
38
- export declare const format: FormatOps;
39
- export {};
10
+ /** Compact notation: 1.2K, 3.4M, … (Intl, en). */
11
+ export declare function compact(value: number): string;
12
+ /** Percentage with smart decimals: 2 below 0.1, 0 at ≥100, else 1. */
13
+ export declare function percentage(value: number): string;
14
+ /** Thousands separators: 1,234,567.89 (optionally fixed decimals). */
15
+ export declare function withCommas(value: number, decimals?: number): string;
40
16
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/universal/format.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,cAAM,SAAS;IACd;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAI/C;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAM1B;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAK1B;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAK1B;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAajD;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAUjC;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAQpD;;OAEG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAO9B;AAED,eAAO,MAAM,MAAM,WAAkB,CAAC"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/universal/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,kDAAkD;AAClD,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK7C;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAMnE"}
@@ -1,83 +1,35 @@
1
- import { getOptimalDecimals, getSymbol, isFiat, isStablecoin, } from "./currency/index.js";
2
1
  /**
3
- * Format utilities for numbers and currencies
2
+ * Pure number formatting. No currency coupling — money formatting (usd, btc,
3
+ * money(value, code), …) lives in ./currency, which owns the symbol/decimals
4
+ * knowledge and its 133KB crypto-symbol dataset. Keeping this module pure is
5
+ * what lets a browser bundle import `compact` without shipping that dataset.
6
+ *
7
+ * Named exports only (no namespace object): a namespace keeps every function
8
+ * — and its imports — alive in the bundle; named imports tree-shake.
4
9
  */
5
- class FormatOps {
6
- /**
7
- * Format a number with specified decimal places
8
- */
9
- number(value, decimals) {
10
- return value.toFixed(decimals);
11
- }
12
- /**
13
- * Format a value as USD with optimal decimals
14
- */
15
- usd(value) {
16
- const decimals = getOptimalDecimals(value, "USD");
17
- const formatted = Math.abs(value).toFixed(decimals);
18
- return value < 0 ? `-$${formatted}` : `$${formatted}`;
19
- }
20
- /**
21
- * Format a value as Bitcoin with optimal decimals
22
- */
23
- btc(value) {
24
- const decimals = getOptimalDecimals(value, "BTC");
25
- return `${value.toFixed(decimals)} ₿`;
26
- }
27
- /**
28
- * Format a value as Ethereum with optimal decimals
29
- */
30
- eth(value) {
31
- const decimals = getOptimalDecimals(value, "ETH");
32
- return `${value.toFixed(decimals)} Ξ`;
33
- }
34
- /**
35
- * Auto format a value with the appropriate currency symbol and decimals
36
- */
37
- auto(value, currencyCode) {
38
- const decimals = getOptimalDecimals(value, currencyCode);
39
- const symbol = getSymbol(currencyCode);
40
- const formatted = Math.abs(value).toFixed(decimals);
41
- // Put symbol before for fiat/stablecoins, after for crypto
42
- if (isFiat(currencyCode) || isStablecoin(currencyCode)) {
43
- return value < 0 ? `-${symbol}${formatted}` : `${symbol}${formatted}`;
44
- }
45
- else {
46
- return value < 0 ? `-${formatted} ${symbol}` : `${formatted} ${symbol}`;
47
- }
48
- }
49
- /**
50
- * Format a percentage with smart decimal places
51
- */
52
- percentage(value) {
53
- let decimals = 1;
54
- if (Math.abs(value) < 0.1) {
55
- decimals = 2;
56
- }
57
- else if (Math.abs(value) >= 100) {
58
- decimals = 0;
59
- }
60
- return `${value.toFixed(decimals)}%`;
61
- }
62
- /**
63
- * Format a value with thousands separators
64
- */
65
- withCommas(value, decimals) {
66
- const fixed = decimals !== undefined ? value.toFixed(decimals) : value.toString();
67
- const parts = fixed.split(".");
68
- parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
69
- return parts.join(".");
10
+ /** Compact notation: 1.2K, 3.4M, … (Intl, en). */
11
+ export function compact(value) {
12
+ return new Intl.NumberFormat("en", {
13
+ notation: "compact",
14
+ maximumFractionDigits: 1,
15
+ }).format(value);
16
+ }
17
+ /** Percentage with smart decimals: 2 below 0.1, 0 at ≥100, else 1. */
18
+ export function percentage(value) {
19
+ let decimals = 1;
20
+ if (Math.abs(value) < 0.1) {
21
+ decimals = 2;
70
22
  }
71
- /**
72
- * Format a value in compact notation (1.2K, 3.4M, etc)
73
- */
74
- compact(value) {
75
- const formatter = new Intl.NumberFormat("en", {
76
- notation: "compact",
77
- maximumFractionDigits: 1,
78
- });
79
- return formatter.format(value);
23
+ else if (Math.abs(value) >= 100) {
24
+ decimals = 0;
80
25
  }
26
+ return `${value.toFixed(decimals)}%`;
27
+ }
28
+ /** Thousands separators: 1,234,567.89 (optionally fixed decimals). */
29
+ export function withCommas(value, decimals) {
30
+ const fixed = decimals !== undefined ? value.toFixed(decimals) : value.toString();
31
+ const parts = fixed.split(".");
32
+ parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
33
+ return parts.join(".");
81
34
  }
82
- export const format = new FormatOps();
83
35
  //# sourceMappingURL=format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/universal/format.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/universal/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,kDAAkD;AAClD,MAAM,UAAU,OAAO,CAAC,KAAa;IACpC,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;QAClC,QAAQ,EAAE,SAAS;QACnB,qBAAqB,EAAE,CAAC;KACxB,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;QAC3B,QAAQ,GAAG,CAAC,CAAC;IACd,CAAC;SAAM,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC;QACnC,QAAQ,GAAG,CAAC,CAAC;IACd,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;AACtC,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,QAAiB;IAC1D,MAAM,KAAK,GACV,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IACrE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adriangalilea/utils",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "TypeScript utilities - logger, currency, formatter, GramIO bot plugins, and more",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -87,6 +87,14 @@
87
87
  "./bot/callbacks": {
88
88
  "types": "./dist/bot/callbacks.d.ts",
89
89
  "default": "./dist/bot/callbacks.js"
90
+ },
91
+ "./bot/session": {
92
+ "types": "./dist/bot/session.d.ts",
93
+ "default": "./dist/bot/session.js"
94
+ },
95
+ "./bot/notify": {
96
+ "types": "./dist/bot/notify.d.ts",
97
+ "default": "./dist/bot/notify.js"
90
98
  }
91
99
  },
92
100
  "files": [
@@ -170,5 +178,6 @@
170
178
  "bugs": {
171
179
  "url": "https://github.com/adriangalilea/ts-utils/issues"
172
180
  },
173
- "homepage": "https://github.com/adriangalilea/ts-utils#readme"
181
+ "homepage": "https://github.com/adriangalilea/ts-utils#readme",
182
+ "sideEffects": false
174
183
  }