@bquery/bquery 1.0.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/LICENSE.md +21 -0
- package/README.md +266 -0
- package/dist/component/index.d.ts +155 -0
- package/dist/component/index.d.ts.map +1 -0
- package/dist/component.es.mjs +128 -0
- package/dist/component.es.mjs.map +1 -0
- package/dist/core/collection.d.ts +198 -0
- package/dist/core/collection.d.ts.map +1 -0
- package/dist/core/element.d.ts +301 -0
- package/dist/core/element.d.ts.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/selector.d.ts +11 -0
- package/dist/core/selector.d.ts.map +1 -0
- package/dist/core/shared.d.ts +7 -0
- package/dist/core/shared.d.ts.map +1 -0
- package/dist/core/utils.d.ts +300 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core.es.mjs +1015 -0
- package/dist/core.es.mjs.map +1 -0
- package/dist/full.d.ts +48 -0
- package/dist/full.d.ts.map +1 -0
- package/dist/full.es.mjs +43 -0
- package/dist/full.es.mjs.map +1 -0
- package/dist/full.iife.js +2 -0
- package/dist/full.iife.js.map +1 -0
- package/dist/full.umd.js +2 -0
- package/dist/full.umd.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.mjs +43 -0
- package/dist/index.es.mjs.map +1 -0
- package/dist/motion/index.d.ts +145 -0
- package/dist/motion/index.d.ts.map +1 -0
- package/dist/motion.es.mjs +104 -0
- package/dist/motion.es.mjs.map +1 -0
- package/dist/platform/buckets.d.ts +44 -0
- package/dist/platform/buckets.d.ts.map +1 -0
- package/dist/platform/cache.d.ts +71 -0
- package/dist/platform/cache.d.ts.map +1 -0
- package/dist/platform/index.d.ts +15 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/notifications.d.ts +52 -0
- package/dist/platform/notifications.d.ts.map +1 -0
- package/dist/platform/storage.d.ts +69 -0
- package/dist/platform/storage.d.ts.map +1 -0
- package/dist/platform.es.mjs +245 -0
- package/dist/platform.es.mjs.map +1 -0
- package/dist/reactive/index.d.ts +8 -0
- package/dist/reactive/index.d.ts.map +1 -0
- package/dist/reactive/signal.d.ts +204 -0
- package/dist/reactive/signal.d.ts.map +1 -0
- package/dist/reactive.es.mjs +123 -0
- package/dist/reactive.es.mjs.map +1 -0
- package/dist/security/index.d.ts +8 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/sanitize.d.ts +99 -0
- package/dist/security/sanitize.d.ts.map +1 -0
- package/dist/security.es.mjs +194 -0
- package/dist/security.es.mjs.map +1 -0
- package/package.json +120 -0
- package/src/component/index.ts +360 -0
- package/src/core/collection.ts +339 -0
- package/src/core/element.ts +493 -0
- package/src/core/index.ts +4 -0
- package/src/core/selector.ts +29 -0
- package/src/core/shared.ts +13 -0
- package/src/core/utils.ts +425 -0
- package/src/full.ts +101 -0
- package/src/index.ts +27 -0
- package/src/motion/index.ts +365 -0
- package/src/platform/buckets.ts +115 -0
- package/src/platform/cache.ts +130 -0
- package/src/platform/index.ts +18 -0
- package/src/platform/notifications.ts +87 -0
- package/src/platform/storage.ts +208 -0
- package/src/reactive/index.ts +9 -0
- package/src/reactive/signal.ts +347 -0
- package/src/security/index.ts +18 -0
- package/src/security/sanitize.ts +446 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.es.mjs","sources":["../src/platform/buckets.ts","../src/platform/cache.ts","../src/platform/notifications.ts","../src/platform/storage.ts"],"sourcesContent":["/**\n * Storage Buckets API wrapper.\n * Provides a simplified interface for storing blobs and binary data.\n * Falls back to IndexedDB when Storage Buckets API is not available.\n */\n\n/**\n * Bucket interface for blob storage operations.\n */\nexport interface Bucket {\n /**\n * Store a blob in the bucket.\n * @param key - Unique identifier for the blob\n * @param data - Blob data to store\n */\n put(key: string, data: Blob): Promise<void>;\n\n /**\n * Retrieve a blob from the bucket.\n * @param key - Blob identifier\n * @returns The stored blob or null if not found\n */\n get(key: string): Promise<Blob | null>;\n\n /**\n * Remove a blob from the bucket.\n * @param key - Blob identifier\n */\n remove(key: string): Promise<void>;\n\n /**\n * List all keys in the bucket.\n * @returns Array of blob keys\n */\n keys(): Promise<string[]>;\n}\n\n/**\n * IndexedDB-based bucket implementation.\n * Used as fallback when Storage Buckets API is unavailable.\n */\nclass IndexedDBBucket implements Bucket {\n private dbPromise: Promise<IDBDatabase> | null = null;\n private readonly storeName = 'blobs';\n\n constructor(private readonly bucketName: string) {}\n\n private openDB(): Promise<IDBDatabase> {\n if (this.dbPromise) return this.dbPromise;\n\n const dbName = `bquery-bucket-${this.bucketName}`;\n this.dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(dbName, 1);\n\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(this.storeName)) {\n db.createObjectStore(this.storeName);\n }\n };\n\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n\n return this.dbPromise;\n }\n\n private async withStore<T>(\n mode: IDBTransactionMode,\n operation: (store: IDBObjectStore) => IDBRequest<T>\n ): Promise<T> {\n const db = await this.openDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this.storeName, mode);\n const store = tx.objectStore(this.storeName);\n const request = operation(store);\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n }\n\n async put(key: string, data: Blob): Promise<void> {\n await this.withStore('readwrite', (store) => store.put(data, key));\n }\n\n async get(key: string): Promise<Blob | null> {\n const result = await this.withStore<Blob | undefined>('readonly', (store) => store.get(key));\n return result ?? null;\n }\n\n async remove(key: string): Promise<void> {\n await this.withStore('readwrite', (store) => store.delete(key));\n }\n\n async keys(): Promise<string[]> {\n const result = await this.withStore<IDBValidKey[]>('readonly', (store) => store.getAllKeys());\n return result.map((key) => String(key));\n }\n}\n\n/**\n * Bucket manager for creating and accessing storage buckets.\n */\nexport const buckets = {\n /**\n * Open or create a storage bucket.\n * @param name - Bucket name\n * @returns Bucket instance for blob operations\n */\n async open(name: string): Promise<Bucket> {\n // Storage Buckets API is experimental; use IndexedDB fallback\n return new IndexedDBBucket(name);\n },\n};\n","/**\n * Cache Storage API wrapper.\n * Provides a simplified interface for caching responses and assets.\n */\n\n/**\n * Cache handle interface for managing cached resources.\n */\nexport interface CacheHandle {\n /**\n * Add a resource to the cache by URL.\n * Fetches the resource and stores the response.\n * @param url - URL to fetch and cache\n */\n add(url: string): Promise<void>;\n\n /**\n * Add multiple resources to the cache.\n * @param urls - Array of URLs to fetch and cache\n */\n addAll(urls: string[]): Promise<void>;\n\n /**\n * Store a custom response in the cache.\n * @param url - URL key for the cached response\n * @param response - Response object to cache\n */\n put(url: string, response: Response): Promise<void>;\n\n /**\n * Retrieve a cached response.\n * @param url - URL to look up\n * @returns Cached Response or undefined if not found\n */\n match(url: string): Promise<Response | undefined>;\n\n /**\n * Remove a cached response.\n * @param url - URL to remove from cache\n * @returns True if the entry was deleted\n */\n remove(url: string): Promise<boolean>;\n\n /**\n * Get all cached request URLs.\n * @returns Array of cached URLs\n */\n keys(): Promise<string[]>;\n}\n\n/**\n * Internal cache handle implementation.\n */\nclass CacheHandleImpl implements CacheHandle {\n constructor(private readonly cache: Cache) {}\n\n async add(url: string): Promise<void> {\n await this.cache.add(url);\n }\n\n async addAll(urls: string[]): Promise<void> {\n await this.cache.addAll(urls);\n }\n\n async put(url: string, response: Response): Promise<void> {\n await this.cache.put(url, response);\n }\n\n async match(url: string): Promise<Response | undefined> {\n return this.cache.match(url);\n }\n\n async remove(url: string): Promise<boolean> {\n return this.cache.delete(url);\n }\n\n async keys(): Promise<string[]> {\n const requests = await this.cache.keys();\n return requests.map((req) => req.url);\n }\n}\n\n/**\n * Cache manager for accessing the Cache Storage API.\n */\nexport const cache = {\n /**\n * Check if Cache Storage API is supported.\n * @returns True if caches API is available\n */\n isSupported(): boolean {\n return 'caches' in window;\n },\n\n /**\n * Open or create a named cache.\n * @param name - Cache name\n * @returns CacheHandle for cache operations\n */\n async open(name: string): Promise<CacheHandle> {\n if (!this.isSupported()) {\n throw new Error('bQuery: Cache Storage API not supported');\n }\n const c = await caches.open(name);\n return new CacheHandleImpl(c);\n },\n\n /**\n * Delete a named cache.\n * @param name - Cache name to delete\n * @returns True if the cache was deleted\n */\n async delete(name: string): Promise<boolean> {\n if (!this.isSupported()) {\n return false;\n }\n return caches.delete(name);\n },\n\n /**\n * List all cache names.\n * @returns Array of cache names\n */\n async keys(): Promise<string[]> {\n if (!this.isSupported()) {\n return [];\n }\n return caches.keys();\n },\n};\n","/**\n * Web Notifications API wrapper.\n * Provides a simplified interface for browser notifications.\n */\n\n/**\n * Notification options matching the standard NotificationOptions interface.\n */\nexport interface NotificationOptions {\n /** Body text of the notification */\n body?: string;\n /** Icon URL for the notification */\n icon?: string;\n /** Badge icon for mobile devices */\n badge?: string;\n /** Tag for grouping notifications */\n tag?: string;\n /** Whether to require user interaction */\n requireInteraction?: boolean;\n /** Vibration pattern for mobile devices */\n vibrate?: number[];\n /** Additional data attached to the notification */\n data?: unknown;\n}\n\n/**\n * Notifications manager providing a clean interface for web notifications.\n */\nexport const notifications = {\n /**\n * Check if notifications are supported.\n * @returns True if Notification API is available\n */\n isSupported(): boolean {\n return 'Notification' in window;\n },\n\n /**\n * Get current permission status.\n * @returns Current permission state\n */\n getPermission(): NotificationPermission {\n if (!this.isSupported()) return 'denied';\n return Notification.permission;\n },\n\n /**\n * Request notification permission from the user.\n * @returns Promise resolving to the permission result\n */\n async requestPermission(): Promise<NotificationPermission> {\n if (!this.isSupported()) {\n return 'denied';\n }\n\n if (Notification.permission === 'granted') {\n return 'granted';\n }\n\n if (Notification.permission === 'denied') {\n return 'denied';\n }\n\n return Notification.requestPermission();\n },\n\n /**\n * Send a notification.\n * Requires 'granted' permission.\n * @param title - Notification title\n * @param options - Optional notification settings\n * @returns The Notification instance or null if not permitted\n */\n send(title: string, options?: NotificationOptions): Notification | null {\n if (!this.isSupported()) {\n console.warn('bQuery: Notifications not supported in this browser');\n return null;\n }\n\n if (Notification.permission !== 'granted') {\n console.warn('bQuery: Notification permission not granted');\n return null;\n }\n\n return new Notification(title, options);\n },\n};\n","/**\n * Unified storage adapters for web platform storage APIs.\n * Provides a consistent, promise-based interface with predictable errors.\n */\n\n/**\n * Common interface for all storage adapters.\n * All methods return promises for a unified async API.\n */\nexport interface StorageAdapter {\n /**\n * Retrieve a value by key.\n * @param key - The storage key\n * @returns The stored value or null if not found\n */\n get<T>(key: string): Promise<T | null>;\n\n /**\n * Store a value by key.\n * @param key - The storage key\n * @param value - The value to store\n */\n set<T>(key: string, value: T): Promise<void>;\n\n /**\n * Remove a value by key.\n * @param key - The storage key\n */\n remove(key: string): Promise<void>;\n\n /**\n * Clear all stored values.\n */\n clear(): Promise<void>;\n\n /**\n * Get all storage keys.\n * @returns Array of all keys\n */\n keys(): Promise<string[]>;\n}\n\n/**\n * Abstract base class for web storage adapters (localStorage/sessionStorage).\n * Implements DRY principle by sharing common logic.\n */\nabstract class WebStorageAdapter implements StorageAdapter {\n constructor(protected readonly storage: Storage) {}\n\n async get<T>(key: string): Promise<T | null> {\n const raw = this.storage.getItem(key);\n if (raw === null) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return raw as unknown as T;\n }\n }\n\n async set<T>(key: string, value: T): Promise<void> {\n const serialized = typeof value === 'string' ? value : JSON.stringify(value);\n this.storage.setItem(key, serialized);\n }\n\n async remove(key: string): Promise<void> {\n this.storage.removeItem(key);\n }\n\n async clear(): Promise<void> {\n this.storage.clear();\n }\n\n async keys(): Promise<string[]> {\n return Object.keys(this.storage);\n }\n}\n\n/**\n * localStorage adapter with async interface.\n */\nclass LocalStorageAdapter extends WebStorageAdapter {\n constructor() {\n super(localStorage);\n }\n}\n\n/**\n * sessionStorage adapter with async interface.\n */\nclass SessionStorageAdapter extends WebStorageAdapter {\n constructor() {\n super(sessionStorage);\n }\n}\n\n/**\n * IndexedDB configuration options.\n */\nexport interface IndexedDBOptions {\n /** Database name */\n name: string;\n /** Object store name */\n store: string;\n /** Database version (optional) */\n version?: number;\n}\n\n/**\n * IndexedDB key-value adapter.\n * Wraps IndexedDB with a simple key-value interface.\n */\nclass IndexedDBAdapter implements StorageAdapter {\n private dbPromise: Promise<IDBDatabase> | null = null;\n\n constructor(private readonly options: IndexedDBOptions) {}\n\n /**\n * Opens or creates the IndexedDB database.\n */\n private openDB(): Promise<IDBDatabase> {\n if (this.dbPromise) return this.dbPromise;\n\n this.dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(this.options.name, this.options.version ?? 1);\n\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(this.options.store)) {\n db.createObjectStore(this.options.store);\n }\n };\n\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n\n return this.dbPromise;\n }\n\n /**\n * Executes a transaction on the object store.\n */\n private async withStore<T>(\n mode: IDBTransactionMode,\n operation: (store: IDBObjectStore) => IDBRequest<T>\n ): Promise<T> {\n const db = await this.openDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this.options.store, mode);\n const store = tx.objectStore(this.options.store);\n const request = operation(store);\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n }\n\n async get<T>(key: string): Promise<T | null> {\n const result = await this.withStore<T | undefined>('readonly', (store) => store.get(key));\n return result ?? null;\n }\n\n async set<T>(key: string, value: T): Promise<void> {\n await this.withStore('readwrite', (store) => store.put(value, key));\n }\n\n async remove(key: string): Promise<void> {\n await this.withStore('readwrite', (store) => store.delete(key));\n }\n\n async clear(): Promise<void> {\n await this.withStore('readwrite', (store) => store.clear());\n }\n\n async keys(): Promise<string[]> {\n const result = await this.withStore<IDBValidKey[]>('readonly', (store) => store.getAllKeys());\n return result.map((key) => String(key));\n }\n}\n\n/**\n * Storage factory providing access to different storage adapters.\n */\nexport const storage = {\n /**\n * Create a localStorage adapter.\n * @returns StorageAdapter wrapping localStorage\n */\n local(): StorageAdapter {\n return new LocalStorageAdapter();\n },\n\n /**\n * Create a sessionStorage adapter.\n * @returns StorageAdapter wrapping sessionStorage\n */\n session(): StorageAdapter {\n return new SessionStorageAdapter();\n },\n\n /**\n * Create an IndexedDB adapter with key-value interface.\n * @param options - Database and store configuration\n * @returns StorageAdapter wrapping IndexedDB\n */\n indexedDB(options: IndexedDBOptions): StorageAdapter {\n return new IndexedDBAdapter(options);\n },\n};\n"],"names":["IndexedDBBucket","bucketName","dbName","resolve","reject","request","db","mode","operation","store","key","data","buckets","name","CacheHandleImpl","cache","url","urls","response","req","c","notifications","title","options","WebStorageAdapter","storage","raw","value","serialized","LocalStorageAdapter","SessionStorageAdapter","IndexedDBAdapter"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive module providing fine-grained reactivity primitives.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/reactive
|
|
5
|
+
*/
|
|
6
|
+
export { batch, Computed, computed, effect, persistedSignal, Signal, signal } from './signal';
|
|
7
|
+
export type { CleanupFn, Observer } from './signal';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reactive/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAE9F,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive primitives inspired by fine-grained reactivity.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a minimal but powerful reactive system:
|
|
5
|
+
* - Signal: A reactive value that notifies subscribers when changed
|
|
6
|
+
* - Computed: A derived value that automatically updates when dependencies change
|
|
7
|
+
* - Effect: A side effect that re-runs when its dependencies change
|
|
8
|
+
* - Batch: Group multiple updates to prevent intermediate re-renders
|
|
9
|
+
*
|
|
10
|
+
* @module bquery/reactive
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const count = signal(0);
|
|
15
|
+
* const doubled = computed(() => count.value * 2);
|
|
16
|
+
*
|
|
17
|
+
* effect(() => {
|
|
18
|
+
* console.log(`Count: ${count.value}, Doubled: ${doubled.value}`);
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* batch(() => {
|
|
22
|
+
* count.value = 1;
|
|
23
|
+
* count.value = 2;
|
|
24
|
+
* });
|
|
25
|
+
* // Logs: "Count: 2, Doubled: 4" (only once due to batching)
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Observer function type used internally for tracking reactivity.
|
|
30
|
+
*/
|
|
31
|
+
export type Observer = () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Cleanup function returned by effects for disposal.
|
|
34
|
+
*/
|
|
35
|
+
export type CleanupFn = () => void;
|
|
36
|
+
/**
|
|
37
|
+
* A reactive value container that notifies subscribers on change.
|
|
38
|
+
*
|
|
39
|
+
* Signals are the foundational primitive of the reactive system.
|
|
40
|
+
* Reading a signal's value inside an effect or computed automatically
|
|
41
|
+
* establishes a reactive dependency.
|
|
42
|
+
*
|
|
43
|
+
* @template T - The type of the stored value
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const name = signal('World');
|
|
48
|
+
* console.log(name.value); // 'World'
|
|
49
|
+
*
|
|
50
|
+
* name.value = 'bQuery';
|
|
51
|
+
* console.log(name.value); // 'bQuery'
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare class Signal<T> {
|
|
55
|
+
private _value;
|
|
56
|
+
private subscribers;
|
|
57
|
+
/**
|
|
58
|
+
* Creates a new signal with an initial value.
|
|
59
|
+
* @param _value - The initial value
|
|
60
|
+
*/
|
|
61
|
+
constructor(_value: T);
|
|
62
|
+
/**
|
|
63
|
+
* Gets the current value and tracks the read if inside an observer.
|
|
64
|
+
*/
|
|
65
|
+
get value(): T;
|
|
66
|
+
/**
|
|
67
|
+
* Sets a new value and notifies all subscribers if the value changed.
|
|
68
|
+
* Uses Object.is for equality comparison.
|
|
69
|
+
*/
|
|
70
|
+
set value(next: T);
|
|
71
|
+
/**
|
|
72
|
+
* Reads the current value without tracking.
|
|
73
|
+
* Useful when you need the value but don't want to create a dependency.
|
|
74
|
+
*
|
|
75
|
+
* @returns The current value
|
|
76
|
+
*/
|
|
77
|
+
peek(): T;
|
|
78
|
+
/**
|
|
79
|
+
* Updates the value using a function.
|
|
80
|
+
* Useful for updates based on the current value.
|
|
81
|
+
*
|
|
82
|
+
* @param updater - Function that receives current value and returns new value
|
|
83
|
+
*/
|
|
84
|
+
update(updater: (current: T) => T): void;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A computed value that derives from other reactive sources.
|
|
88
|
+
*
|
|
89
|
+
* Computed values are lazily evaluated and cached. They only
|
|
90
|
+
* recompute when their dependencies change.
|
|
91
|
+
*
|
|
92
|
+
* @template T - The type of the computed value
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* const price = signal(100);
|
|
97
|
+
* const quantity = signal(2);
|
|
98
|
+
* const total = computed(() => price.value * quantity.value);
|
|
99
|
+
*
|
|
100
|
+
* console.log(total.value); // 200
|
|
101
|
+
* price.value = 150;
|
|
102
|
+
* console.log(total.value); // 300
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare class Computed<T> {
|
|
106
|
+
private readonly compute;
|
|
107
|
+
private cachedValue;
|
|
108
|
+
private dirty;
|
|
109
|
+
private subscribers;
|
|
110
|
+
private readonly markDirty;
|
|
111
|
+
/**
|
|
112
|
+
* Creates a new computed value.
|
|
113
|
+
* @param compute - Function that computes the value
|
|
114
|
+
*/
|
|
115
|
+
constructor(compute: () => T);
|
|
116
|
+
/**
|
|
117
|
+
* Gets the computed value, recomputing if dependencies changed.
|
|
118
|
+
*/
|
|
119
|
+
get value(): T;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Creates a new reactive signal.
|
|
123
|
+
*
|
|
124
|
+
* @template T - The type of the signal value
|
|
125
|
+
* @param value - The initial value
|
|
126
|
+
* @returns A new Signal instance
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* const count = signal(0);
|
|
131
|
+
* count.value++; // Triggers subscribers
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export declare const signal: <T>(value: T) => Signal<T>;
|
|
135
|
+
/**
|
|
136
|
+
* Creates a new computed value.
|
|
137
|
+
*
|
|
138
|
+
* @template T - The type of the computed value
|
|
139
|
+
* @param fn - Function that computes the value from reactive sources
|
|
140
|
+
* @returns A new Computed instance
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* const doubled = computed(() => count.value * 2);
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
export declare const computed: <T>(fn: () => T) => Computed<T>;
|
|
148
|
+
/**
|
|
149
|
+
* Creates a side effect that automatically re-runs when dependencies change.
|
|
150
|
+
*
|
|
151
|
+
* The effect runs immediately upon creation and then re-runs whenever
|
|
152
|
+
* any signal or computed value read inside it changes.
|
|
153
|
+
*
|
|
154
|
+
* @param fn - The effect function to run
|
|
155
|
+
* @returns A cleanup function to stop the effect
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* const count = signal(0);
|
|
160
|
+
*
|
|
161
|
+
* const cleanup = effect(() => {
|
|
162
|
+
* document.title = `Count: ${count.value}`;
|
|
163
|
+
* });
|
|
164
|
+
*
|
|
165
|
+
* // Later, to stop the effect:
|
|
166
|
+
* cleanup();
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export declare const effect: (fn: () => void | CleanupFn) => CleanupFn;
|
|
170
|
+
/**
|
|
171
|
+
* Batches multiple signal updates into a single notification cycle.
|
|
172
|
+
*
|
|
173
|
+
* Updates made inside the batch function are deferred until the batch
|
|
174
|
+
* completes, preventing intermediate re-renders and improving performance.
|
|
175
|
+
*
|
|
176
|
+
* @param fn - Function containing multiple signal updates
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* batch(() => {
|
|
181
|
+
* firstName.value = 'John';
|
|
182
|
+
* lastName.value = 'Doe';
|
|
183
|
+
* age.value = 30;
|
|
184
|
+
* });
|
|
185
|
+
* // Effects only run once with all three updates
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
export declare const batch: (fn: () => void) => void;
|
|
189
|
+
/**
|
|
190
|
+
* Creates a signal that persists to localStorage.
|
|
191
|
+
*
|
|
192
|
+
* @template T - The type of the signal value
|
|
193
|
+
* @param key - The localStorage key
|
|
194
|
+
* @param initialValue - The initial value if not found in storage
|
|
195
|
+
* @returns A Signal that syncs with localStorage
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* const theme = persistedSignal('theme', 'light');
|
|
200
|
+
* theme.value = 'dark'; // Automatically saved to localStorage
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
export declare const persistedSignal: <T>(key: string, initialValue: T) => Signal<T>;
|
|
204
|
+
//# sourceMappingURL=signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/reactive/signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC;AAElC;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC;AA2CnC;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,MAAM,CAAC,CAAC;IAOP,OAAO,CAAC,MAAM;IAN1B,OAAO,CAAC,WAAW,CAAuB;IAE1C;;;OAGG;gBACiB,MAAM,EAAE,CAAC;IAE7B;;OAEG;IACH,IAAI,KAAK,IAAI,CAAC,CAMb;IAED;;;OAGG;IACH,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,EAMhB;IAED;;;;;OAKG;IACH,IAAI,IAAI,CAAC;IAIT;;;;;OAKG;IACH,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI;CAGzC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,QAAQ,CAAC,CAAC;IAeT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAdpC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAKxB;IAEF;;;OAGG;gBAC0B,OAAO,EAAE,MAAM,CAAC;IAE7C;;OAEG;IACH,IAAI,KAAK,IAAI,CAAC,CAUb;CACF;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,CAAC,CAAsB,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,IAAI,MAAM,CAAC,KAAG,QAAQ,CAAC,CAAC,CAAqB,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,MAAM,GAAI,IAAI,MAAM,IAAI,GAAG,SAAS,KAAG,SAwBnD,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,KAAK,GAAI,IAAI,MAAM,IAAI,KAAG,IAUtC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,KAAK,MAAM,EAAE,cAAc,CAAC,KAAG,MAAM,CAAC,CAAC,CAwBzE,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
let r = [], c = 0;
|
|
2
|
+
const n = /* @__PURE__ */ new Set(), a = (e, t) => {
|
|
3
|
+
r = [...r, e];
|
|
4
|
+
try {
|
|
5
|
+
return t();
|
|
6
|
+
} finally {
|
|
7
|
+
r = r.slice(0, -1);
|
|
8
|
+
}
|
|
9
|
+
}, l = (e) => {
|
|
10
|
+
if (c > 0) {
|
|
11
|
+
n.add(e);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
e();
|
|
15
|
+
}, o = () => {
|
|
16
|
+
for (const e of Array.from(n))
|
|
17
|
+
n.delete(e), e();
|
|
18
|
+
};
|
|
19
|
+
class h {
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new signal with an initial value.
|
|
22
|
+
* @param _value - The initial value
|
|
23
|
+
*/
|
|
24
|
+
constructor(t) {
|
|
25
|
+
this._value = t, this.subscribers = /* @__PURE__ */ new Set();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Gets the current value and tracks the read if inside an observer.
|
|
29
|
+
*/
|
|
30
|
+
get value() {
|
|
31
|
+
const t = r[r.length - 1];
|
|
32
|
+
return t && this.subscribers.add(t), this._value;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Sets a new value and notifies all subscribers if the value changed.
|
|
36
|
+
* Uses Object.is for equality comparison.
|
|
37
|
+
*/
|
|
38
|
+
set value(t) {
|
|
39
|
+
if (!Object.is(this._value, t)) {
|
|
40
|
+
this._value = t;
|
|
41
|
+
for (const s of this.subscribers)
|
|
42
|
+
l(s);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Reads the current value without tracking.
|
|
47
|
+
* Useful when you need the value but don't want to create a dependency.
|
|
48
|
+
*
|
|
49
|
+
* @returns The current value
|
|
50
|
+
*/
|
|
51
|
+
peek() {
|
|
52
|
+
return this._value;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Updates the value using a function.
|
|
56
|
+
* Useful for updates based on the current value.
|
|
57
|
+
*
|
|
58
|
+
* @param updater - Function that receives current value and returns new value
|
|
59
|
+
*/
|
|
60
|
+
update(t) {
|
|
61
|
+
this.value = t(this._value);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
class f {
|
|
65
|
+
/**
|
|
66
|
+
* Creates a new computed value.
|
|
67
|
+
* @param compute - Function that computes the value
|
|
68
|
+
*/
|
|
69
|
+
constructor(t) {
|
|
70
|
+
this.compute = t, this.dirty = !0, this.subscribers = /* @__PURE__ */ new Set(), this.markDirty = () => {
|
|
71
|
+
this.dirty = !0;
|
|
72
|
+
for (const s of this.subscribers)
|
|
73
|
+
l(s);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Gets the computed value, recomputing if dependencies changed.
|
|
78
|
+
*/
|
|
79
|
+
get value() {
|
|
80
|
+
const t = r[r.length - 1];
|
|
81
|
+
return t && this.subscribers.add(t), this.dirty && (this.dirty = !1, this.cachedValue = a(this.markDirty, this.compute)), this.cachedValue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const b = (e) => new h(e), v = (e) => new f(e), d = (e) => {
|
|
85
|
+
let t, s = !1;
|
|
86
|
+
const i = () => {
|
|
87
|
+
s || (t && t(), t = a(i, e));
|
|
88
|
+
};
|
|
89
|
+
return i(), () => {
|
|
90
|
+
s = !0, t && t();
|
|
91
|
+
};
|
|
92
|
+
}, g = (e) => {
|
|
93
|
+
c += 1;
|
|
94
|
+
try {
|
|
95
|
+
e();
|
|
96
|
+
} finally {
|
|
97
|
+
c -= 1, c === 0 && o();
|
|
98
|
+
}
|
|
99
|
+
}, p = (e, t) => {
|
|
100
|
+
let s = t;
|
|
101
|
+
try {
|
|
102
|
+
const u = localStorage.getItem(e);
|
|
103
|
+
u !== null && (s = JSON.parse(u));
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
const i = b(s);
|
|
107
|
+
return d(() => {
|
|
108
|
+
try {
|
|
109
|
+
localStorage.setItem(e, JSON.stringify(i.value));
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}), i;
|
|
113
|
+
};
|
|
114
|
+
export {
|
|
115
|
+
f as Computed,
|
|
116
|
+
h as Signal,
|
|
117
|
+
g as batch,
|
|
118
|
+
v as computed,
|
|
119
|
+
d as effect,
|
|
120
|
+
p as persistedSignal,
|
|
121
|
+
b as signal
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=reactive.es.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactive.es.mjs","sources":["../src/reactive/signal.ts"],"sourcesContent":["/**\n * Reactive primitives inspired by fine-grained reactivity.\n *\n * This module provides a minimal but powerful reactive system:\n * - Signal: A reactive value that notifies subscribers when changed\n * - Computed: A derived value that automatically updates when dependencies change\n * - Effect: A side effect that re-runs when its dependencies change\n * - Batch: Group multiple updates to prevent intermediate re-renders\n *\n * @module bquery/reactive\n *\n * @example\n * ```ts\n * const count = signal(0);\n * const doubled = computed(() => count.value * 2);\n *\n * effect(() => {\n * console.log(`Count: ${count.value}, Doubled: ${doubled.value}`);\n * });\n *\n * batch(() => {\n * count.value = 1;\n * count.value = 2;\n * });\n * // Logs: \"Count: 2, Doubled: 4\" (only once due to batching)\n * ```\n */\n\n/**\n * Observer function type used internally for tracking reactivity.\n */\nexport type Observer = () => void;\n\n/**\n * Cleanup function returned by effects for disposal.\n */\nexport type CleanupFn = () => void;\n\n// Internal state for tracking the current observer context\nlet observerStack: Observer[] = [];\nlet batchDepth = 0;\nconst pendingObservers = new Set<Observer>();\n\n/**\n * Tracks dependencies during a function execution.\n * @internal\n */\nconst track = <T>(observer: Observer, fn: () => T): T => {\n observerStack = [...observerStack, observer];\n try {\n return fn();\n } finally {\n observerStack = observerStack.slice(0, -1);\n }\n};\n\n/**\n * Schedules an observer to run, respecting batch mode.\n * @internal\n */\nconst scheduleObserver = (observer: Observer) => {\n if (batchDepth > 0) {\n pendingObservers.add(observer);\n return;\n }\n observer();\n};\n\n/**\n * Flushes all pending observers after a batch completes.\n * @internal\n */\nconst flushObservers = () => {\n for (const observer of Array.from(pendingObservers)) {\n pendingObservers.delete(observer);\n observer();\n }\n};\n\n/**\n * A reactive value container that notifies subscribers on change.\n *\n * Signals are the foundational primitive of the reactive system.\n * Reading a signal's value inside an effect or computed automatically\n * establishes a reactive dependency.\n *\n * @template T - The type of the stored value\n *\n * @example\n * ```ts\n * const name = signal('World');\n * console.log(name.value); // 'World'\n *\n * name.value = 'bQuery';\n * console.log(name.value); // 'bQuery'\n * ```\n */\nexport class Signal<T> {\n private subscribers = new Set<Observer>();\n\n /**\n * Creates a new signal with an initial value.\n * @param _value - The initial value\n */\n constructor(private _value: T) {}\n\n /**\n * Gets the current value and tracks the read if inside an observer.\n */\n get value(): T {\n const current = observerStack[observerStack.length - 1];\n if (current) {\n this.subscribers.add(current);\n }\n return this._value;\n }\n\n /**\n * Sets a new value and notifies all subscribers if the value changed.\n * Uses Object.is for equality comparison.\n */\n set value(next: T) {\n if (Object.is(this._value, next)) return;\n this._value = next;\n for (const subscriber of this.subscribers) {\n scheduleObserver(subscriber);\n }\n }\n\n /**\n * Reads the current value without tracking.\n * Useful when you need the value but don't want to create a dependency.\n *\n * @returns The current value\n */\n peek(): T {\n return this._value;\n }\n\n /**\n * Updates the value using a function.\n * Useful for updates based on the current value.\n *\n * @param updater - Function that receives current value and returns new value\n */\n update(updater: (current: T) => T): void {\n this.value = updater(this._value);\n }\n}\n\n/**\n * A computed value that derives from other reactive sources.\n *\n * Computed values are lazily evaluated and cached. They only\n * recompute when their dependencies change.\n *\n * @template T - The type of the computed value\n *\n * @example\n * ```ts\n * const price = signal(100);\n * const quantity = signal(2);\n * const total = computed(() => price.value * quantity.value);\n *\n * console.log(total.value); // 200\n * price.value = 150;\n * console.log(total.value); // 300\n * ```\n */\nexport class Computed<T> {\n private cachedValue!: T;\n private dirty = true;\n private subscribers = new Set<Observer>();\n private readonly markDirty = () => {\n this.dirty = true;\n for (const subscriber of this.subscribers) {\n scheduleObserver(subscriber);\n }\n };\n\n /**\n * Creates a new computed value.\n * @param compute - Function that computes the value\n */\n constructor(private readonly compute: () => T) {}\n\n /**\n * Gets the computed value, recomputing if dependencies changed.\n */\n get value(): T {\n const current = observerStack[observerStack.length - 1];\n if (current) {\n this.subscribers.add(current);\n }\n if (this.dirty) {\n this.dirty = false;\n this.cachedValue = track(this.markDirty, this.compute);\n }\n return this.cachedValue;\n }\n}\n\n/**\n * Creates a new reactive signal.\n *\n * @template T - The type of the signal value\n * @param value - The initial value\n * @returns A new Signal instance\n *\n * @example\n * ```ts\n * const count = signal(0);\n * count.value++; // Triggers subscribers\n * ```\n */\nexport const signal = <T>(value: T): Signal<T> => new Signal(value);\n\n/**\n * Creates a new computed value.\n *\n * @template T - The type of the computed value\n * @param fn - Function that computes the value from reactive sources\n * @returns A new Computed instance\n *\n * @example\n * ```ts\n * const doubled = computed(() => count.value * 2);\n * ```\n */\nexport const computed = <T>(fn: () => T): Computed<T> => new Computed(fn);\n\n/**\n * Creates a side effect that automatically re-runs when dependencies change.\n *\n * The effect runs immediately upon creation and then re-runs whenever\n * any signal or computed value read inside it changes.\n *\n * @param fn - The effect function to run\n * @returns A cleanup function to stop the effect\n *\n * @example\n * ```ts\n * const count = signal(0);\n *\n * const cleanup = effect(() => {\n * document.title = `Count: ${count.value}`;\n * });\n *\n * // Later, to stop the effect:\n * cleanup();\n * ```\n */\nexport const effect = (fn: () => void | CleanupFn): CleanupFn => {\n let cleanupFn: CleanupFn | void;\n let isDisposed = false;\n\n const observer: Observer = () => {\n if (isDisposed) return;\n\n // Run previous cleanup if exists\n if (cleanupFn) {\n cleanupFn();\n }\n\n // Run effect and capture cleanup\n cleanupFn = track(observer, fn);\n };\n\n observer();\n\n return () => {\n isDisposed = true;\n if (cleanupFn) {\n cleanupFn();\n }\n };\n};\n\n/**\n * Batches multiple signal updates into a single notification cycle.\n *\n * Updates made inside the batch function are deferred until the batch\n * completes, preventing intermediate re-renders and improving performance.\n *\n * @param fn - Function containing multiple signal updates\n *\n * @example\n * ```ts\n * batch(() => {\n * firstName.value = 'John';\n * lastName.value = 'Doe';\n * age.value = 30;\n * });\n * // Effects only run once with all three updates\n * ```\n */\nexport const batch = (fn: () => void): void => {\n batchDepth += 1;\n try {\n fn();\n } finally {\n batchDepth -= 1;\n if (batchDepth === 0) {\n flushObservers();\n }\n }\n};\n\n/**\n * Creates a signal that persists to localStorage.\n *\n * @template T - The type of the signal value\n * @param key - The localStorage key\n * @param initialValue - The initial value if not found in storage\n * @returns A Signal that syncs with localStorage\n *\n * @example\n * ```ts\n * const theme = persistedSignal('theme', 'light');\n * theme.value = 'dark'; // Automatically saved to localStorage\n * ```\n */\nexport const persistedSignal = <T>(key: string, initialValue: T): Signal<T> => {\n let stored: T = initialValue;\n\n try {\n const raw = localStorage.getItem(key);\n if (raw !== null) {\n stored = JSON.parse(raw) as T;\n }\n } catch {\n // Use initial value on parse error\n }\n\n const sig = signal(stored);\n\n // Create an effect to persist changes\n effect(() => {\n try {\n localStorage.setItem(key, JSON.stringify(sig.value));\n } catch {\n // Ignore storage errors\n }\n });\n\n return sig;\n};\n"],"names":["observerStack","batchDepth","pendingObservers","track","observer","fn","scheduleObserver","flushObservers","Signal","_value","current","next","subscriber","updater","Computed","compute","signal","value","computed","effect","cleanupFn","isDisposed","batch","persistedSignal","key","initialValue","stored","raw","sig"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security module providing sanitization, CSP compatibility, and Trusted Types.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/security
|
|
5
|
+
*/
|
|
6
|
+
export { createTrustedHtml, escapeHtml, generateNonce, getTrustedTypesPolicy, hasCSPDirective, isTrustedTypesSupported, sanitizeHtml as sanitize, sanitizeHtml, stripTags, } from './sanitize';
|
|
7
|
+
export type { SanitizeOptions } from './sanitize';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/security/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,qBAAqB,EACrB,eAAe,EACf,uBAAuB,EACvB,YAAY,IAAI,QAAQ,EACxB,YAAY,EACZ,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security utilities for HTML sanitization, CSP compatibility, and Trusted Types.
|
|
3
|
+
* All DOM writes are sanitized by default to prevent XSS attacks.
|
|
4
|
+
*
|
|
5
|
+
* @module bquery/security
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Sanitizer configuration options.
|
|
9
|
+
*/
|
|
10
|
+
export interface SanitizeOptions {
|
|
11
|
+
/** Allow these additional tags (default: none) */
|
|
12
|
+
allowTags?: string[];
|
|
13
|
+
/** Allow these additional attributes (default: none) */
|
|
14
|
+
allowAttributes?: string[];
|
|
15
|
+
/** Allow data-* attributes (default: true) */
|
|
16
|
+
allowDataAttributes?: boolean;
|
|
17
|
+
/** Strip all tags and return plain text (default: false) */
|
|
18
|
+
stripAllTags?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Trusted Types policy interface */
|
|
21
|
+
interface TrustedTypePolicy {
|
|
22
|
+
createHTML: (input: string) => TrustedHTML;
|
|
23
|
+
}
|
|
24
|
+
/** Trusted HTML type placeholder for environments without Trusted Types */
|
|
25
|
+
interface TrustedHTML {
|
|
26
|
+
toString(): string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if Trusted Types API is available.
|
|
30
|
+
* @returns True if Trusted Types are supported
|
|
31
|
+
*/
|
|
32
|
+
export declare const isTrustedTypesSupported: () => boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Get or create the bQuery Trusted Types policy.
|
|
35
|
+
* @returns The Trusted Types policy or null if unsupported
|
|
36
|
+
*/
|
|
37
|
+
export declare const getTrustedTypesPolicy: () => TrustedTypePolicy | null;
|
|
38
|
+
/**
|
|
39
|
+
* Sanitize HTML string, removing dangerous elements and attributes.
|
|
40
|
+
* Uses Trusted Types when available for CSP compliance.
|
|
41
|
+
*
|
|
42
|
+
* @param html - The HTML string to sanitize
|
|
43
|
+
* @param options - Sanitization options
|
|
44
|
+
* @returns Sanitized HTML string
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const safe = sanitizeHtml('<div onclick="alert(1)">Hello</div>');
|
|
49
|
+
* // Returns: '<div>Hello</div>'
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare const sanitizeHtml: (html: string, options?: SanitizeOptions) => string;
|
|
53
|
+
/**
|
|
54
|
+
* Create a Trusted HTML value for use with Trusted Types-enabled sites.
|
|
55
|
+
* Falls back to regular string when Trusted Types are unavailable.
|
|
56
|
+
*
|
|
57
|
+
* @param html - The HTML string to wrap
|
|
58
|
+
* @returns Trusted HTML value or sanitized string
|
|
59
|
+
*/
|
|
60
|
+
export declare const createTrustedHtml: (html: string) => TrustedHTML | string;
|
|
61
|
+
/**
|
|
62
|
+
* Escape HTML entities to prevent XSS.
|
|
63
|
+
* Use this for displaying user content as text.
|
|
64
|
+
*
|
|
65
|
+
* @param text - The text to escape
|
|
66
|
+
* @returns Escaped HTML string
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* escapeHtml('<script>alert(1)</script>');
|
|
71
|
+
* // Returns: '<script>alert(1)</script>'
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare const escapeHtml: (text: string) => string;
|
|
75
|
+
/**
|
|
76
|
+
* Strip all HTML tags and return plain text.
|
|
77
|
+
*
|
|
78
|
+
* @param html - The HTML string to strip
|
|
79
|
+
* @returns Plain text content
|
|
80
|
+
*/
|
|
81
|
+
export declare const stripTags: (html: string) => string;
|
|
82
|
+
/**
|
|
83
|
+
* Generate a nonce for inline scripts/styles.
|
|
84
|
+
* Use with Content-Security-Policy nonce directives.
|
|
85
|
+
*
|
|
86
|
+
* @param length - Nonce length (default: 16)
|
|
87
|
+
* @returns Cryptographically random nonce string
|
|
88
|
+
*/
|
|
89
|
+
export declare const generateNonce: (length?: number) => string;
|
|
90
|
+
/**
|
|
91
|
+
* Check if a CSP header is present with specific directive.
|
|
92
|
+
* Useful for feature detection and fallback strategies.
|
|
93
|
+
*
|
|
94
|
+
* @param directive - The CSP directive to check (e.g., 'script-src')
|
|
95
|
+
* @returns True if the directive appears to be enforced
|
|
96
|
+
*/
|
|
97
|
+
export declare const hasCSPDirective: (directive: string) => boolean;
|
|
98
|
+
export {};
|
|
99
|
+
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/security/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,8CAA8C;IAC9C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAsBD,qCAAqC;AACrC,UAAU,iBAAiB;IACzB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,WAAW,CAAC;CAC5C;AAED,2EAA2E;AAC3E,UAAU,WAAW;IACnB,QAAQ,IAAI,MAAM,CAAC;CACpB;AAKD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,QAAO,OAE1C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAO,iBAAiB,GAAG,IAgB5D,CAAC;AAgQF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,UAAS,eAAoB,KAAG,MAE1E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,WAAW,GAAG,MAM9D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,MAUzC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,KAAG,MAExC,CAAC;AAMF;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,SAAQ,MAAW,KAAG,MAOnD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,WAAW,MAAM,KAAG,OAQnD,CAAC"}
|