@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.
- package/README.md +501 -427
- package/dist/batch-4LAvfLE7.js +13 -0
- package/dist/batch-4LAvfLE7.js.map +1 -0
- package/dist/component/component.d.ts +69 -0
- package/dist/component/component.d.ts.map +1 -0
- package/dist/component/html.d.ts +35 -0
- package/dist/component/html.d.ts.map +1 -0
- package/dist/component/index.d.ts +3 -126
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/props.d.ts +18 -0
- package/dist/component/props.d.ts.map +1 -0
- package/dist/component/types.d.ts +77 -0
- package/dist/component/types.d.ts.map +1 -0
- package/dist/component.es.mjs +90 -59
- package/dist/component.es.mjs.map +1 -1
- package/dist/core/collection.d.ts +36 -0
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/dom.d.ts +6 -0
- package/dist/core/dom.d.ts.map +1 -0
- package/dist/core/element.d.ts +8 -0
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/utils/array.d.ts +74 -0
- package/dist/core/utils/array.d.ts.map +1 -0
- package/dist/core/utils/function.d.ts +70 -0
- package/dist/core/utils/function.d.ts.map +1 -0
- package/dist/core/utils/index.d.ts +70 -0
- package/dist/core/utils/index.d.ts.map +1 -0
- package/dist/core/utils/misc.d.ts +63 -0
- package/dist/core/utils/misc.d.ts.map +1 -0
- package/dist/core/utils/number.d.ts +65 -0
- package/dist/core/utils/number.d.ts.map +1 -0
- package/dist/core/utils/object.d.ts +133 -0
- package/dist/core/utils/object.d.ts.map +1 -0
- package/dist/core/utils/string.d.ts +80 -0
- package/dist/core/utils/string.d.ts.map +1 -0
- package/dist/core/utils/type-guards.d.ts +79 -0
- package/dist/core/utils/type-guards.d.ts.map +1 -0
- package/dist/core-COenAZjD.js +145 -0
- package/dist/core-COenAZjD.js.map +1 -0
- package/dist/core.es.mjs +411 -448
- package/dist/core.es.mjs.map +1 -1
- package/dist/full.d.ts +2 -2
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +87 -64
- package/dist/full.es.mjs.map +1 -1
- package/dist/full.iife.js +2 -2
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +2 -2
- package/dist/full.umd.js.map +1 -1
- package/dist/index.es.mjs +138 -68
- package/dist/index.es.mjs.map +1 -1
- package/dist/motion/animate.d.ts +25 -0
- package/dist/motion/animate.d.ts.map +1 -0
- package/dist/motion/easing.d.ts +30 -0
- package/dist/motion/easing.d.ts.map +1 -0
- package/dist/motion/flip.d.ts +55 -0
- package/dist/motion/flip.d.ts.map +1 -0
- package/dist/motion/index.d.ts +11 -138
- package/dist/motion/index.d.ts.map +1 -1
- package/dist/motion/keyframes.d.ts +21 -0
- package/dist/motion/keyframes.d.ts.map +1 -0
- package/dist/motion/reduced-motion.d.ts +12 -0
- package/dist/motion/reduced-motion.d.ts.map +1 -0
- package/dist/motion/scroll.d.ts +15 -0
- package/dist/motion/scroll.d.ts.map +1 -0
- package/dist/motion/spring.d.ts +42 -0
- package/dist/motion/spring.d.ts.map +1 -0
- package/dist/motion/stagger.d.ts +22 -0
- package/dist/motion/stagger.d.ts.map +1 -0
- package/dist/motion/timeline.d.ts +21 -0
- package/dist/motion/timeline.d.ts.map +1 -0
- package/dist/motion/transition.d.ts +22 -0
- package/dist/motion/transition.d.ts.map +1 -0
- package/dist/motion/types.d.ts +182 -0
- package/dist/motion/types.d.ts.map +1 -0
- package/dist/motion.es.mjs +320 -61
- package/dist/motion.es.mjs.map +1 -1
- package/dist/persisted-Dz_ryNuC.js +278 -0
- package/dist/persisted-Dz_ryNuC.js.map +1 -0
- package/dist/reactive/batch.d.ts +13 -0
- package/dist/reactive/batch.d.ts.map +1 -0
- package/dist/reactive/computed.d.ts +50 -0
- package/dist/reactive/computed.d.ts.map +1 -0
- package/dist/reactive/core.d.ts +60 -0
- package/dist/reactive/core.d.ts.map +1 -0
- package/dist/reactive/effect.d.ts +15 -0
- package/dist/reactive/effect.d.ts.map +1 -0
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/internals.d.ts +36 -0
- package/dist/reactive/internals.d.ts.map +1 -0
- package/dist/reactive/linked.d.ts +36 -0
- package/dist/reactive/linked.d.ts.map +1 -0
- package/dist/reactive/persisted.d.ts +14 -0
- package/dist/reactive/persisted.d.ts.map +1 -0
- package/dist/reactive/readonly.d.ts +26 -0
- package/dist/reactive/readonly.d.ts.map +1 -0
- package/dist/reactive/signal.d.ts +13 -312
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive/type-guards.d.ts +20 -0
- package/dist/reactive/type-guards.d.ts.map +1 -0
- package/dist/reactive/untrack.d.ts +29 -0
- package/dist/reactive/untrack.d.ts.map +1 -0
- package/dist/reactive/watch.d.ts +42 -0
- package/dist/reactive/watch.d.ts.map +1 -0
- package/dist/reactive.es.mjs +30 -163
- package/dist/reactive.es.mjs.map +1 -1
- package/dist/router/index.d.ts +6 -252
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/links.d.ts +44 -0
- package/dist/router/links.d.ts.map +1 -0
- package/dist/router/match.d.ts +20 -0
- package/dist/router/match.d.ts.map +1 -0
- package/dist/router/navigation.d.ts +45 -0
- package/dist/router/navigation.d.ts.map +1 -0
- package/dist/router/query.d.ts +16 -0
- package/dist/router/query.d.ts.map +1 -0
- package/dist/router/router.d.ts +34 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/state.d.ts +27 -0
- package/dist/router/state.d.ts.map +1 -0
- package/dist/router/types.d.ts +88 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/utils.d.ts +65 -0
- package/dist/router/utils.d.ts.map +1 -0
- package/dist/router.es.mjs +168 -132
- package/dist/router.es.mjs.map +1 -1
- package/dist/sanitize-1FBEPAFH.js +272 -0
- package/dist/sanitize-1FBEPAFH.js.map +1 -0
- package/dist/security/constants.d.ts +42 -0
- package/dist/security/constants.d.ts.map +1 -0
- package/dist/security/csp.d.ts +24 -0
- package/dist/security/csp.d.ts.map +1 -0
- package/dist/security/index.d.ts +4 -2
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/sanitize-core.d.ts +13 -0
- package/dist/security/sanitize-core.d.ts.map +1 -0
- package/dist/security/sanitize.d.ts +5 -57
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security/trusted-types.d.ts +25 -0
- package/dist/security/trusted-types.d.ts.map +1 -0
- package/dist/security/types.d.ts +36 -0
- package/dist/security/types.d.ts.map +1 -0
- package/dist/security.es.mjs +50 -277
- package/dist/security.es.mjs.map +1 -1
- package/dist/store/create-store.d.ts +15 -0
- package/dist/store/create-store.d.ts.map +1 -0
- package/dist/store/define-store.d.ts +28 -0
- package/dist/store/define-store.d.ts.map +1 -0
- package/dist/store/devtools.d.ts +22 -0
- package/dist/store/devtools.d.ts.map +1 -0
- package/dist/store/index.d.ts +10 -286
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/mapping.d.ts +28 -0
- package/dist/store/mapping.d.ts.map +1 -0
- package/dist/store/persisted.d.ts +13 -0
- package/dist/store/persisted.d.ts.map +1 -0
- package/dist/store/plugins.d.ts +13 -0
- package/dist/store/plugins.d.ts.map +1 -0
- package/dist/store/registry.d.ts +28 -0
- package/dist/store/registry.d.ts.map +1 -0
- package/dist/store/types.d.ts +71 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/utils.d.ts +28 -0
- package/dist/store/utils.d.ts.map +1 -0
- package/dist/store/watch.d.ts +23 -0
- package/dist/store/watch.d.ts.map +1 -0
- package/dist/store.es.mjs +22 -224
- package/dist/store.es.mjs.map +1 -1
- package/dist/type-guards-DRma3-Kc.js +16 -0
- package/dist/type-guards-DRma3-Kc.js.map +1 -0
- package/dist/untrack-BuEQKH7_.js +6 -0
- package/dist/untrack-BuEQKH7_.js.map +1 -0
- package/dist/view/directives/bind.d.ts +7 -0
- package/dist/view/directives/bind.d.ts.map +1 -0
- package/dist/view/directives/class.d.ts +8 -0
- package/dist/view/directives/class.d.ts.map +1 -0
- package/dist/view/directives/for.d.ts +23 -0
- package/dist/view/directives/for.d.ts.map +1 -0
- package/dist/view/directives/html.d.ts +7 -0
- package/dist/view/directives/html.d.ts.map +1 -0
- package/dist/view/directives/if.d.ts +7 -0
- package/dist/view/directives/if.d.ts.map +1 -0
- package/dist/view/directives/index.d.ts +12 -0
- package/dist/view/directives/index.d.ts.map +1 -0
- package/dist/view/directives/model.d.ts +7 -0
- package/dist/view/directives/model.d.ts.map +1 -0
- package/dist/view/directives/on.d.ts +7 -0
- package/dist/view/directives/on.d.ts.map +1 -0
- package/dist/view/directives/ref.d.ts +7 -0
- package/dist/view/directives/ref.d.ts.map +1 -0
- package/dist/view/directives/show.d.ts +7 -0
- package/dist/view/directives/show.d.ts.map +1 -0
- package/dist/view/directives/style.d.ts +7 -0
- package/dist/view/directives/style.d.ts.map +1 -0
- package/dist/view/directives/text.d.ts +7 -0
- package/dist/view/directives/text.d.ts.map +1 -0
- package/dist/view/evaluate.d.ts +43 -0
- package/dist/view/evaluate.d.ts.map +1 -0
- package/dist/view/index.d.ts +3 -93
- package/dist/view/index.d.ts.map +1 -1
- package/dist/view/mount.d.ts +69 -0
- package/dist/view/mount.d.ts.map +1 -0
- package/dist/view/process.d.ts +26 -0
- package/dist/view/process.d.ts.map +1 -0
- package/dist/view/types.d.ts +36 -0
- package/dist/view/types.d.ts.map +1 -0
- package/dist/view.es.mjs +368 -267
- package/dist/view.es.mjs.map +1 -1
- package/dist/watch-CXyaBC_9.js +58 -0
- package/dist/watch-CXyaBC_9.js.map +1 -0
- package/package.json +132 -132
- package/src/component/component.ts +289 -0
- package/src/component/html.ts +53 -0
- package/src/component/index.ts +40 -414
- package/src/component/props.ts +116 -0
- package/src/component/types.ts +85 -0
- package/src/core/collection.ts +588 -454
- package/src/core/dom.ts +38 -0
- package/src/core/element.ts +746 -740
- package/src/core/index.ts +43 -0
- package/src/core/utils/array.ts +102 -0
- package/src/core/utils/function.ts +110 -0
- package/src/core/utils/index.ts +83 -0
- package/src/core/utils/misc.ts +82 -0
- package/src/core/utils/number.ts +78 -0
- package/src/core/utils/object.ts +206 -0
- package/src/core/utils/string.ts +112 -0
- package/src/core/utils/type-guards.ts +112 -0
- package/src/full.ts +187 -150
- package/src/index.ts +36 -36
- package/src/motion/animate.ts +113 -0
- package/src/motion/easing.ts +40 -0
- package/src/motion/flip.ts +176 -0
- package/src/motion/index.ts +41 -358
- package/src/motion/keyframes.ts +46 -0
- package/src/motion/reduced-motion.ts +17 -0
- package/src/motion/scroll.ts +57 -0
- package/src/motion/spring.ts +150 -0
- package/src/motion/stagger.ts +43 -0
- package/src/motion/timeline.ts +246 -0
- package/src/motion/transition.ts +51 -0
- package/src/motion/types.ts +198 -0
- package/src/reactive/batch.ts +22 -0
- package/src/reactive/computed.ts +92 -0
- package/src/reactive/core.ts +93 -0
- package/src/reactive/effect.ts +43 -0
- package/src/reactive/index.ts +23 -22
- package/src/reactive/internals.ts +105 -0
- package/src/reactive/linked.ts +56 -0
- package/src/reactive/persisted.ts +74 -0
- package/src/reactive/readonly.ts +35 -0
- package/src/reactive/signal.ts +20 -520
- package/src/reactive/type-guards.ts +22 -0
- package/src/reactive/untrack.ts +31 -0
- package/src/reactive/watch.ts +73 -0
- package/src/router/index.ts +41 -718
- package/src/router/links.ts +130 -0
- package/src/router/match.ts +106 -0
- package/src/router/navigation.ts +71 -0
- package/src/router/query.ts +35 -0
- package/src/router/router.ts +211 -0
- package/src/router/state.ts +46 -0
- package/src/router/types.ts +93 -0
- package/src/router/utils.ts +116 -0
- package/src/security/constants.ts +209 -0
- package/src/security/csp.ts +77 -0
- package/src/security/index.ts +4 -12
- package/src/security/sanitize-core.ts +343 -0
- package/src/security/sanitize.ts +66 -625
- package/src/security/trusted-types.ts +69 -0
- package/src/security/types.ts +40 -0
- package/src/store/create-store.ts +329 -0
- package/src/store/define-store.ts +48 -0
- package/src/store/devtools.ts +45 -0
- package/src/store/index.ts +22 -848
- package/src/store/mapping.ts +73 -0
- package/src/store/persisted.ts +61 -0
- package/src/store/plugins.ts +32 -0
- package/src/store/registry.ts +51 -0
- package/src/store/types.ts +94 -0
- package/src/store/utils.ts +141 -0
- package/src/store/watch.ts +52 -0
- package/src/view/directives/bind.ts +23 -0
- package/src/view/directives/class.ts +70 -0
- package/src/view/directives/for.ts +275 -0
- package/src/view/directives/html.ts +19 -0
- package/src/view/directives/if.ts +30 -0
- package/src/view/directives/index.ts +11 -0
- package/src/view/directives/model.ts +56 -0
- package/src/view/directives/on.ts +41 -0
- package/src/view/directives/ref.ts +41 -0
- package/src/view/directives/show.ts +26 -0
- package/src/view/directives/style.ts +47 -0
- package/src/view/directives/text.ts +15 -0
- package/src/view/evaluate.ts +274 -0
- package/src/view/index.ts +112 -1041
- package/src/view/mount.ts +200 -0
- package/src/view/process.ts +92 -0
- package/src/view/types.ts +44 -0
- package/dist/core/utils.d.ts +0 -313
- package/dist/core/utils.d.ts.map +0 -1
- 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
|
+
};
|
package/src/security/index.ts
CHANGED
|
@@ -4,15 +4,7 @@
|
|
|
4
4
|
* @module bquery/security
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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';
|