@bquery/bquery 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/README.md +127 -27
  2. package/dist/batch-x7b2eZST.js +13 -0
  3. package/dist/batch-x7b2eZST.js.map +1 -0
  4. package/dist/component/component.d.ts +69 -0
  5. package/dist/component/component.d.ts.map +1 -0
  6. package/dist/component/html.d.ts +35 -0
  7. package/dist/component/html.d.ts.map +1 -0
  8. package/dist/component/index.d.ts +3 -126
  9. package/dist/component/index.d.ts.map +1 -1
  10. package/dist/component/props.d.ts +18 -0
  11. package/dist/component/props.d.ts.map +1 -0
  12. package/dist/component/types.d.ts +77 -0
  13. package/dist/component/types.d.ts.map +1 -0
  14. package/dist/component.es.mjs +90 -59
  15. package/dist/component.es.mjs.map +1 -1
  16. package/dist/core/collection.d.ts +55 -3
  17. package/dist/core/collection.d.ts.map +1 -1
  18. package/dist/core/dom.d.ts +6 -0
  19. package/dist/core/dom.d.ts.map +1 -0
  20. package/dist/core/element.d.ts +31 -4
  21. package/dist/core/element.d.ts.map +1 -1
  22. package/dist/core/index.d.ts +2 -0
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/utils/array.d.ts +74 -0
  25. package/dist/core/utils/array.d.ts.map +1 -0
  26. package/dist/core/utils/function.d.ts +87 -0
  27. package/dist/core/utils/function.d.ts.map +1 -0
  28. package/dist/core/utils/index.d.ts +70 -0
  29. package/dist/core/utils/index.d.ts.map +1 -0
  30. package/dist/core/utils/misc.d.ts +63 -0
  31. package/dist/core/utils/misc.d.ts.map +1 -0
  32. package/dist/core/utils/number.d.ts +65 -0
  33. package/dist/core/utils/number.d.ts.map +1 -0
  34. package/dist/core/utils/object.d.ts +133 -0
  35. package/dist/core/utils/object.d.ts.map +1 -0
  36. package/dist/core/utils/string.d.ts +80 -0
  37. package/dist/core/utils/string.d.ts.map +1 -0
  38. package/dist/core/utils/type-guards.d.ts +79 -0
  39. package/dist/core/utils/type-guards.d.ts.map +1 -0
  40. package/dist/core-BhpuvPhy.js +170 -0
  41. package/dist/core-BhpuvPhy.js.map +1 -0
  42. package/dist/core.es.mjs +495 -489
  43. package/dist/core.es.mjs.map +1 -1
  44. package/dist/full.d.ts +2 -2
  45. package/dist/full.d.ts.map +1 -1
  46. package/dist/full.es.mjs +87 -64
  47. package/dist/full.es.mjs.map +1 -1
  48. package/dist/full.iife.js +2 -2
  49. package/dist/full.iife.js.map +1 -1
  50. package/dist/full.umd.js +2 -2
  51. package/dist/full.umd.js.map +1 -1
  52. package/dist/index.es.mjs +138 -68
  53. package/dist/index.es.mjs.map +1 -1
  54. package/dist/motion/animate.d.ts +25 -0
  55. package/dist/motion/animate.d.ts.map +1 -0
  56. package/dist/motion/easing.d.ts +30 -0
  57. package/dist/motion/easing.d.ts.map +1 -0
  58. package/dist/motion/flip.d.ts +55 -0
  59. package/dist/motion/flip.d.ts.map +1 -0
  60. package/dist/motion/index.d.ts +11 -138
  61. package/dist/motion/index.d.ts.map +1 -1
  62. package/dist/motion/keyframes.d.ts +21 -0
  63. package/dist/motion/keyframes.d.ts.map +1 -0
  64. package/dist/motion/reduced-motion.d.ts +12 -0
  65. package/dist/motion/reduced-motion.d.ts.map +1 -0
  66. package/dist/motion/scroll.d.ts +15 -0
  67. package/dist/motion/scroll.d.ts.map +1 -0
  68. package/dist/motion/spring.d.ts +42 -0
  69. package/dist/motion/spring.d.ts.map +1 -0
  70. package/dist/motion/stagger.d.ts +22 -0
  71. package/dist/motion/stagger.d.ts.map +1 -0
  72. package/dist/motion/timeline.d.ts +21 -0
  73. package/dist/motion/timeline.d.ts.map +1 -0
  74. package/dist/motion/transition.d.ts +22 -0
  75. package/dist/motion/transition.d.ts.map +1 -0
  76. package/dist/motion/types.d.ts +182 -0
  77. package/dist/motion/types.d.ts.map +1 -0
  78. package/dist/motion.es.mjs +320 -61
  79. package/dist/motion.es.mjs.map +1 -1
  80. package/dist/persisted-DHoi3uEs.js +278 -0
  81. package/dist/persisted-DHoi3uEs.js.map +1 -0
  82. package/dist/platform/storage.d.ts.map +1 -1
  83. package/dist/platform.es.mjs +12 -7
  84. package/dist/platform.es.mjs.map +1 -1
  85. package/dist/reactive/batch.d.ts +13 -0
  86. package/dist/reactive/batch.d.ts.map +1 -0
  87. package/dist/reactive/computed.d.ts +50 -0
  88. package/dist/reactive/computed.d.ts.map +1 -0
  89. package/dist/reactive/core.d.ts +72 -0
  90. package/dist/reactive/core.d.ts.map +1 -0
  91. package/dist/reactive/effect.d.ts +15 -0
  92. package/dist/reactive/effect.d.ts.map +1 -0
  93. package/dist/reactive/index.d.ts +2 -2
  94. package/dist/reactive/index.d.ts.map +1 -1
  95. package/dist/reactive/internals.d.ts +42 -0
  96. package/dist/reactive/internals.d.ts.map +1 -0
  97. package/dist/reactive/linked.d.ts +36 -0
  98. package/dist/reactive/linked.d.ts.map +1 -0
  99. package/dist/reactive/persisted.d.ts +14 -0
  100. package/dist/reactive/persisted.d.ts.map +1 -0
  101. package/dist/reactive/readonly.d.ts +26 -0
  102. package/dist/reactive/readonly.d.ts.map +1 -0
  103. package/dist/reactive/signal.d.ts +13 -312
  104. package/dist/reactive/signal.d.ts.map +1 -1
  105. package/dist/reactive/type-guards.d.ts +20 -0
  106. package/dist/reactive/type-guards.d.ts.map +1 -0
  107. package/dist/reactive/untrack.d.ts +29 -0
  108. package/dist/reactive/untrack.d.ts.map +1 -0
  109. package/dist/reactive/watch.d.ts +42 -0
  110. package/dist/reactive/watch.d.ts.map +1 -0
  111. package/dist/reactive.es.mjs +30 -163
  112. package/dist/reactive.es.mjs.map +1 -1
  113. package/dist/router/index.d.ts +6 -252
  114. package/dist/router/index.d.ts.map +1 -1
  115. package/dist/router/links.d.ts +44 -0
  116. package/dist/router/links.d.ts.map +1 -0
  117. package/dist/router/match.d.ts +20 -0
  118. package/dist/router/match.d.ts.map +1 -0
  119. package/dist/router/navigation.d.ts +45 -0
  120. package/dist/router/navigation.d.ts.map +1 -0
  121. package/dist/router/query.d.ts +16 -0
  122. package/dist/router/query.d.ts.map +1 -0
  123. package/dist/router/router.d.ts +34 -0
  124. package/dist/router/router.d.ts.map +1 -0
  125. package/dist/router/state.d.ts +27 -0
  126. package/dist/router/state.d.ts.map +1 -0
  127. package/dist/router/types.d.ts +88 -0
  128. package/dist/router/types.d.ts.map +1 -0
  129. package/dist/router/utils.d.ts +65 -0
  130. package/dist/router/utils.d.ts.map +1 -0
  131. package/dist/router.es.mjs +168 -132
  132. package/dist/router.es.mjs.map +1 -1
  133. package/dist/sanitize-Cxvxa-DX.js +283 -0
  134. package/dist/sanitize-Cxvxa-DX.js.map +1 -0
  135. package/dist/security/constants.d.ts +42 -0
  136. package/dist/security/constants.d.ts.map +1 -0
  137. package/dist/security/csp.d.ts +24 -0
  138. package/dist/security/csp.d.ts.map +1 -0
  139. package/dist/security/index.d.ts +4 -2
  140. package/dist/security/index.d.ts.map +1 -1
  141. package/dist/security/sanitize-core.d.ts +13 -0
  142. package/dist/security/sanitize-core.d.ts.map +1 -0
  143. package/dist/security/sanitize.d.ts +5 -57
  144. package/dist/security/sanitize.d.ts.map +1 -1
  145. package/dist/security/trusted-types.d.ts +25 -0
  146. package/dist/security/trusted-types.d.ts.map +1 -0
  147. package/dist/security/types.d.ts +36 -0
  148. package/dist/security/types.d.ts.map +1 -0
  149. package/dist/security.es.mjs +50 -277
  150. package/dist/security.es.mjs.map +1 -1
  151. package/dist/store/create-store.d.ts +15 -0
  152. package/dist/store/create-store.d.ts.map +1 -0
  153. package/dist/store/define-store.d.ts +28 -0
  154. package/dist/store/define-store.d.ts.map +1 -0
  155. package/dist/store/devtools.d.ts +22 -0
  156. package/dist/store/devtools.d.ts.map +1 -0
  157. package/dist/store/index.d.ts +10 -286
  158. package/dist/store/index.d.ts.map +1 -1
  159. package/dist/store/mapping.d.ts +28 -0
  160. package/dist/store/mapping.d.ts.map +1 -0
  161. package/dist/store/persisted.d.ts +13 -0
  162. package/dist/store/persisted.d.ts.map +1 -0
  163. package/dist/store/plugins.d.ts +13 -0
  164. package/dist/store/plugins.d.ts.map +1 -0
  165. package/dist/store/registry.d.ts +28 -0
  166. package/dist/store/registry.d.ts.map +1 -0
  167. package/dist/store/types.d.ts +71 -0
  168. package/dist/store/types.d.ts.map +1 -0
  169. package/dist/store/utils.d.ts +28 -0
  170. package/dist/store/utils.d.ts.map +1 -0
  171. package/dist/store/watch.d.ts +23 -0
  172. package/dist/store/watch.d.ts.map +1 -0
  173. package/dist/store.es.mjs +22 -224
  174. package/dist/store.es.mjs.map +1 -1
  175. package/dist/type-guards-BdKlYYlS.js +32 -0
  176. package/dist/type-guards-BdKlYYlS.js.map +1 -0
  177. package/dist/untrack-DNnnqdlR.js +6 -0
  178. package/dist/untrack-DNnnqdlR.js.map +1 -0
  179. package/dist/view/directives/bind.d.ts +7 -0
  180. package/dist/view/directives/bind.d.ts.map +1 -0
  181. package/dist/view/directives/class.d.ts +8 -0
  182. package/dist/view/directives/class.d.ts.map +1 -0
  183. package/dist/view/directives/for.d.ts +23 -0
  184. package/dist/view/directives/for.d.ts.map +1 -0
  185. package/dist/view/directives/html.d.ts +7 -0
  186. package/dist/view/directives/html.d.ts.map +1 -0
  187. package/dist/view/directives/if.d.ts +7 -0
  188. package/dist/view/directives/if.d.ts.map +1 -0
  189. package/dist/view/directives/index.d.ts +12 -0
  190. package/dist/view/directives/index.d.ts.map +1 -0
  191. package/dist/view/directives/model.d.ts +7 -0
  192. package/dist/view/directives/model.d.ts.map +1 -0
  193. package/dist/view/directives/on.d.ts +7 -0
  194. package/dist/view/directives/on.d.ts.map +1 -0
  195. package/dist/view/directives/ref.d.ts +7 -0
  196. package/dist/view/directives/ref.d.ts.map +1 -0
  197. package/dist/view/directives/show.d.ts +7 -0
  198. package/dist/view/directives/show.d.ts.map +1 -0
  199. package/dist/view/directives/style.d.ts +7 -0
  200. package/dist/view/directives/style.d.ts.map +1 -0
  201. package/dist/view/directives/text.d.ts +7 -0
  202. package/dist/view/directives/text.d.ts.map +1 -0
  203. package/dist/view/evaluate.d.ts +43 -0
  204. package/dist/view/evaluate.d.ts.map +1 -0
  205. package/dist/view/index.d.ts +3 -93
  206. package/dist/view/index.d.ts.map +1 -1
  207. package/dist/view/mount.d.ts +69 -0
  208. package/dist/view/mount.d.ts.map +1 -0
  209. package/dist/view/process.d.ts +26 -0
  210. package/dist/view/process.d.ts.map +1 -0
  211. package/dist/view/types.d.ts +36 -0
  212. package/dist/view/types.d.ts.map +1 -0
  213. package/dist/view.es.mjs +358 -251
  214. package/dist/view.es.mjs.map +1 -1
  215. package/dist/watch-DXXv3iAI.js +58 -0
  216. package/dist/watch-DXXv3iAI.js.map +1 -0
  217. package/package.json +14 -14
  218. package/src/component/component.ts +289 -0
  219. package/src/component/html.ts +53 -0
  220. package/src/component/index.ts +40 -414
  221. package/src/component/props.ts +116 -0
  222. package/src/component/types.ts +85 -0
  223. package/src/core/collection.ts +181 -7
  224. package/src/core/dom.ts +38 -0
  225. package/src/core/element.ts +59 -25
  226. package/src/core/index.ts +48 -4
  227. package/src/core/utils/array.ts +102 -0
  228. package/src/core/utils/function.ts +151 -0
  229. package/src/core/utils/index.ts +83 -0
  230. package/src/core/utils/misc.ts +82 -0
  231. package/src/core/utils/number.ts +78 -0
  232. package/src/core/utils/object.ts +206 -0
  233. package/src/core/utils/string.ts +112 -0
  234. package/src/core/utils/type-guards.ts +112 -0
  235. package/src/full.ts +187 -150
  236. package/src/index.ts +36 -36
  237. package/src/motion/animate.ts +113 -0
  238. package/src/motion/easing.ts +40 -0
  239. package/src/motion/flip.ts +176 -0
  240. package/src/motion/index.ts +41 -358
  241. package/src/motion/keyframes.ts +46 -0
  242. package/src/motion/reduced-motion.ts +17 -0
  243. package/src/motion/scroll.ts +57 -0
  244. package/src/motion/spring.ts +150 -0
  245. package/src/motion/stagger.ts +43 -0
  246. package/src/motion/timeline.ts +246 -0
  247. package/src/motion/transition.ts +51 -0
  248. package/src/motion/types.ts +198 -0
  249. package/src/platform/storage.ts +215 -208
  250. package/src/reactive/batch.ts +22 -0
  251. package/src/reactive/computed.ts +92 -0
  252. package/src/reactive/core.ts +114 -0
  253. package/src/reactive/effect.ts +54 -0
  254. package/src/reactive/index.ts +23 -22
  255. package/src/reactive/internals.ts +122 -0
  256. package/src/reactive/linked.ts +56 -0
  257. package/src/reactive/persisted.ts +74 -0
  258. package/src/reactive/readonly.ts +35 -0
  259. package/src/reactive/signal.ts +20 -520
  260. package/src/reactive/type-guards.ts +22 -0
  261. package/src/reactive/untrack.ts +31 -0
  262. package/src/reactive/watch.ts +73 -0
  263. package/src/router/index.ts +41 -718
  264. package/src/router/links.ts +130 -0
  265. package/src/router/match.ts +106 -0
  266. package/src/router/navigation.ts +71 -0
  267. package/src/router/query.ts +35 -0
  268. package/src/router/router.ts +211 -0
  269. package/src/router/state.ts +46 -0
  270. package/src/router/types.ts +93 -0
  271. package/src/router/utils.ts +116 -0
  272. package/src/security/constants.ts +209 -0
  273. package/src/security/csp.ts +77 -0
  274. package/src/security/index.ts +4 -12
  275. package/src/security/sanitize-core.ts +364 -0
  276. package/src/security/sanitize.ts +66 -625
  277. package/src/security/trusted-types.ts +69 -0
  278. package/src/security/types.ts +40 -0
  279. package/src/store/create-store.ts +329 -0
  280. package/src/store/define-store.ts +48 -0
  281. package/src/store/devtools.ts +45 -0
  282. package/src/store/index.ts +22 -848
  283. package/src/store/mapping.ts +73 -0
  284. package/src/store/persisted.ts +61 -0
  285. package/src/store/plugins.ts +32 -0
  286. package/src/store/registry.ts +51 -0
  287. package/src/store/types.ts +94 -0
  288. package/src/store/utils.ts +141 -0
  289. package/src/store/watch.ts +52 -0
  290. package/src/view/directives/bind.ts +23 -0
  291. package/src/view/directives/class.ts +70 -0
  292. package/src/view/directives/for.ts +275 -0
  293. package/src/view/directives/html.ts +19 -0
  294. package/src/view/directives/if.ts +30 -0
  295. package/src/view/directives/index.ts +11 -0
  296. package/src/view/directives/model.ts +56 -0
  297. package/src/view/directives/on.ts +41 -0
  298. package/src/view/directives/ref.ts +41 -0
  299. package/src/view/directives/show.ts +26 -0
  300. package/src/view/directives/style.ts +47 -0
  301. package/src/view/directives/text.ts +15 -0
  302. package/src/view/evaluate.ts +290 -0
  303. package/src/view/index.ts +112 -1041
  304. package/src/view/mount.ts +200 -0
  305. package/src/view/process.ts +92 -0
  306. package/src/view/types.ts +44 -0
  307. package/dist/core/utils.d.ts +0 -313
  308. package/dist/core/utils.d.ts.map +0 -1
  309. package/src/core/utils.ts +0 -444
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Function-focused utility helpers.
3
+ *
4
+ * @module bquery/core/utils/function
5
+ */
6
+
7
+ /** A debounced function with a cancel method to clear the pending timeout. */
8
+ export interface DebouncedFn<TArgs extends unknown[]> {
9
+ (...args: TArgs): void;
10
+ /** Cancels the pending debounced invocation. */
11
+ cancel(): void;
12
+ }
13
+
14
+ /** A throttled function with a cancel method to reset the throttle timer. */
15
+ export interface ThrottledFn<TArgs extends unknown[]> {
16
+ (...args: TArgs): void;
17
+ /** Resets the throttle timer, allowing the next call to execute immediately. */
18
+ cancel(): void;
19
+ }
20
+
21
+ /**
22
+ * Creates a debounced function that delays execution until after
23
+ * the specified delay has elapsed since the last call.
24
+ *
25
+ * @template TArgs - The argument types of the function
26
+ * @param fn - The function to debounce
27
+ * @param delayMs - Delay in milliseconds
28
+ * @returns A debounced version of the function with a `cancel()` method
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const search = debounce((query: string) => {
33
+ * console.log('Searching:', query);
34
+ * }, 300);
35
+ *
36
+ * search('h');
37
+ * search('he');
38
+ * search('hello'); // Only this call executes after 300ms
39
+ *
40
+ * search('cancel me');
41
+ * search.cancel(); // Cancels the pending invocation
42
+ * ```
43
+ */
44
+ export function debounce<TArgs extends unknown[]>(
45
+ fn: (...args: TArgs) => void,
46
+ delayMs: number
47
+ ): DebouncedFn<TArgs> {
48
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
49
+ const debounced: DebouncedFn<TArgs> = Object.assign(
50
+ (...args: TArgs) => {
51
+ if (timeoutId !== undefined) {
52
+ clearTimeout(timeoutId);
53
+ }
54
+ timeoutId = setTimeout(() => {
55
+ timeoutId = undefined;
56
+ fn(...args);
57
+ }, delayMs);
58
+ },
59
+ {
60
+ cancel: () => {
61
+ if (timeoutId !== undefined) {
62
+ clearTimeout(timeoutId);
63
+ timeoutId = undefined;
64
+ }
65
+ },
66
+ }
67
+ );
68
+ return debounced;
69
+ }
70
+
71
+ /**
72
+ * Creates a throttled function that runs at most once per interval.
73
+ *
74
+ * @template TArgs - The argument types of the function
75
+ * @param fn - The function to throttle
76
+ * @param intervalMs - Minimum interval between calls in milliseconds
77
+ * @returns A throttled version of the function with a `cancel()` method
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * const handleScroll = throttle(() => {
82
+ * console.log('Scroll position:', window.scrollY);
83
+ * }, 100);
84
+ *
85
+ * window.addEventListener('scroll', handleScroll);
86
+ *
87
+ * handleScroll.cancel(); // Resets throttle, next call executes immediately
88
+ * ```
89
+ */
90
+ export function throttle<TArgs extends unknown[]>(
91
+ fn: (...args: TArgs) => void,
92
+ intervalMs: number
93
+ ): ThrottledFn<TArgs> {
94
+ let lastRun = 0;
95
+ const throttled: ThrottledFn<TArgs> = Object.assign(
96
+ (...args: TArgs) => {
97
+ const now = Date.now();
98
+ if (now - lastRun >= intervalMs) {
99
+ lastRun = now;
100
+ fn(...args);
101
+ }
102
+ },
103
+ {
104
+ cancel: () => {
105
+ lastRun = 0;
106
+ },
107
+ }
108
+ );
109
+ return throttled;
110
+ }
111
+
112
+ /**
113
+ * Ensures a function only runs once. Subsequent calls return the first result.
114
+ *
115
+ * @template TArgs - The argument types of the function
116
+ * @template TResult - The return type of the function
117
+ * @param fn - The function to wrap
118
+ * @returns A function that only runs once
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * const init = once(() => ({ ready: true }));
123
+ * init();
124
+ * init(); // only runs once
125
+ * ```
126
+ */
127
+ export function once<TArgs extends unknown[], TResult>(
128
+ fn: (...args: TArgs) => TResult
129
+ ): (...args: TArgs) => TResult {
130
+ let hasRun = false;
131
+ let result!: TResult;
132
+ return (...args: TArgs) => {
133
+ if (!hasRun) {
134
+ result = fn(...args);
135
+ hasRun = true;
136
+ }
137
+ return result;
138
+ };
139
+ }
140
+
141
+ /**
142
+ * A no-operation function.
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * noop();
147
+ * ```
148
+ */
149
+ export function noop(): void {
150
+ // Intentionally empty
151
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Utility helpers used across the framework.
3
+ * These are intentionally small and framework-agnostic to keep the core tiny.
4
+ *
5
+ * @module bquery/core/utils
6
+ */
7
+
8
+ export * from './array';
9
+ export * from './function';
10
+ export * from './misc';
11
+ export * from './number';
12
+ export * from './object';
13
+ export * from './string';
14
+ export * from './type-guards';
15
+
16
+ import { chunk, compact, ensureArray, flatten, unique } from './array';
17
+ import { debounce, noop, once, throttle } from './function';
18
+ import { isEmpty, parseJson, sleep, uid } from './misc';
19
+ import { clamp, inRange, randomInt, toNumber } from './number';
20
+ import { clone, hasOwn, isPlainObject, merge, omit, pick } from './object';
21
+ import { capitalize, escapeRegExp, slugify, toCamelCase, toKebabCase, truncate } from './string';
22
+ import {
23
+ isArray,
24
+ isBoolean,
25
+ isCollection,
26
+ isDate,
27
+ isElement,
28
+ isFunction,
29
+ isNumber,
30
+ isObject,
31
+ isPromise,
32
+ isString,
33
+ } from './type-guards';
34
+
35
+ /**
36
+ * Utility object containing common helper functions.
37
+ * All utilities are designed to be tree-shakeable and have zero dependencies.
38
+ *
39
+ * Note: `isPrototypePollutionKey` is intentionally excluded from this namespace
40
+ * as it is an internal security helper. It remains available as a named export
41
+ * for internal framework use.
42
+ */
43
+ export const utils = {
44
+ clone,
45
+ merge,
46
+ pick,
47
+ omit,
48
+ hasOwn,
49
+ debounce,
50
+ throttle,
51
+ once,
52
+ noop,
53
+ uid,
54
+ isElement,
55
+ isCollection,
56
+ isEmpty,
57
+ isPlainObject,
58
+ isFunction,
59
+ isString,
60
+ isNumber,
61
+ isBoolean,
62
+ isArray,
63
+ isDate,
64
+ isPromise,
65
+ isObject,
66
+ parseJson,
67
+ sleep,
68
+ randomInt,
69
+ clamp,
70
+ inRange,
71
+ toNumber,
72
+ capitalize,
73
+ toKebabCase,
74
+ toCamelCase,
75
+ truncate,
76
+ slugify,
77
+ escapeRegExp,
78
+ ensureArray,
79
+ unique,
80
+ chunk,
81
+ compact,
82
+ flatten,
83
+ };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Miscellaneous utility helpers.
3
+ *
4
+ * @module bquery/core/utils/misc
5
+ */
6
+
7
+ /**
8
+ * Creates a stable unique ID for DOM usage.
9
+ *
10
+ * @param prefix - Optional prefix for the ID (default: 'bQuery')
11
+ * @returns A unique identifier string
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const id = uid('modal'); // 'modal_x7k2m9p'
16
+ * ```
17
+ */
18
+ export function uid(prefix = 'bQuery'): string {
19
+ return `${prefix}_${Math.random().toString(36).slice(2, 9)}`;
20
+ }
21
+
22
+ /**
23
+ * Delays execution for a specified number of milliseconds.
24
+ *
25
+ * @param ms - Milliseconds to delay
26
+ * @returns A promise that resolves after the delay
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * await sleep(1000); // Wait 1 second
31
+ * console.log('Done!');
32
+ * ```
33
+ */
34
+ export function sleep(ms: number): Promise<void> {
35
+ return new Promise((resolve) => setTimeout(resolve, ms));
36
+ }
37
+
38
+ /**
39
+ * Safely parses a JSON string, returning a default value on error.
40
+ *
41
+ * @template T - The expected type of the parsed value
42
+ * @param json - The JSON string to parse
43
+ * @param fallback - The default value if parsing fails
44
+ * @returns The parsed value or the fallback
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * parseJson('{"name":"bQuery"}', {}); // { name: 'bQuery' }
49
+ * parseJson('invalid', {}); // {}
50
+ * ```
51
+ */
52
+ export function parseJson<T>(json: string, fallback: T): T {
53
+ try {
54
+ return JSON.parse(json) as T;
55
+ } catch {
56
+ return fallback;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Checks for emptiness across common value types.
62
+ *
63
+ * @param value - The value to check
64
+ * @returns True if the value is empty (null, undefined, empty string, empty array, or empty object)
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * isEmpty(''); // true
69
+ * isEmpty([]); // true
70
+ * isEmpty({}); // true
71
+ * isEmpty(null); // true
72
+ * isEmpty('hello'); // false
73
+ * isEmpty([1, 2]); // false
74
+ * ```
75
+ */
76
+ export function isEmpty(value: unknown): boolean {
77
+ if (value == null) return true;
78
+ if (typeof value === 'string') return value.trim().length === 0;
79
+ if (Array.isArray(value)) return value.length === 0;
80
+ if (typeof value === 'object') return Object.keys(value as object).length === 0;
81
+ return false;
82
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Number-focused utility helpers.
3
+ *
4
+ * @module bquery/core/utils/number
5
+ */
6
+
7
+ /**
8
+ * Generates a random integer between min and max (inclusive).
9
+ *
10
+ * @param min - Minimum value
11
+ * @param max - Maximum value
12
+ * @returns A random integer in the range [min, max]
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const roll = randomInt(1, 6); // Random dice roll
17
+ * ```
18
+ */
19
+ export function randomInt(min: number, max: number): number {
20
+ return Math.floor(Math.random() * (max - min + 1)) + min;
21
+ }
22
+
23
+ /**
24
+ * Clamps a number between a minimum and maximum value.
25
+ *
26
+ * @param value - The value to clamp
27
+ * @param min - Minimum value
28
+ * @param max - Maximum value
29
+ * @returns The clamped value
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * clamp(150, 0, 100); // 100
34
+ * clamp(-10, 0, 100); // 0
35
+ * clamp(50, 0, 100); // 50
36
+ * ```
37
+ */
38
+ export function clamp(value: number, min: number, max: number): number {
39
+ return Math.min(Math.max(value, min), max);
40
+ }
41
+
42
+ /**
43
+ * Checks if a number is within a range.
44
+ *
45
+ * @param value - The value to check
46
+ * @param min - Minimum value
47
+ * @param max - Maximum value
48
+ * @param inclusive - Whether the range is inclusive (default: true)
49
+ * @returns True if the value is within the range
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * inRange(5, 1, 10); // true
54
+ * inRange(10, 1, 10, false); // false
55
+ * ```
56
+ */
57
+ export function inRange(value: number, min: number, max: number, inclusive = true): boolean {
58
+ if (inclusive) return value >= min && value <= max;
59
+ return value > min && value < max;
60
+ }
61
+
62
+ /**
63
+ * Converts a value to a number with a fallback on NaN.
64
+ *
65
+ * @param value - The value to convert
66
+ * @param fallback - The fallback value if conversion fails (default: 0)
67
+ * @returns The parsed number or the fallback
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * toNumber('42'); // 42
72
+ * toNumber('nope', 10); // 10
73
+ * ```
74
+ */
75
+ export function toNumber(value: unknown, fallback = 0): number {
76
+ const parsed = typeof value === 'number' ? value : Number(value);
77
+ return Number.isNaN(parsed) ? fallback : parsed;
78
+ }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Object-focused utility helpers.
3
+ *
4
+ * @module bquery/core/utils/object
5
+ */
6
+
7
+ /**
8
+ * Checks if a value is a plain object (not null, array, or class instance).
9
+ *
10
+ * @param value - The value to check
11
+ * @returns True if the value is a plain object
12
+ */
13
+ export function isPlainObject(value: unknown): value is Record<string, unknown> {
14
+ return Object.prototype.toString.call(value) === '[object Object]';
15
+ }
16
+
17
+ /**
18
+ * Checks if a key could cause prototype pollution.
19
+ * These keys are dangerous when used in object merging operations.
20
+ *
21
+ * @param key - The key to check
22
+ * @returns True if the key is a prototype pollution vector
23
+ *
24
+ * @internal
25
+ */
26
+ export function isPrototypePollutionKey(key: string): boolean {
27
+ return key === '__proto__' || key === 'constructor' || key === 'prototype';
28
+ }
29
+
30
+ /**
31
+ * Creates a deep clone using structuredClone if available, otherwise fallback to JSON.
32
+ *
33
+ * @template T - The type of value being cloned
34
+ * @param value - The value to clone
35
+ * @returns A deep copy of the value
36
+ *
37
+ * @remarks
38
+ * When `structuredClone` is available (modern browsers, Node 17+, Bun), this function
39
+ * provides full deep cloning including circular references, Date, Map, Set, ArrayBuffer, etc.
40
+ *
41
+ * **JSON fallback limitations** (older environments without `structuredClone`):
42
+ * - **Throws** on circular references
43
+ * - **Drops** functions, `undefined`, and Symbol properties
44
+ * - **Transforms** Date → ISO string, Map/Set → empty object, BigInt → throws
45
+ * - **Loses** prototype chains and non-enumerable properties
46
+ *
47
+ * For guaranteed safe cloning of arbitrary data, ensure your environment supports
48
+ * `structuredClone` or pre-validate your data structure.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const original = { nested: { value: 1 } };
53
+ * const copy = clone(original);
54
+ * copy.nested.value = 2;
55
+ * console.log(original.nested.value); // 1
56
+ * ```
57
+ */
58
+ export function clone<T>(value: T): T {
59
+ if (typeof structuredClone === 'function') {
60
+ return structuredClone(value);
61
+ }
62
+ return JSON.parse(JSON.stringify(value)) as T;
63
+ }
64
+
65
+ /**
66
+ * Deep-merges plain objects into a new object.
67
+ * Later sources override earlier ones for primitive values.
68
+ * Objects are recursively merged.
69
+ *
70
+ * @param sources - Objects to merge
71
+ * @returns A new object with all sources merged as an intersection type
72
+ *
73
+ * @remarks
74
+ * This function uses overloads to provide accurate intersection types for up to 5 sources.
75
+ * For more than 5 sources, the return type falls back to `Record<string, unknown>`.
76
+ *
77
+ * Note that deep merging creates a shallow intersection at the type level. Nested objects
78
+ * are merged at runtime, but TypeScript sees them as intersected types which may not
79
+ * perfectly represent the merged structure for deeply nested conflicting types.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const result = merge(
84
+ * { a: 1, nested: { x: 1 } },
85
+ * { b: 2, nested: { y: 2 } }
86
+ * );
87
+ * // Result: { a: 1, b: 2, nested: { x: 1, y: 2 } }
88
+ * // Type: { a: number; nested: { x: number } } & { b: number; nested: { y: number } }
89
+ * ```
90
+ *
91
+ * @security This method is protected against prototype pollution attacks.
92
+ * Keys like `__proto__`, `constructor`, and `prototype` are ignored.
93
+ */
94
+ export function merge<T1 extends Record<string, unknown>>(source1: T1): T1;
95
+ export function merge<T1 extends Record<string, unknown>, T2 extends Record<string, unknown>>(
96
+ source1: T1,
97
+ source2: T2
98
+ ): T1 & T2;
99
+ export function merge<
100
+ T1 extends Record<string, unknown>,
101
+ T2 extends Record<string, unknown>,
102
+ T3 extends Record<string, unknown>,
103
+ >(source1: T1, source2: T2, source3: T3): T1 & T2 & T3;
104
+ export function merge<
105
+ T1 extends Record<string, unknown>,
106
+ T2 extends Record<string, unknown>,
107
+ T3 extends Record<string, unknown>,
108
+ T4 extends Record<string, unknown>,
109
+ >(source1: T1, source2: T2, source3: T3, source4: T4): T1 & T2 & T3 & T4;
110
+ export function merge<
111
+ T1 extends Record<string, unknown>,
112
+ T2 extends Record<string, unknown>,
113
+ T3 extends Record<string, unknown>,
114
+ T4 extends Record<string, unknown>,
115
+ T5 extends Record<string, unknown>,
116
+ >(source1: T1, source2: T2, source3: T3, source4: T4, source5: T5): T1 & T2 & T3 & T4 & T5;
117
+ export function merge(...sources: Record<string, unknown>[]): Record<string, unknown>;
118
+ export function merge(...sources: Record<string, unknown>[]): Record<string, unknown> {
119
+ const result: Record<string, unknown> = {};
120
+ for (const source of sources) {
121
+ for (const [key, value] of Object.entries(source)) {
122
+ if (isPrototypePollutionKey(key)) continue;
123
+
124
+ if (isPlainObject(value) && isPlainObject(result[key])) {
125
+ result[key] = merge(
126
+ result[key] as Record<string, unknown>,
127
+ value as Record<string, unknown>
128
+ );
129
+ } else {
130
+ result[key] = value;
131
+ }
132
+ }
133
+ }
134
+ return result;
135
+ }
136
+
137
+ /**
138
+ * Picks specified keys from an object.
139
+ *
140
+ * @template T - The object type
141
+ * @template K - The key type
142
+ * @param obj - The source object
143
+ * @param keys - Keys to pick
144
+ * @returns A new object with only the specified keys
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * const user = { name: 'John', age: 30, email: 'john@example.com' };
149
+ * pick(user, ['name', 'email']); // { name: 'John', email: 'john@example.com' }
150
+ * ```
151
+ */
152
+ export function pick<T extends Record<string, unknown>, K extends keyof T>(
153
+ obj: T,
154
+ keys: K[]
155
+ ): Pick<T, K> {
156
+ const result = {} as Pick<T, K>;
157
+ for (const key of keys) {
158
+ if (key in obj) {
159
+ result[key] = obj[key];
160
+ }
161
+ }
162
+ return result;
163
+ }
164
+
165
+ /**
166
+ * Omits specified keys from an object.
167
+ *
168
+ * @template T - The object type
169
+ * @template K - The key type
170
+ * @param obj - The source object
171
+ * @param keys - Keys to omit
172
+ * @returns A new object without the specified keys
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * const user = { name: 'John', age: 30, password: 'secret' };
177
+ * omit(user, ['password']); // { name: 'John', age: 30 }
178
+ * ```
179
+ */
180
+ export function omit<T extends Record<string, unknown>, K extends keyof T>(
181
+ obj: T,
182
+ keys: K[]
183
+ ): Omit<T, K> {
184
+ const result = { ...obj };
185
+ for (const key of keys) {
186
+ delete result[key];
187
+ }
188
+ return result as Omit<T, K>;
189
+ }
190
+
191
+ /**
192
+ * Checks if an object has a given own property.
193
+ *
194
+ * @template T - The object type
195
+ * @param obj - The object to check
196
+ * @param key - The property key
197
+ * @returns True if the property exists on the object
198
+ *
199
+ * @example
200
+ * ```ts
201
+ * hasOwn({ a: 1 }, 'a'); // true
202
+ * ```
203
+ */
204
+ export function hasOwn<T extends object>(obj: T, key: PropertyKey): key is keyof T {
205
+ return Object.prototype.hasOwnProperty.call(obj, key);
206
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * String-focused utility helpers.
3
+ *
4
+ * @module bquery/core/utils/string
5
+ */
6
+
7
+ /**
8
+ * Capitalizes the first letter of a string.
9
+ *
10
+ * @param str - The string to capitalize
11
+ * @returns The capitalized string
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * capitalize('hello'); // 'Hello'
16
+ * ```
17
+ */
18
+ export function capitalize(str: string): string {
19
+ if (!str) return str;
20
+ return str.charAt(0).toUpperCase() + str.slice(1);
21
+ }
22
+
23
+ /**
24
+ * Converts a string to kebab-case.
25
+ *
26
+ * @param str - The string to convert
27
+ * @returns The kebab-cased string
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * toKebabCase('myVariableName'); // 'my-variable-name'
32
+ * ```
33
+ */
34
+ export function toKebabCase(str: string): string {
35
+ return str
36
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
37
+ .replace(/[\s_]+/g, '-')
38
+ .toLowerCase();
39
+ }
40
+
41
+ /**
42
+ * Converts a string to camelCase.
43
+ *
44
+ * @param str - The string to convert
45
+ * @returns The camelCased string
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * toCamelCase('my-variable-name'); // 'myVariableName'
50
+ * ```
51
+ */
52
+ export function toCamelCase(str: string): string {
53
+ return str
54
+ .replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ''))
55
+ .replace(/^[A-Z]/, (char) => char.toLowerCase());
56
+ }
57
+
58
+ /**
59
+ * Truncates a string to a maximum length.
60
+ *
61
+ * @param str - The string to truncate
62
+ * @param maxLength - The maximum length
63
+ * @param suffix - The suffix to append when truncating (default: '…')
64
+ * @returns The truncated string
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * truncate('Hello world', 8); // 'Hello w…'
69
+ * ```
70
+ */
71
+ export function truncate(str: string, maxLength: number, suffix = '…'): string {
72
+ if (maxLength <= 0) return '';
73
+ if (str.length <= maxLength) return str;
74
+ const sliceLength = Math.max(0, maxLength - suffix.length);
75
+ return `${str.slice(0, sliceLength)}${suffix}`;
76
+ }
77
+
78
+ /**
79
+ * Converts a string to a URL-friendly slug.
80
+ *
81
+ * @param str - The string to slugify
82
+ * @returns The slugified string
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * slugify('Hello, World!'); // 'hello-world'
87
+ * ```
88
+ */
89
+ export function slugify(str: string): string {
90
+ return str
91
+ .normalize('NFD')
92
+ .replace(/[\u0300-\u036f]/g, '')
93
+ .replace(/[^\w\s-]/g, '')
94
+ .trim()
95
+ .replace(/[\s_-]+/g, '-')
96
+ .toLowerCase();
97
+ }
98
+
99
+ /**
100
+ * Escapes a string for safe usage inside a RegExp.
101
+ *
102
+ * @param str - The string to escape
103
+ * @returns The escaped string
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * escapeRegExp('[a-z]+'); // '\\[a-z\\]+'
108
+ * ```
109
+ */
110
+ export function escapeRegExp(str: string): string {
111
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
112
+ }