@xlsft/grammy-reactive 0.7.34

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 (187) hide show
  1. package/LICENSE +7 -0
  2. package/dist/jsx/index.d.ts +3 -0
  3. package/dist/jsx/index.d.ts.map +1 -0
  4. package/dist/jsx/index.js +2 -0
  5. package/dist/jsx/index.js.map +1 -0
  6. package/dist/jsx/jsx-dev-runtime.d.ts +2 -0
  7. package/dist/jsx/jsx-dev-runtime.d.ts.map +1 -0
  8. package/dist/jsx/jsx-dev-runtime.js +2 -0
  9. package/dist/jsx/jsx-dev-runtime.js.map +1 -0
  10. package/dist/jsx/jsx-runtime.d.ts +2 -0
  11. package/dist/jsx/jsx-runtime.d.ts.map +1 -0
  12. package/dist/jsx/jsx-runtime.js +2 -0
  13. package/dist/jsx/jsx-runtime.js.map +1 -0
  14. package/dist/jsx/runtime/jsx.d.ts +10 -0
  15. package/dist/jsx/runtime/jsx.d.ts.map +1 -0
  16. package/dist/jsx/runtime/jsx.errors.d.ts +4 -0
  17. package/dist/jsx/runtime/jsx.errors.d.ts.map +1 -0
  18. package/dist/jsx/runtime/jsx.errors.js +7 -0
  19. package/dist/jsx/runtime/jsx.errors.js.map +1 -0
  20. package/dist/jsx/runtime/jsx.js +209 -0
  21. package/dist/jsx/runtime/jsx.js.map +1 -0
  22. package/dist/jsx/runtime/jsx.runtime.d.ts +18 -0
  23. package/dist/jsx/runtime/jsx.runtime.d.ts.map +1 -0
  24. package/dist/jsx/runtime/jsx.runtime.js +3 -0
  25. package/dist/jsx/runtime/jsx.runtime.js.map +1 -0
  26. package/dist/lib/helpers/context.helper.d.ts +3 -0
  27. package/dist/lib/helpers/context.helper.d.ts.map +1 -0
  28. package/dist/lib/helpers/index.d.ts +2 -0
  29. package/dist/lib/helpers/index.d.ts.map +1 -0
  30. package/dist/lib/index.d.ts +12 -0
  31. package/dist/lib/index.d.ts.map +1 -0
  32. package/dist/lib/index.js +45 -0
  33. package/dist/lib/index.js.map +1 -0
  34. package/dist/lib/jsx-runtime.d.ts +2 -0
  35. package/dist/lib/jsx-runtime.d.ts.map +1 -0
  36. package/dist/lib/plugin/index.d.ts +4 -0
  37. package/dist/lib/plugin/index.d.ts.map +1 -0
  38. package/dist/lib/plugin/index.js +40 -0
  39. package/dist/lib/plugin/index.js.map +1 -0
  40. package/dist/lib/render/components/Error.d.ts +26 -0
  41. package/dist/lib/render/components/Error.d.ts.map +1 -0
  42. package/dist/lib/render/components/Error.js +25 -0
  43. package/dist/lib/render/components/Error.js.map +1 -0
  44. package/dist/lib/render/message.render.d.ts +40 -0
  45. package/dist/lib/render/message.render.d.ts.map +1 -0
  46. package/dist/lib/render/message.render.js +60 -0
  47. package/dist/lib/render/message.render.js.map +1 -0
  48. package/dist/lib/render/node/fragmemt.render.d.ts +34 -0
  49. package/dist/lib/render/node/fragmemt.render.d.ts.map +1 -0
  50. package/dist/lib/render/node/fragmemt.render.js +56 -0
  51. package/dist/lib/render/node/fragmemt.render.js.map +1 -0
  52. package/dist/lib/render/node/intrinsic.render.d.ts +37 -0
  53. package/dist/lib/render/node/intrinsic.render.d.ts.map +1 -0
  54. package/dist/lib/render/node/intrinsic.render.js +167 -0
  55. package/dist/lib/render/node/intrinsic.render.js.map +1 -0
  56. package/dist/lib/render/node/plain.render.d.ts +27 -0
  57. package/dist/lib/render/node/plain.render.d.ts.map +1 -0
  58. package/dist/lib/render/node/plain.render.js +32 -0
  59. package/dist/lib/render/node/plain.render.js.map +1 -0
  60. package/dist/lib/render/tag.render.d.ts +24 -0
  61. package/dist/lib/render/tag.render.d.ts.map +1 -0
  62. package/dist/lib/render/tag.render.js +27 -0
  63. package/dist/lib/render/tag.render.js.map +1 -0
  64. package/dist/lib/state/create.state.d.ts +8 -0
  65. package/dist/lib/state/create.state.d.ts.map +1 -0
  66. package/dist/lib/state/create.state.js +61 -0
  67. package/dist/lib/state/create.state.js.map +1 -0
  68. package/dist/lib/state/events/onclick.event.d.ts +21 -0
  69. package/dist/lib/state/events/onclick.event.d.ts.map +1 -0
  70. package/dist/lib/state/events/onclick.event.js +36 -0
  71. package/dist/lib/state/events/onclick.event.js.map +1 -0
  72. package/dist/lib/state/hooks/callback.hooks.d.ts +2 -0
  73. package/dist/lib/state/hooks/callback.hooks.d.ts.map +1 -0
  74. package/dist/lib/state/hooks/create.d.ts +7 -0
  75. package/dist/lib/state/hooks/create.d.ts.map +1 -0
  76. package/dist/lib/state/hooks/create.hooks.d.ts +29 -0
  77. package/dist/lib/state/hooks/create.hooks.d.ts.map +1 -0
  78. package/dist/lib/state/hooks/create.hooks.js +38 -0
  79. package/dist/lib/state/hooks/create.hooks.js.map +1 -0
  80. package/dist/lib/state/hooks/effect.hooks.d.ts +5 -0
  81. package/dist/lib/state/hooks/effect.hooks.d.ts.map +1 -0
  82. package/dist/lib/state/hooks/get.hooks.d.ts +25 -0
  83. package/dist/lib/state/hooks/get.hooks.d.ts.map +1 -0
  84. package/dist/lib/state/hooks/get.hooks.js +33 -0
  85. package/dist/lib/state/hooks/get.hooks.js.map +1 -0
  86. package/dist/lib/state/hooks/index.d.ts +7 -0
  87. package/dist/lib/state/hooks/index.d.ts.map +1 -0
  88. package/dist/lib/state/hooks/memo.hooks.d.ts +2 -0
  89. package/dist/lib/state/hooks/memo.hooks.d.ts.map +1 -0
  90. package/dist/lib/state/hooks/reducer.hooks.d.ts +3 -0
  91. package/dist/lib/state/hooks/reducer.hooks.d.ts.map +1 -0
  92. package/dist/lib/state/hooks/ref.hooks.d.ts +4 -0
  93. package/dist/lib/state/hooks/ref.hooks.d.ts.map +1 -0
  94. package/dist/lib/state/hooks/reset.hooks.d.ts +16 -0
  95. package/dist/lib/state/hooks/reset.hooks.d.ts.map +1 -0
  96. package/dist/lib/state/hooks/reset.hooks.js +19 -0
  97. package/dist/lib/state/hooks/reset.hooks.js.map +1 -0
  98. package/dist/lib/state/hooks/set.hooks.d.ts +23 -0
  99. package/dist/lib/state/hooks/set.hooks.d.ts.map +1 -0
  100. package/dist/lib/state/hooks/set.hooks.js +28 -0
  101. package/dist/lib/state/hooks/set.hooks.js.map +1 -0
  102. package/dist/lib/state/hooks/state.hooks.d.ts +50 -0
  103. package/dist/lib/state/hooks/state.hooks.d.ts.map +1 -0
  104. package/dist/lib/state/lifecycle/error.state.d.ts +7 -0
  105. package/dist/lib/state/lifecycle/error.state.d.ts.map +1 -0
  106. package/dist/lib/state/lifecycle/error.state.js +55 -0
  107. package/dist/lib/state/lifecycle/error.state.js.map +1 -0
  108. package/dist/lib/state/lifecycle/mount.state.d.ts +10 -0
  109. package/dist/lib/state/lifecycle/mount.state.d.ts.map +1 -0
  110. package/dist/lib/state/lifecycle/mount.state.js +84 -0
  111. package/dist/lib/state/lifecycle/mount.state.js.map +1 -0
  112. package/dist/lib/state/lifecycle/rerender.state.d.ts +10 -0
  113. package/dist/lib/state/lifecycle/rerender.state.d.ts.map +1 -0
  114. package/dist/lib/state/lifecycle/rerender.state.js +86 -0
  115. package/dist/lib/state/lifecycle/rerender.state.js.map +1 -0
  116. package/dist/lib/state/lifecycle/unmount.state.d.ts +9 -0
  117. package/dist/lib/state/lifecycle/unmount.state.d.ts.map +1 -0
  118. package/dist/lib/state/lifecycle/unmount.state.js +33 -0
  119. package/dist/lib/state/lifecycle/unmount.state.js.map +1 -0
  120. package/dist/types/grammy.types.d.ts +29 -0
  121. package/dist/types/grammy.types.d.ts.map +1 -0
  122. package/dist/types/grammy.types.js +3 -0
  123. package/dist/types/grammy.types.js.map +1 -0
  124. package/dist/types/hooks.types.d.ts +27 -0
  125. package/dist/types/hooks.types.d.ts.map +1 -0
  126. package/dist/types/jsx.types.d.ts +240 -0
  127. package/dist/types/jsx.types.d.ts.map +1 -0
  128. package/dist/types/jsx.types.js +29 -0
  129. package/dist/types/jsx.types.js.map +1 -0
  130. package/dist/types/lib.types.d.ts +80 -0
  131. package/dist/types/lib.types.d.ts.map +1 -0
  132. package/dist/types/lib.types.js +1 -0
  133. package/dist/types/lib.types.js.map +1 -0
  134. package/dist/types/plugin.types.d.ts +81 -0
  135. package/dist/types/plugin.types.d.ts.map +1 -0
  136. package/dist/types/plugin.types.js +1 -0
  137. package/dist/types/plugin.types.js.map +1 -0
  138. package/dist/utils/generateUniqueId.d.ts +29 -0
  139. package/dist/utils/generateUniqueId.d.ts.map +1 -0
  140. package/dist/utils/generateUniqueId.js +41 -0
  141. package/dist/utils/generateUniqueId.js.map +1 -0
  142. package/dist/utils/getEmoji.d.ts +26 -0
  143. package/dist/utils/getEmoji.d.ts.map +1 -0
  144. package/dist/utils/getEmoji.js +33 -0
  145. package/dist/utils/getEmoji.js.map +1 -0
  146. package/dist/utils/getPlainText.d.ts +23 -0
  147. package/dist/utils/getPlainText.d.ts.map +1 -0
  148. package/dist/utils/getPlainText.js +30 -0
  149. package/dist/utils/getPlainText.js.map +1 -0
  150. package/dist/utils/global.d.ts +13 -0
  151. package/dist/utils/global.d.ts.map +1 -0
  152. package/dist/utils/global.js +7 -0
  153. package/dist/utils/global.js.map +1 -0
  154. package/dist/utils/index.d.ts +9 -0
  155. package/dist/utils/index.d.ts.map +1 -0
  156. package/dist/utils/index.js +10 -0
  157. package/dist/utils/index.js.map +1 -0
  158. package/dist/utils/isAbortError.d.ts +2 -0
  159. package/dist/utils/isAbortError.d.ts.map +1 -0
  160. package/dist/utils/isEmoji.d.ts +25 -0
  161. package/dist/utils/isEmoji.d.ts.map +1 -0
  162. package/dist/utils/isEmoji.js +27 -0
  163. package/dist/utils/isEmoji.js.map +1 -0
  164. package/dist/utils/isEqual.d.ts +42 -0
  165. package/dist/utils/isEqual.d.ts.map +1 -0
  166. package/dist/utils/isEqual.js +142 -0
  167. package/dist/utils/isEqual.js.map +1 -0
  168. package/dist/utils/isIntrinsicElement.d.ts +23 -0
  169. package/dist/utils/isIntrinsicElement.d.ts.map +1 -0
  170. package/dist/utils/isIntrinsicElement.js +25 -0
  171. package/dist/utils/isIntrinsicElement.js.map +1 -0
  172. package/dist/utils/isMessageNotFount.d.ts +2 -0
  173. package/dist/utils/isMessageNotFount.d.ts.map +1 -0
  174. package/dist/utils/isUnixTime.d.ts +32 -0
  175. package/dist/utils/isUnixTime.d.ts.map +1 -0
  176. package/dist/utils/isUnixTime.js +38 -0
  177. package/dist/utils/isUnixTime.js.map +1 -0
  178. package/dist/utils/sanitizeHtmlString.d.ts +24 -0
  179. package/dist/utils/sanitizeHtmlString.d.ts.map +1 -0
  180. package/dist/utils/sanitizeHtmlString.js +29 -0
  181. package/dist/utils/sanitizeHtmlString.js.map +1 -0
  182. package/dist/utils/withComponentScope.d.ts +2 -0
  183. package/dist/utils/withComponentScope.d.ts.map +1 -0
  184. package/dist/utils/withRuntime.d.ts +4 -0
  185. package/dist/utils/withRuntime.d.ts.map +1 -0
  186. package/package.json +62 -0
  187. package/readme.md +373 -0
@@ -0,0 +1,23 @@
1
+ import { type IntrinsicElements } from "~/types/jsx.types";
2
+ /**
3
+ * Checks whether a string is a valid intrinsic JSX element name.
4
+ *
5
+ * This utility acts as a runtime type guard for intrinsic JSX tags
6
+ * supported by the renderer.
7
+ *
8
+ * When the check succeeds, TypeScript narrows `name` to
9
+ * `keyof IntrinsicElements`, making it safe to use in intrinsic
10
+ * element factories, render dispatchers, and JSX runtime pipelines.
11
+ *
12
+ * @param {string} name - The element name to validate.
13
+ * @returns {name is keyof IntrinsicElements}
14
+ * `true` if the name is a supported intrinsic element.
15
+ *
16
+ * @example
17
+ * if (isIntrinsicElement(tag)) {
18
+ * // tag is narrowed to keyof IntrinsicElements
19
+ * createIntrinsicElement({ tag, props });
20
+ * }
21
+ */
22
+ export declare function isIntrinsicElement(name: string): name is keyof IntrinsicElements;
23
+ //# sourceMappingURL=isIntrinsicElement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isIntrinsicElement.d.ts","sourceRoot":"","sources":["../../src/utils/isIntrinsicElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,MAAM,iBAAiB,CAEhF"}
@@ -0,0 +1,25 @@
1
+ import { intrinsicElements } from "~/types/jsx.types";
2
+ /**
3
+ * Checks whether a string is a valid intrinsic JSX element name.
4
+ *
5
+ * This utility acts as a runtime type guard for intrinsic JSX tags
6
+ * supported by the renderer.
7
+ *
8
+ * When the check succeeds, TypeScript narrows `name` to
9
+ * `keyof IntrinsicElements`, making it safe to use in intrinsic
10
+ * element factories, render dispatchers, and JSX runtime pipelines.
11
+ *
12
+ * @param {string} name - The element name to validate.
13
+ * @returns {name is keyof IntrinsicElements}
14
+ * `true` if the name is a supported intrinsic element.
15
+ *
16
+ * @example
17
+ * if (isIntrinsicElement(tag)) {
18
+ * // tag is narrowed to keyof IntrinsicElements
19
+ * createIntrinsicElement({ tag, props });
20
+ * }
21
+ */
22
+ export function isIntrinsicElement(name) {
23
+ return name in intrinsicElements;
24
+ }
25
+ //# sourceMappingURL=isIntrinsicElement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isIntrinsicElement.js","sourceRoot":"","sources":["../../src/utils/isIntrinsicElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA0B,MAAM,mBAAmB,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC3C,OAAO,IAAI,IAAI,iBAAiB,CAAC;AACrC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function isMessageNotFound(error: unknown): boolean;
2
+ //# sourceMappingURL=isMessageNotFount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isMessageNotFount.d.ts","sourceRoot":"","sources":["../../src/utils/isMessageNotFount.ts"],"names":[],"mappings":"AAEA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQzD"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Checks whether a numeric value is a valid Unix timestamp.
3
+ *
4
+ * Supports both:
5
+ * - Unix time in **seconds**
6
+ * - Unix time in **milliseconds**
7
+ *
8
+ * Millisecond timestamps are automatically normalized to seconds
9
+ * before range validation.
10
+ *
11
+ * @remarks
12
+ * The accepted range is:
13
+ * - minimum: Unix epoch (`0`)
14
+ * - maximum: year `3000-01-01T00:00:00Z`
15
+ *
16
+ * @param {number} time - The timestamp value to validate.
17
+ * @returns {boolean} `true` if the value is a finite valid Unix timestamp.
18
+ *
19
+ * @example
20
+ * isUnixTime(1712745600); // true
21
+ *
22
+ * @example
23
+ * isUnixTime(1712745600000); // true
24
+ *
25
+ * @example
26
+ * isUnixTime(NaN); // false
27
+ *
28
+ * @example
29
+ * isUnixTime(-1); // false
30
+ */
31
+ export declare function isUnixTime(time: number): boolean;
32
+ //# sourceMappingURL=isUnixTime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isUnixTime.d.ts","sourceRoot":"","sources":["../../src/utils/isUnixTime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKhD"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Checks whether a numeric value is a valid Unix timestamp.
3
+ *
4
+ * Supports both:
5
+ * - Unix time in **seconds**
6
+ * - Unix time in **milliseconds**
7
+ *
8
+ * Millisecond timestamps are automatically normalized to seconds
9
+ * before range validation.
10
+ *
11
+ * @remarks
12
+ * The accepted range is:
13
+ * - minimum: Unix epoch (`0`)
14
+ * - maximum: year `3000-01-01T00:00:00Z`
15
+ *
16
+ * @param {number} time - The timestamp value to validate.
17
+ * @returns {boolean} `true` if the value is a finite valid Unix timestamp.
18
+ *
19
+ * @example
20
+ * isUnixTime(1712745600); // true
21
+ *
22
+ * @example
23
+ * isUnixTime(1712745600000); // true
24
+ *
25
+ * @example
26
+ * isUnixTime(NaN); // false
27
+ *
28
+ * @example
29
+ * isUnixTime(-1); // false
30
+ */
31
+ export function isUnixTime(time) {
32
+ if (typeof time !== "number" || !Number.isFinite(time))
33
+ return false;
34
+ const min = 0, max = 32503680000;
35
+ const t = time > 1e12 ? Math.floor(time / 1000) : time;
36
+ return t >= min && t <= max;
37
+ }
38
+ //# sourceMappingURL=isUnixTime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isUnixTime.js","sourceRoot":"","sources":["../../src/utils/isUnixTime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACnC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACrE,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,WAAW,CAAC;IACjC,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACvD,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC;AAChC,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Escapes unsafe characters in a string for safe HTML rendering.
3
+ *
4
+ * This utility replaces the core HTML-sensitive characters:
5
+ * - `&` → `&amp;`
6
+ * - `<` → `&lt;`
7
+ * - `>` → `&gt;`
8
+ *
9
+ * It is primarily used during the final message serialization phase
10
+ * to ensure that plain text content cannot break Telegram HTML markup.
11
+ *
12
+ * @param {string} unsafe - The raw string to sanitize.
13
+ * @returns {string} The escaped HTML-safe string.
14
+ *
15
+ * @example
16
+ * sanitizeHtmlString("<b>Hello</b>");
17
+ * // "&lt;b&gt;Hello&lt;/b&gt;"
18
+ *
19
+ * @example
20
+ * sanitizeHtmlString("Tom & Jerry");
21
+ * // "Tom &amp; Jerry"
22
+ */
23
+ export declare function sanitizeHtmlString(unsafe: string): string;
24
+ //# sourceMappingURL=sanitizeHtmlString.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizeHtmlString.d.ts","sourceRoot":"","sources":["../../src/utils/sanitizeHtmlString.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKzD"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Escapes unsafe characters in a string for safe HTML rendering.
3
+ *
4
+ * This utility replaces the core HTML-sensitive characters:
5
+ * - `&` → `&amp;`
6
+ * - `<` → `&lt;`
7
+ * - `>` → `&gt;`
8
+ *
9
+ * It is primarily used during the final message serialization phase
10
+ * to ensure that plain text content cannot break Telegram HTML markup.
11
+ *
12
+ * @param {string} unsafe - The raw string to sanitize.
13
+ * @returns {string} The escaped HTML-safe string.
14
+ *
15
+ * @example
16
+ * sanitizeHtmlString("<b>Hello</b>");
17
+ * // "&lt;b&gt;Hello&lt;/b&gt;"
18
+ *
19
+ * @example
20
+ * sanitizeHtmlString("Tom & Jerry");
21
+ * // "Tom &amp; Jerry"
22
+ */
23
+ export function sanitizeHtmlString(unsafe) {
24
+ return unsafe
25
+ .replaceAll("&", "&amp;")
26
+ .replaceAll("<", "&lt;")
27
+ .replaceAll(">", "&gt;");
28
+ }
29
+ //# sourceMappingURL=sanitizeHtmlString.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizeHtmlString.js","sourceRoot":"","sources":["../../src/utils/sanitizeHtmlString.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC7C,OAAO,MAAM;SACR,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function withComponentScope<T>(callback: () => Promise<T>): Promise<T>;
2
+ //# sourceMappingURL=withComponentScope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withComponentScope.d.ts","sourceRoot":"","sources":["../../src/utils/withComponentScope.ts"],"names":[],"mappings":"AAEA,wBAAsB,kBAAkB,CAAC,CAAC,EACtC,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC,CAyBZ"}
@@ -0,0 +1,4 @@
1
+ import type { HookRuntime } from "~/types/hooks.types";
2
+ import type { ReactiveContext } from "~/types/plugin.types";
3
+ export declare function withRuntime<C extends ReactiveContext>(runtime: HookRuntime, callback: () => Promise<void>): Promise<void>;
4
+ //# sourceMappingURL=withRuntime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withRuntime.d.ts","sourceRoot":"","sources":["../../src/utils/withRuntime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,EACjD,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,iBAkBhC"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "type": "module",
3
+ "name": "@xlsft/grammy-reactive",
4
+ "description": "React-like stateful library for grammY",
5
+ "license": "MIT",
6
+ "version": "0.7.34",
7
+ "engines": {
8
+ "node": ">=14.17.0"
9
+ },
10
+ "author": "xlsft",
11
+ "homepage": "https://cv.xlsft.ru/en/projects/grammy-reactive",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/xlsft/grammy-reactive.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/xlsft/grammy-reactive/issues"
18
+ },
19
+ "module": "true",
20
+ "main": "./dist/lib/index.js",
21
+ "types": "./dist/lib/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/lib/index.d.ts",
25
+ "import": "./dist/lib/index.js"
26
+ },
27
+ "./jsx": {
28
+ "types": "./dist/jsx/index.d.ts",
29
+ "import": "./dist/jsx/index.js"
30
+ },
31
+ "./jsx/jsx-runtime": {
32
+ "types": "./dist/jsx/jsx-runtime.d.ts",
33
+ "import": "./dist/jsx/jsx-runtime.js"
34
+ },
35
+ "./jsx/jsx-dev-runtime": {
36
+ "types": "./dist/jsx/jsx-dev-runtime.d.ts",
37
+ "import": "./dist/jsx/jsx-dev-runtime.js"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist"
42
+ ],
43
+ "scripts": {
44
+ "dev": "bun run --watch examples/main.tsx",
45
+ "build": "tsc -p tsconfig.build.json",
46
+ "publish": "npm publish --access public"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^25.6.0",
50
+ "typescript": "^5"
51
+ },
52
+ "peerDependencies": {
53
+ "grammy": ">=1.0.0 <2"
54
+ },
55
+ "keywords": [
56
+ "declarative",
57
+ "library",
58
+ "jsx",
59
+ "grammy",
60
+ "bot"
61
+ ]
62
+ }
package/readme.md ADDED
@@ -0,0 +1,373 @@
1
+ # @xlsft/grammy-reactive
2
+
3
+ > React-like stateful library for **grammY** with JSX, hooks, lifecycle, effects, and component error boundaries.
4
+
5
+ ---
6
+
7
+ # ✨ Features
8
+
9
+ - ⚛️ JSX component system
10
+ - 🪝 React-like hooks API
11
+ - 🔁 Stateful rerendering
12
+ - 🧠 Persistent component-local hook storage
13
+ - 🧹 `useEffect` with cleanup support
14
+ - 💥 Error boundaries for root + nested components
15
+ - ⛔ Abortable rerenders via `AbortController`
16
+ - 🧩 Async component support
17
+ - 📦 Component path–scoped state isolation
18
+ - 🤖 Deep access to `grammY` context via runtime hooks
19
+
20
+ ---
21
+
22
+ # 🚀 Quick Start
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ # Bun
28
+ bun add @xlsft/grammy-reactive
29
+
30
+ # npm
31
+ npm install @xlsft/grammy-reactive
32
+
33
+ # pnpm
34
+ pnpm add @xlsft/grammy-reactive
35
+
36
+ # yarn
37
+ yarn add @xlsft/grammy-reactive
38
+ ```
39
+
40
+ Add JSX runtime to `tsconfig.json`
41
+
42
+ ```json
43
+ {
44
+ "compilerOptions": {
45
+ "jsxImportSource": "@xlsft/grammy-reactive/jsx",
46
+ "jsx": "react-jsx",
47
+ },
48
+ }
49
+
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Basic Message Component
55
+
56
+ ```tsx
57
+ import { Bot, Context } from "grammy";
58
+ import { defineMessageHandler, useState, reactive, type ReactiveFlavor } from "@xlsft/grammy-reactive";
59
+
60
+ const bot = new Bot<ReactiveFlavor<Context>>(process.env.TG_TOKEN!);
61
+ bot.use(reactive());
62
+
63
+ const startHandler = defineMessageHandler(async () => {
64
+ const [count, setCount] = useState(0);
65
+
66
+ return (
67
+ <>
68
+ <h>Counter</h>
69
+ <blockquote>
70
+ Value: <b>{count}</b>
71
+ </blockquote>
72
+ <button onClick={() => setCount(c => c + 1)}>
73
+ Increment
74
+ </button>
75
+ </>
76
+ );
77
+ });
78
+
79
+ bot.command("start", startHandler);
80
+ ```
81
+ ---
82
+ # 📔 Full documentation
83
+
84
+ WIP
85
+
86
+ ---
87
+
88
+ # 🧠 Core Mental Model
89
+
90
+ Every message handler creates a **persistent message runtime instance**.
91
+
92
+ This runtime stores:
93
+
94
+ - current Telegram message state
95
+ - hook values
96
+ - effect queue
97
+ - cleanup callbacks
98
+ - component tree path
99
+ - abort controller
100
+ - grammY context
101
+
102
+ Each rerender reuses the same runtime.
103
+
104
+ ---
105
+
106
+ # 🪝 Hooks API
107
+
108
+ The framework ships with a familiar React-like hooks layer:
109
+
110
+ - `useState` → local reactive state
111
+ - `useMemo` → memoized computed values
112
+ - `useCallback` → stable callback references
113
+ - `useReducer` → complex state transitions
114
+ - `useEffect` → post-commit side effects + cleanup
115
+
116
+ ...etc
117
+
118
+ ### All hooks are:
119
+
120
+ - component-path scoped
121
+ - persistent across rerenders
122
+ - safe inside async components
123
+ - automatically restored by render order
124
+
125
+ ---
126
+
127
+
128
+ # 🔘 Inline Buttons
129
+
130
+ Inline buttons are a **first-class reactive UI primitive**.
131
+
132
+ They behave like component event handlers and automatically participate in the lifecycle runtime.
133
+
134
+ ```tsx
135
+ <button color="primary" onClick={() => setCount(c => c + 1)}>
136
+ Increment
137
+ </button>
138
+ ```
139
+ ```tsx
140
+ <button variant="copy" value="Copy this text!">
141
+ Copy what text?
142
+ </button>
143
+ ```
144
+ ```tsx
145
+ <button variant="url" url="https://google.com">
146
+ Go to google
147
+ </button>
148
+ ```
149
+
150
+
151
+ # 🧩 Stateful Component Example
152
+
153
+ ```tsx
154
+ import {
155
+ defineMessageHandler,
156
+ useEffect,
157
+ useState,
158
+ useContext,
159
+ } from "~/lib";
160
+
161
+ export default defineMessageHandler(async () => {
162
+ const [seconds, setSeconds] = useState(0);
163
+ const ctx = useContext();
164
+
165
+ useEffect(() => {
166
+ const interval = setInterval(() => {
167
+ setSeconds(s => s + 1);
168
+ }, 1000);
169
+
170
+ return () => clearInterval(interval);
171
+ }, []);
172
+
173
+ return <b>Running for {seconds}s</b>;
174
+ });
175
+ ```
176
+
177
+ ---
178
+
179
+ # 🔄 Lifecycle Model
180
+
181
+ The framework uses a **render → commit → effects** lifecycle.
182
+
183
+ ```mermaid
184
+ flowchart TD
185
+ A[Handler / Event] --> B[Render JSX Tree]
186
+ B --> C[Commit to Telegram]
187
+ C --> D[Flush useEffect Queue]
188
+ D --> E[Wait for User Events]
189
+ E -->|setState| B
190
+ E -->|unmount| F[Run Cleanup]
191
+ F --> G[Delete Message]
192
+ ```
193
+
194
+ ---
195
+
196
+ # 🧠 Hook Storage Architecture
197
+
198
+ Hooks are stored by **component path key**.
199
+
200
+ ```mermaid
201
+ flowchart LR
202
+ A[Runtime] --> B[hooks: Map]
203
+ B --> C[0]
204
+ B --> D[0.0]
205
+ B --> E[0.1]
206
+ C --> F[Parent hooks]
207
+ D --> G[Child A hooks]
208
+ E --> H[Child B hooks]
209
+ ```
210
+
211
+ This guarantees:
212
+
213
+ - sibling isolation
214
+ - nested component stability
215
+ - deterministic hook ordering
216
+ - subtree-safe rerenders
217
+
218
+ ---
219
+
220
+ # 🧬 State Management Principles
221
+
222
+ ## 1) Component Path Isolation
223
+ Each component subtree owns its own hook array.
224
+
225
+ This prevents collisions between:
226
+
227
+ ```tsx
228
+ <A />
229
+ <B />
230
+ ```
231
+
232
+ Both may safely use:
233
+
234
+ ```tsx
235
+ useState()
236
+ ```
237
+
238
+ without shared state.
239
+
240
+ ---
241
+
242
+ ## 2) Functional Updates Preferred
243
+ Always prefer:
244
+
245
+ ```tsx
246
+ setCount(prev => prev + 1)
247
+ ```
248
+
249
+ over:
250
+
251
+ ```tsx
252
+ setCount(count + 1)
253
+ ```
254
+
255
+ This avoids stale closures in async effects.
256
+
257
+ ---
258
+
259
+ ## 3) Effects Are Commit-Phase Only
260
+ `useEffect` runs **after successful Telegram message update**.
261
+
262
+ This guarantees UI consistency.
263
+
264
+ ---
265
+
266
+ # 💥 Error Boundaries
267
+
268
+ The framework automatically catches:
269
+
270
+ - root handler exceptions
271
+ - nested component exceptions
272
+ - rerender errors
273
+ - effect cleanup errors
274
+ - event handler errors
275
+
276
+ Fallback UI renders `InternalError` safely.
277
+
278
+ ```mermaid
279
+ flowchart TD
280
+ A[Render Component] --> B{Throws?}
281
+ B -->|No| C[Continue Render]
282
+ B -->|Yes| D[Render InternalError]
283
+ D --> E[Commit Fallback UI]
284
+ ```
285
+
286
+ ---
287
+
288
+ # ⛔ Abort + Rerender Semantics
289
+
290
+ Every message runtime owns its own `AbortController`.
291
+
292
+ Before rerender:
293
+
294
+ 1. previous controller is aborted
295
+ 2. new controller is created
296
+ 3. stale async updates are cancelled
297
+
298
+ ```mermaid
299
+ sequenceDiagram
300
+ participant U as User
301
+ participant R as Runtime
302
+ participant T as Telegram
303
+
304
+ U->>R: setState()
305
+ R->>R: abort previous controller
306
+ R->>R: render new tree
307
+ R->>T: editMessageText()
308
+ R->>R: flushEffects()
309
+ ```
310
+
311
+ ---
312
+
313
+ # 📐 Best Practices
314
+
315
+ ## ✅ Prefer split effects
316
+
317
+ ```tsx
318
+ useEffect(startInterval, []);
319
+ useEffect(syncReaction, [seconds]);
320
+ ```
321
+
322
+ instead of mixing unrelated side effects.
323
+
324
+ ---
325
+
326
+ ## ✅ Use reducer for flows
327
+ Use `useReducer` for:
328
+
329
+ - wizards
330
+ - menus
331
+ - calculators
332
+ - async status machines
333
+ - pagination
334
+
335
+ ---
336
+
337
+ ## ✅ Memoize callbacks for child props
338
+
339
+ ```tsx
340
+ const onSave = useCallback(() => save(id), [id]);
341
+ ```
342
+
343
+ ---
344
+
345
+ # 🏗️ Architecture Summary
346
+
347
+ ```mermaid
348
+ flowchart TD
349
+ A[defineMessageHandler] --> B[createMessageState]
350
+ B --> C[withRuntime]
351
+ C --> D[createMessageRender]
352
+ D --> E[Telegram Commit]
353
+ E --> F[flushEffects]
354
+ F --> G[cleanupEffects on unmount]
355
+ ```
356
+
357
+ ---
358
+
359
+ # ❤️ Philosophy
360
+
361
+ The goal is simple:
362
+
363
+ > Bring **React-like declarative stateful UI architecture** into Telegram bots without losing grammY power.
364
+
365
+ This library treats a Telegram message as a **live reactive UI surface**.
366
+
367
+ State, effects, rerenders, and cleanup behave like a real component runtime.
368
+
369
+ ---
370
+
371
+ # 📄 License
372
+
373
+ MIT