@bquery/bquery 1.2.0 → 1.3.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 (305) hide show
  1. package/README.md +501 -427
  2. package/dist/batch-4LAvfLE7.js +13 -0
  3. package/dist/batch-4LAvfLE7.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 +36 -0
  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 +8 -0
  21. package/dist/core/element.d.ts.map +1 -1
  22. package/dist/core/index.d.ts +1 -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 +70 -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-COenAZjD.js +145 -0
  41. package/dist/core-COenAZjD.js.map +1 -0
  42. package/dist/core.es.mjs +411 -448
  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-Dz_ryNuC.js +278 -0
  81. package/dist/persisted-Dz_ryNuC.js.map +1 -0
  82. package/dist/reactive/batch.d.ts +13 -0
  83. package/dist/reactive/batch.d.ts.map +1 -0
  84. package/dist/reactive/computed.d.ts +50 -0
  85. package/dist/reactive/computed.d.ts.map +1 -0
  86. package/dist/reactive/core.d.ts +60 -0
  87. package/dist/reactive/core.d.ts.map +1 -0
  88. package/dist/reactive/effect.d.ts +15 -0
  89. package/dist/reactive/effect.d.ts.map +1 -0
  90. package/dist/reactive/index.d.ts +2 -2
  91. package/dist/reactive/index.d.ts.map +1 -1
  92. package/dist/reactive/internals.d.ts +36 -0
  93. package/dist/reactive/internals.d.ts.map +1 -0
  94. package/dist/reactive/linked.d.ts +36 -0
  95. package/dist/reactive/linked.d.ts.map +1 -0
  96. package/dist/reactive/persisted.d.ts +14 -0
  97. package/dist/reactive/persisted.d.ts.map +1 -0
  98. package/dist/reactive/readonly.d.ts +26 -0
  99. package/dist/reactive/readonly.d.ts.map +1 -0
  100. package/dist/reactive/signal.d.ts +13 -312
  101. package/dist/reactive/signal.d.ts.map +1 -1
  102. package/dist/reactive/type-guards.d.ts +20 -0
  103. package/dist/reactive/type-guards.d.ts.map +1 -0
  104. package/dist/reactive/untrack.d.ts +29 -0
  105. package/dist/reactive/untrack.d.ts.map +1 -0
  106. package/dist/reactive/watch.d.ts +42 -0
  107. package/dist/reactive/watch.d.ts.map +1 -0
  108. package/dist/reactive.es.mjs +30 -163
  109. package/dist/reactive.es.mjs.map +1 -1
  110. package/dist/router/index.d.ts +6 -252
  111. package/dist/router/index.d.ts.map +1 -1
  112. package/dist/router/links.d.ts +44 -0
  113. package/dist/router/links.d.ts.map +1 -0
  114. package/dist/router/match.d.ts +20 -0
  115. package/dist/router/match.d.ts.map +1 -0
  116. package/dist/router/navigation.d.ts +45 -0
  117. package/dist/router/navigation.d.ts.map +1 -0
  118. package/dist/router/query.d.ts +16 -0
  119. package/dist/router/query.d.ts.map +1 -0
  120. package/dist/router/router.d.ts +34 -0
  121. package/dist/router/router.d.ts.map +1 -0
  122. package/dist/router/state.d.ts +27 -0
  123. package/dist/router/state.d.ts.map +1 -0
  124. package/dist/router/types.d.ts +88 -0
  125. package/dist/router/types.d.ts.map +1 -0
  126. package/dist/router/utils.d.ts +65 -0
  127. package/dist/router/utils.d.ts.map +1 -0
  128. package/dist/router.es.mjs +168 -132
  129. package/dist/router.es.mjs.map +1 -1
  130. package/dist/sanitize-1FBEPAFH.js +272 -0
  131. package/dist/sanitize-1FBEPAFH.js.map +1 -0
  132. package/dist/security/constants.d.ts +42 -0
  133. package/dist/security/constants.d.ts.map +1 -0
  134. package/dist/security/csp.d.ts +24 -0
  135. package/dist/security/csp.d.ts.map +1 -0
  136. package/dist/security/index.d.ts +4 -2
  137. package/dist/security/index.d.ts.map +1 -1
  138. package/dist/security/sanitize-core.d.ts +13 -0
  139. package/dist/security/sanitize-core.d.ts.map +1 -0
  140. package/dist/security/sanitize.d.ts +5 -57
  141. package/dist/security/sanitize.d.ts.map +1 -1
  142. package/dist/security/trusted-types.d.ts +25 -0
  143. package/dist/security/trusted-types.d.ts.map +1 -0
  144. package/dist/security/types.d.ts +36 -0
  145. package/dist/security/types.d.ts.map +1 -0
  146. package/dist/security.es.mjs +50 -277
  147. package/dist/security.es.mjs.map +1 -1
  148. package/dist/store/create-store.d.ts +15 -0
  149. package/dist/store/create-store.d.ts.map +1 -0
  150. package/dist/store/define-store.d.ts +28 -0
  151. package/dist/store/define-store.d.ts.map +1 -0
  152. package/dist/store/devtools.d.ts +22 -0
  153. package/dist/store/devtools.d.ts.map +1 -0
  154. package/dist/store/index.d.ts +10 -286
  155. package/dist/store/index.d.ts.map +1 -1
  156. package/dist/store/mapping.d.ts +28 -0
  157. package/dist/store/mapping.d.ts.map +1 -0
  158. package/dist/store/persisted.d.ts +13 -0
  159. package/dist/store/persisted.d.ts.map +1 -0
  160. package/dist/store/plugins.d.ts +13 -0
  161. package/dist/store/plugins.d.ts.map +1 -0
  162. package/dist/store/registry.d.ts +28 -0
  163. package/dist/store/registry.d.ts.map +1 -0
  164. package/dist/store/types.d.ts +71 -0
  165. package/dist/store/types.d.ts.map +1 -0
  166. package/dist/store/utils.d.ts +28 -0
  167. package/dist/store/utils.d.ts.map +1 -0
  168. package/dist/store/watch.d.ts +23 -0
  169. package/dist/store/watch.d.ts.map +1 -0
  170. package/dist/store.es.mjs +22 -224
  171. package/dist/store.es.mjs.map +1 -1
  172. package/dist/type-guards-DRma3-Kc.js +16 -0
  173. package/dist/type-guards-DRma3-Kc.js.map +1 -0
  174. package/dist/untrack-BuEQKH7_.js +6 -0
  175. package/dist/untrack-BuEQKH7_.js.map +1 -0
  176. package/dist/view/directives/bind.d.ts +7 -0
  177. package/dist/view/directives/bind.d.ts.map +1 -0
  178. package/dist/view/directives/class.d.ts +8 -0
  179. package/dist/view/directives/class.d.ts.map +1 -0
  180. package/dist/view/directives/for.d.ts +23 -0
  181. package/dist/view/directives/for.d.ts.map +1 -0
  182. package/dist/view/directives/html.d.ts +7 -0
  183. package/dist/view/directives/html.d.ts.map +1 -0
  184. package/dist/view/directives/if.d.ts +7 -0
  185. package/dist/view/directives/if.d.ts.map +1 -0
  186. package/dist/view/directives/index.d.ts +12 -0
  187. package/dist/view/directives/index.d.ts.map +1 -0
  188. package/dist/view/directives/model.d.ts +7 -0
  189. package/dist/view/directives/model.d.ts.map +1 -0
  190. package/dist/view/directives/on.d.ts +7 -0
  191. package/dist/view/directives/on.d.ts.map +1 -0
  192. package/dist/view/directives/ref.d.ts +7 -0
  193. package/dist/view/directives/ref.d.ts.map +1 -0
  194. package/dist/view/directives/show.d.ts +7 -0
  195. package/dist/view/directives/show.d.ts.map +1 -0
  196. package/dist/view/directives/style.d.ts +7 -0
  197. package/dist/view/directives/style.d.ts.map +1 -0
  198. package/dist/view/directives/text.d.ts +7 -0
  199. package/dist/view/directives/text.d.ts.map +1 -0
  200. package/dist/view/evaluate.d.ts +43 -0
  201. package/dist/view/evaluate.d.ts.map +1 -0
  202. package/dist/view/index.d.ts +3 -93
  203. package/dist/view/index.d.ts.map +1 -1
  204. package/dist/view/mount.d.ts +69 -0
  205. package/dist/view/mount.d.ts.map +1 -0
  206. package/dist/view/process.d.ts +26 -0
  207. package/dist/view/process.d.ts.map +1 -0
  208. package/dist/view/types.d.ts +36 -0
  209. package/dist/view/types.d.ts.map +1 -0
  210. package/dist/view.es.mjs +368 -267
  211. package/dist/view.es.mjs.map +1 -1
  212. package/dist/watch-CXyaBC_9.js +58 -0
  213. package/dist/watch-CXyaBC_9.js.map +1 -0
  214. package/package.json +132 -132
  215. package/src/component/component.ts +289 -0
  216. package/src/component/html.ts +53 -0
  217. package/src/component/index.ts +40 -414
  218. package/src/component/props.ts +116 -0
  219. package/src/component/types.ts +85 -0
  220. package/src/core/collection.ts +588 -454
  221. package/src/core/dom.ts +38 -0
  222. package/src/core/element.ts +746 -740
  223. package/src/core/index.ts +43 -0
  224. package/src/core/utils/array.ts +102 -0
  225. package/src/core/utils/function.ts +110 -0
  226. package/src/core/utils/index.ts +83 -0
  227. package/src/core/utils/misc.ts +82 -0
  228. package/src/core/utils/number.ts +78 -0
  229. package/src/core/utils/object.ts +206 -0
  230. package/src/core/utils/string.ts +112 -0
  231. package/src/core/utils/type-guards.ts +112 -0
  232. package/src/full.ts +187 -150
  233. package/src/index.ts +36 -36
  234. package/src/motion/animate.ts +113 -0
  235. package/src/motion/easing.ts +40 -0
  236. package/src/motion/flip.ts +176 -0
  237. package/src/motion/index.ts +41 -358
  238. package/src/motion/keyframes.ts +46 -0
  239. package/src/motion/reduced-motion.ts +17 -0
  240. package/src/motion/scroll.ts +57 -0
  241. package/src/motion/spring.ts +150 -0
  242. package/src/motion/stagger.ts +43 -0
  243. package/src/motion/timeline.ts +246 -0
  244. package/src/motion/transition.ts +51 -0
  245. package/src/motion/types.ts +198 -0
  246. package/src/reactive/batch.ts +22 -0
  247. package/src/reactive/computed.ts +92 -0
  248. package/src/reactive/core.ts +93 -0
  249. package/src/reactive/effect.ts +43 -0
  250. package/src/reactive/index.ts +23 -22
  251. package/src/reactive/internals.ts +105 -0
  252. package/src/reactive/linked.ts +56 -0
  253. package/src/reactive/persisted.ts +74 -0
  254. package/src/reactive/readonly.ts +35 -0
  255. package/src/reactive/signal.ts +20 -520
  256. package/src/reactive/type-guards.ts +22 -0
  257. package/src/reactive/untrack.ts +31 -0
  258. package/src/reactive/watch.ts +73 -0
  259. package/src/router/index.ts +41 -718
  260. package/src/router/links.ts +130 -0
  261. package/src/router/match.ts +106 -0
  262. package/src/router/navigation.ts +71 -0
  263. package/src/router/query.ts +35 -0
  264. package/src/router/router.ts +211 -0
  265. package/src/router/state.ts +46 -0
  266. package/src/router/types.ts +93 -0
  267. package/src/router/utils.ts +116 -0
  268. package/src/security/constants.ts +209 -0
  269. package/src/security/csp.ts +77 -0
  270. package/src/security/index.ts +4 -12
  271. package/src/security/sanitize-core.ts +343 -0
  272. package/src/security/sanitize.ts +66 -625
  273. package/src/security/trusted-types.ts +69 -0
  274. package/src/security/types.ts +40 -0
  275. package/src/store/create-store.ts +329 -0
  276. package/src/store/define-store.ts +48 -0
  277. package/src/store/devtools.ts +45 -0
  278. package/src/store/index.ts +22 -848
  279. package/src/store/mapping.ts +73 -0
  280. package/src/store/persisted.ts +61 -0
  281. package/src/store/plugins.ts +32 -0
  282. package/src/store/registry.ts +51 -0
  283. package/src/store/types.ts +94 -0
  284. package/src/store/utils.ts +141 -0
  285. package/src/store/watch.ts +52 -0
  286. package/src/view/directives/bind.ts +23 -0
  287. package/src/view/directives/class.ts +70 -0
  288. package/src/view/directives/for.ts +275 -0
  289. package/src/view/directives/html.ts +19 -0
  290. package/src/view/directives/if.ts +30 -0
  291. package/src/view/directives/index.ts +11 -0
  292. package/src/view/directives/model.ts +56 -0
  293. package/src/view/directives/on.ts +41 -0
  294. package/src/view/directives/ref.ts +41 -0
  295. package/src/view/directives/show.ts +26 -0
  296. package/src/view/directives/style.ts +47 -0
  297. package/src/view/directives/text.ts +15 -0
  298. package/src/view/evaluate.ts +274 -0
  299. package/src/view/index.ts +112 -1041
  300. package/src/view/mount.ts +200 -0
  301. package/src/view/process.ts +92 -0
  302. package/src/view/types.ts +44 -0
  303. package/dist/core/utils.d.ts +0 -313
  304. package/dist/core/utils.d.ts.map +0 -1
  305. package/src/core/utils.ts +0 -444
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Router utilities.
3
+ * @module bquery/router
4
+ */
5
+
6
+ import { computed, type ReadonlySignal } from '../reactive/index';
7
+ import { getActiveRouter, routeSignal } from './state';
8
+ import type { RouteDefinition } from './types';
9
+
10
+ // ============================================================================
11
+ // Utilities
12
+ // ============================================================================
13
+
14
+ /**
15
+ * Flattens nested routes into a single array with full paths.
16
+ * Does NOT include the router base - base is only for browser history.
17
+ * @internal
18
+ */
19
+ export const flattenRoutes = (routes: RouteDefinition[], parentPath = ''): RouteDefinition[] => {
20
+ const result: RouteDefinition[] = [];
21
+
22
+ for (const route of routes) {
23
+ const fullPath = route.path === '*' ? '*' : `${parentPath}${route.path}`.replace(/\/+/g, '/');
24
+
25
+ result.push({
26
+ ...route,
27
+ path: fullPath,
28
+ });
29
+
30
+ if (route.children) {
31
+ result.push(...flattenRoutes(route.children, fullPath));
32
+ }
33
+ }
34
+
35
+ return result;
36
+ };
37
+
38
+ /**
39
+ * Resolves a route by name and params.
40
+ *
41
+ * @param name - The route name
42
+ * @param params - Route params to interpolate
43
+ * @returns The resolved path
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { resolve } from 'bquery/router';
48
+ *
49
+ * const path = resolve('user', { id: '42' });
50
+ * // Returns '/user/42' if route is defined as { name: 'user', path: '/user/:id' }
51
+ * ```
52
+ */
53
+ export const resolve = (name: string, params: Record<string, string> = {}): string => {
54
+ const activeRouter = getActiveRouter();
55
+ if (!activeRouter) {
56
+ throw new Error('bQuery router: No router initialized.');
57
+ }
58
+
59
+ const route = activeRouter.routes.find((r) => r.name === name);
60
+ if (!route) {
61
+ throw new Error(`bQuery router: Route "${name}" not found.`);
62
+ }
63
+
64
+ let path = route.path;
65
+ for (const [key, value] of Object.entries(params)) {
66
+ path = path.replace(`:${key}`, encodeURIComponent(value));
67
+ }
68
+
69
+ return path;
70
+ };
71
+
72
+ /**
73
+ * Checks if a path matches the current route.
74
+ *
75
+ * @param path - Path to check
76
+ * @param exact - Whether to match exactly (default: false)
77
+ * @returns True if the path matches
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * import { isActive } from 'bquery/router';
82
+ *
83
+ * if (isActive('/dashboard')) {
84
+ * // Highlight nav item
85
+ * }
86
+ * ```
87
+ */
88
+ export const isActive = (path: string, exact = false): boolean => {
89
+ const current = routeSignal.value.path;
90
+ return exact ? current === path : current.startsWith(path);
91
+ };
92
+
93
+ /**
94
+ * Creates a computed signal that checks if a path is active.
95
+ *
96
+ * @param path - Path to check
97
+ * @param exact - Whether to match exactly
98
+ * @returns A reactive signal
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * import { isActiveSignal } from 'bquery/router';
103
+ * import { effect } from 'bquery/reactive';
104
+ *
105
+ * const dashboardActive = isActiveSignal('/dashboard');
106
+ * effect(() => {
107
+ * navItem.classList.toggle('active', dashboardActive.value);
108
+ * });
109
+ * ```
110
+ */
111
+ export const isActiveSignal = (path: string, exact = false): ReadonlySignal<boolean> => {
112
+ return computed(() => {
113
+ const current = routeSignal.value.path;
114
+ return exact ? current === path : current.startsWith(path);
115
+ });
116
+ };
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Security constants and safe lists.
3
+ *
4
+ * @module bquery/security
5
+ */
6
+
7
+ /**
8
+ * Trusted Types policy name.
9
+ */
10
+ export const POLICY_NAME = 'bquery-sanitizer';
11
+
12
+ /**
13
+ * Default allowed HTML tags considered safe.
14
+ */
15
+ export const DEFAULT_ALLOWED_TAGS = new Set([
16
+ 'a',
17
+ 'abbr',
18
+ 'address',
19
+ 'article',
20
+ 'aside',
21
+ 'b',
22
+ 'bdi',
23
+ 'bdo',
24
+ 'blockquote',
25
+ 'br',
26
+ 'button',
27
+ 'caption',
28
+ 'cite',
29
+ 'code',
30
+ 'col',
31
+ 'colgroup',
32
+ 'data',
33
+ 'dd',
34
+ 'del',
35
+ 'details',
36
+ 'dfn',
37
+ 'div',
38
+ 'dl',
39
+ 'dt',
40
+ 'em',
41
+ 'figcaption',
42
+ 'figure',
43
+ 'footer',
44
+ 'form',
45
+ 'h1',
46
+ 'h2',
47
+ 'h3',
48
+ 'h4',
49
+ 'h5',
50
+ 'h6',
51
+ 'header',
52
+ 'hgroup',
53
+ 'hr',
54
+ 'i',
55
+ 'img',
56
+ 'input',
57
+ 'ins',
58
+ 'kbd',
59
+ 'label',
60
+ 'legend',
61
+ 'li',
62
+ 'main',
63
+ 'mark',
64
+ 'nav',
65
+ 'ol',
66
+ 'optgroup',
67
+ 'option',
68
+ 'p',
69
+ 'picture',
70
+ 'pre',
71
+ 'progress',
72
+ 'q',
73
+ 'rp',
74
+ 'rt',
75
+ 'ruby',
76
+ 's',
77
+ 'samp',
78
+ 'section',
79
+ 'select',
80
+ 'small',
81
+ 'source',
82
+ 'span',
83
+ 'strong',
84
+ 'sub',
85
+ 'summary',
86
+ 'sup',
87
+ 'table',
88
+ 'tbody',
89
+ 'td',
90
+ 'textarea',
91
+ 'tfoot',
92
+ 'th',
93
+ 'thead',
94
+ 'time',
95
+ 'tr',
96
+ 'u',
97
+ 'ul',
98
+ 'var',
99
+ 'wbr',
100
+ ]);
101
+
102
+ /**
103
+ * Explicitly dangerous tags that should never be allowed.
104
+ * These are checked even if somehow added to allowTags.
105
+ */
106
+ export const DANGEROUS_TAGS = new Set([
107
+ 'script',
108
+ 'iframe',
109
+ 'frame',
110
+ 'frameset',
111
+ 'object',
112
+ 'embed',
113
+ 'applet',
114
+ 'link',
115
+ 'meta',
116
+ 'style',
117
+ 'base',
118
+ 'template',
119
+ 'slot',
120
+ 'math',
121
+ 'svg',
122
+ 'foreignobject',
123
+ 'noscript',
124
+ ]);
125
+
126
+ /**
127
+ * Reserved IDs that could cause DOM clobbering attacks.
128
+ * These are prevented to avoid overwriting global browser objects.
129
+ */
130
+ export const RESERVED_IDS = new Set([
131
+ // Global objects
132
+ 'document',
133
+ 'window',
134
+ 'location',
135
+ 'top',
136
+ 'self',
137
+ 'parent',
138
+ 'frames',
139
+ 'history',
140
+ 'navigator',
141
+ 'screen',
142
+ // Dangerous functions
143
+ 'alert',
144
+ 'confirm',
145
+ 'prompt',
146
+ 'eval',
147
+ 'function',
148
+ // Document properties
149
+ 'cookie',
150
+ 'domain',
151
+ 'referrer',
152
+ 'body',
153
+ 'head',
154
+ 'forms',
155
+ 'images',
156
+ 'links',
157
+ 'scripts',
158
+ // DOM traversal properties
159
+ 'children',
160
+ 'parentnode',
161
+ 'firstchild',
162
+ 'lastchild',
163
+ // Content manipulation
164
+ 'innerhtml',
165
+ 'outerhtml',
166
+ 'textcontent',
167
+ ]);
168
+
169
+ /**
170
+ * Default allowed attributes considered safe.
171
+ * Note: 'style' is excluded by default because inline CSS can be abused for:
172
+ * - UI redressing attacks
173
+ * - Data exfiltration via url() in CSS
174
+ * - CSS injection vectors
175
+ * If you need to allow inline styles, add 'style' to allowAttributes in your
176
+ * sanitizeHtml options, but ensure you implement proper CSS value validation.
177
+ */
178
+ export const DEFAULT_ALLOWED_ATTRIBUTES = new Set([
179
+ 'alt',
180
+ 'class',
181
+ 'dir',
182
+ 'height',
183
+ 'hidden',
184
+ 'href',
185
+ 'id',
186
+ 'lang',
187
+ 'loading',
188
+ 'name',
189
+ 'rel',
190
+ 'role',
191
+ 'src',
192
+ 'srcset',
193
+ 'tabindex',
194
+ 'target',
195
+ 'title',
196
+ 'type',
197
+ 'width',
198
+ 'aria-*',
199
+ ]);
200
+
201
+ /**
202
+ * Dangerous attribute prefixes to always remove.
203
+ */
204
+ export const DANGEROUS_ATTR_PREFIXES = ['on', 'formaction', 'xlink:', 'xmlns:'];
205
+
206
+ /**
207
+ * Dangerous URL protocols to block.
208
+ */
209
+ export const DANGEROUS_PROTOCOLS = ['javascript:', 'data:', 'vbscript:', 'file:'];
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Content Security Policy helpers.
3
+ *
4
+ * @module bquery/security
5
+ */
6
+
7
+ /** Maximum allowed nonce length to prevent memory issues */
8
+ const MAX_NONCE_LENGTH = 1024;
9
+
10
+ /** Chunk size for building strings to avoid argument limit in String.fromCharCode */
11
+ const CHUNK_SIZE = 8192;
12
+
13
+ /**
14
+ * Generate a nonce for inline scripts/styles.
15
+ * Use with Content-Security-Policy nonce directives.
16
+ *
17
+ * @param length - Nonce length in bytes (default: 16, max: 1024)
18
+ * @returns Cryptographically random nonce string
19
+ * @throws {Error} If crypto.getRandomValues or btoa are not available
20
+ * @throws {RangeError} If length is invalid (negative, non-integer, or exceeds maximum)
21
+ */
22
+ export const generateNonce = (length: number = 16): string => {
23
+ // Validate length parameter
24
+ if (!Number.isInteger(length) || length < 1) {
25
+ throw new RangeError('generateNonce length must be a positive integer');
26
+ }
27
+ if (length > MAX_NONCE_LENGTH) {
28
+ throw new RangeError(`generateNonce length must not exceed ${MAX_NONCE_LENGTH}`);
29
+ }
30
+
31
+ // Check for required globals in browser/crypto environments
32
+ if (
33
+ typeof globalThis.crypto === 'undefined' ||
34
+ typeof globalThis.crypto.getRandomValues !== 'function'
35
+ ) {
36
+ throw new Error(
37
+ 'generateNonce requires crypto.getRandomValues (not available in this environment)'
38
+ );
39
+ }
40
+ if (typeof globalThis.btoa !== 'function') {
41
+ throw new Error('generateNonce requires btoa (not available in this environment)');
42
+ }
43
+
44
+ const array = new Uint8Array(length);
45
+ globalThis.crypto.getRandomValues(array);
46
+
47
+ // Build string in chunks to avoid argument limit in String.fromCharCode
48
+ let binaryString = '';
49
+ for (let i = 0; i < array.length; i += CHUNK_SIZE) {
50
+ const chunk = array.subarray(i, Math.min(i + CHUNK_SIZE, array.length));
51
+ binaryString += String.fromCharCode(...chunk);
52
+ }
53
+
54
+ return globalThis.btoa(binaryString).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
55
+ };
56
+
57
+ /**
58
+ * Check if a CSP header is present with specific directive.
59
+ * Useful for feature detection and fallback strategies.
60
+ *
61
+ * @param directive - The CSP directive to check (e.g., 'script-src')
62
+ * @returns True if the directive appears to be enforced
63
+ */
64
+ export const hasCSPDirective = (directive: string): boolean => {
65
+ // Guard for non-DOM environments (SSR, tests, etc.)
66
+ if (typeof document === 'undefined') {
67
+ return false;
68
+ }
69
+
70
+ // Check meta tag
71
+ const meta = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
72
+ if (meta) {
73
+ const content = meta.getAttribute('content') ?? '';
74
+ return content.includes(directive);
75
+ }
76
+ return false;
77
+ };
@@ -4,15 +4,7 @@
4
4
  * @module bquery/security
5
5
  */
6
6
 
7
- export {
8
- createTrustedHtml,
9
- escapeHtml,
10
- generateNonce,
11
- getTrustedTypesPolicy,
12
- hasCSPDirective,
13
- isTrustedTypesSupported,
14
- sanitizeHtml as sanitize,
15
- sanitizeHtml,
16
- stripTags,
17
- } from './sanitize';
18
- export type { SanitizeOptions } from './sanitize';
7
+ export { generateNonce, hasCSPDirective } from './csp';
8
+ export { escapeHtml, sanitizeHtml as sanitize, sanitizeHtml, stripTags } from './sanitize';
9
+ export { createTrustedHtml, getTrustedTypesPolicy, isTrustedTypesSupported } from './trusted-types';
10
+ export type { SanitizeOptions } from './types';