-
Notifications
You must be signed in to change notification settings - Fork 0
Add wipe_and_recreate method to SQLiteWasmDatabase #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
02413bc
b85dcb0
305a7c7
01cecfc
3ab8cf4
5ae510b
1a6fa53
78c9010
cf1c2d7
b47e95d
f412590
57128f0
83e48cc
4fb9517
fc98572
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ use web_sys::Worker; | |
|
|
||
| use crate::errors::SQLiteWasmDatabaseError; | ||
| use crate::messages::WORKER_ERROR_TYPE_INITIALIZATION_PENDING; | ||
| use crate::opfs::delete_opfs_sahpool_directory; | ||
| use crate::params::normalize_params_js; | ||
| use crate::ready::{InitializationState, ReadySignal}; | ||
| use crate::utils::describe_js_value; | ||
|
|
@@ -19,7 +20,8 @@ use crate::worker_template::generate_self_contained_worker; | |
|
|
||
| #[wasm_bindgen] | ||
| pub struct SQLiteWasmDatabase { | ||
| worker: Worker, | ||
| worker: Rc<RefCell<Worker>>, | ||
| db_name: String, | ||
| pending_queries: Rc<RefCell<HashMap<u32, (js_sys::Function, js_sys::Function)>>>, | ||
| next_request_id: Rc<RefCell<u32>>, | ||
| ready_signal: ReadySignal, | ||
|
|
@@ -63,7 +65,8 @@ impl SQLiteWasmDatabase { | |
| let next_request_id = Rc::new(RefCell::new(1u32)); | ||
|
|
||
| Ok(SQLiteWasmDatabase { | ||
| worker, | ||
| worker: Rc::new(RefCell::new(worker)), | ||
| db_name: db_name.to_string(), | ||
| pending_queries, | ||
| next_request_id, | ||
| ready_signal, | ||
|
|
@@ -112,7 +115,7 @@ impl SQLiteWasmDatabase { | |
| sql: &str, | ||
| params: Option<Array>, | ||
| ) -> Result<String, SQLiteWasmDatabaseError> { | ||
| let worker = &self.worker; | ||
| let worker = Rc::clone(&self.worker); | ||
| let pending_queries = Rc::clone(&self.pending_queries); | ||
| let sql = sql.to_string(); | ||
| let params_array = Self::normalize_params(params)?; | ||
|
|
@@ -154,17 +157,19 @@ impl SQLiteWasmDatabase { | |
| } | ||
|
|
||
| let rid_for_insert = request_id; | ||
| let promise = | ||
| js_sys::Promise::new(&mut |resolve, reject| match worker.post_message(&message) { | ||
| Ok(()) => { | ||
| pending_queries | ||
| .borrow_mut() | ||
| .insert(rid_for_insert, (resolve, reject)); | ||
| } | ||
| Err(err) => { | ||
| let _ = reject.call1(&JsValue::NULL, &err); | ||
| } | ||
| }); | ||
| let promise = js_sys::Promise::new(&mut |resolve, reject| match worker | ||
| .borrow() | ||
| .post_message(&message) | ||
| { | ||
| Ok(()) => { | ||
| pending_queries | ||
| .borrow_mut() | ||
| .insert(rid_for_insert, (resolve, reject)); | ||
| } | ||
| Err(err) => { | ||
| let _ = reject.call1(&JsValue::NULL, &err); | ||
| } | ||
| }); | ||
|
|
||
| let result = match JsFuture::from(promise).await { | ||
| Ok(value) => value, | ||
|
|
@@ -177,6 +182,36 @@ impl SQLiteWasmDatabase { | |
| }; | ||
| Ok(result.as_string().unwrap_or_else(|| format!("{result:?}"))) | ||
| } | ||
|
|
||
| #[wasm_export(js_name = "wipeAndRecreate", unchecked_return_type = "void")] | ||
| pub async fn wipe_and_recreate(&self) -> Result<(), SQLiteWasmDatabaseError> { | ||
| self.worker.borrow().terminate(); | ||
|
|
||
| for (_, (_, reject)) in self.pending_queries.borrow_mut().drain() { | ||
| let err = JsValue::from_str("Database wipe in progress"); | ||
| let _ = reject.call1(&JsValue::NULL, &err); | ||
| } | ||
|
|
||
| self.ready_signal.reset(); | ||
|
|
||
| let deletion_result = delete_opfs_sahpool_directory().await; | ||
|
|
||
| let worker_code = generate_self_contained_worker(&self.db_name); | ||
| let new_worker = | ||
| create_worker_from_code(&worker_code).map_err(SQLiteWasmDatabaseError::JsError)?; | ||
|
|
||
| install_onmessage_handler( | ||
| &new_worker, | ||
| Rc::clone(&self.pending_queries), | ||
| self.ready_signal.clone(), | ||
| ); | ||
|
|
||
| *self.worker.borrow_mut() = new_worker; | ||
|
|
||
| self.wait_until_ready().await?; | ||
|
|
||
| deletion_result | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
|
|
||
| fn is_initialization_pending_error(err: &JsValue) -> bool { | ||
|
|
@@ -294,4 +329,77 @@ mod tests { | |
| let js_val = JsValue::from_str(WORKER_ERROR_TYPE_INITIALIZATION_PENDING); | ||
| assert!(is_initialization_pending_error(&js_val)); | ||
| } | ||
|
|
||
| #[wasm_bindgen_test(async)] | ||
| async fn wipe_and_recreate_tests() { | ||
| let db = SQLiteWasmDatabase::new("test_wipe").await.unwrap(); | ||
| db.wipe_and_recreate().await.unwrap(); | ||
|
|
||
| db.query( | ||
| "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)", | ||
| None, | ||
| ) | ||
| .await | ||
| .unwrap(); | ||
| db.query("INSERT INTO users (name) VALUES ('Alice')", None) | ||
| .await | ||
| .unwrap(); | ||
|
|
||
| let result = db | ||
| .query("SELECT COUNT(*) as count FROM users", None) | ||
| .await | ||
| .unwrap(); | ||
| assert!(result.contains("\"count\": 1")); | ||
|
|
||
| db.wipe_and_recreate().await.unwrap(); | ||
|
|
||
| let result = db.query("SELECT * FROM users", None).await; | ||
| assert!(result.is_err() || result.unwrap().contains("no such table")); | ||
|
|
||
| let create_result = db | ||
| .query( | ||
| "CREATE TABLE new_table (id INTEGER PRIMARY KEY, value TEXT)", | ||
| None, | ||
| ) | ||
| .await; | ||
| assert!(create_result.is_ok()); | ||
|
|
||
| let insert_result = db | ||
| .query("INSERT INTO new_table (value) VALUES ('test')", None) | ||
| .await; | ||
| assert!(insert_result.is_ok()); | ||
|
|
||
| let select_result = db.query("SELECT * FROM new_table", None).await.unwrap(); | ||
| assert!(select_result.contains("test")); | ||
|
|
||
| for i in 0..3 { | ||
| db.query(&format!("CREATE TABLE t{} (id INTEGER)", i), None) | ||
| .await | ||
| .unwrap(); | ||
| db.wipe_and_recreate().await.unwrap(); | ||
| } | ||
|
|
||
| let result = db | ||
| .query("SELECT name FROM sqlite_master WHERE type='table'", None) | ||
| .await | ||
| .unwrap(); | ||
| assert!(!result.contains("t0")); | ||
| assert!(!result.contains("t1")); | ||
| assert!(!result.contains("t2")); | ||
|
|
||
| let arr = Array::new(); | ||
| arr.push(&JsValue::from_f64(f64::NAN)); | ||
| let res = db.query("SELECT ?", Some(arr)).await; | ||
| assert!(res.is_err(), "NaN should be rejected"); | ||
|
|
||
| let arr = Array::new(); | ||
| arr.push(&JsValue::from_f64(f64::INFINITY)); | ||
| let res = db.query("SELECT ?", Some(arr)).await; | ||
| assert!(res.is_err(), "+Infinity should be rejected"); | ||
|
|
||
| let arr = Array::new(); | ||
| arr.push(&JsValue::from_f64(f64::NEG_INFINITY)); | ||
| let res = db.query("SELECT ?", Some(arr)).await; | ||
| assert!(res.is_err(), "-Infinity should be rejected"); | ||
| } | ||
|
Comment on lines
+333
to
+404
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider splitting the test into focused test cases. This test bundles wipe/recreate scenarios, consecutive wipes, and parameter validation (NaN/Infinity) into a single function. While functional, isolated tests improve debuggability and make failures easier to diagnose. The invalid parameter tests (lines 390-403) are unrelated to 🤖 Prompt for AI Agents
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have opted to use a single test because the database persists between the tests and it was causing us issues. By doing it this way, we make sure that we have we are testing everything properly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
✏️ Learnings added
🧠 Learnings used |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| mod db; | ||
| mod errors; | ||
| mod messages; | ||
| mod opfs; | ||
| mod params; | ||
| mod ready; | ||
| mod utils; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,154 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use js_sys::Reflect; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use wasm_bindgen::prelude::*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use wasm_bindgen_futures::JsFuture; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use web_sys::{FileSystemDirectoryHandle, FileSystemGetDirectoryOptions, FileSystemRemoveOptions}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::errors::SQLiteWasmDatabaseError; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::utils::describe_js_value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const SAHPOOL_DIR_NAME: &str = ".opfs-sahpool"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub async fn delete_opfs_sahpool_directory() -> Result<(), SQLiteWasmDatabaseError> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let root = get_opfs_root().await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let sahpool_dir = match get_directory_if_exists(&root, SAHPOOL_DIR_NAME).await? { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some(dir) => dir, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None => return Ok(()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| delete_directory_contents(&sahpool_dir).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let remove_options = FileSystemRemoveOptions::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remove_options.set_recursive(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsFuture::from(root.remove_entry_with_options(SAHPOOL_DIR_NAME, &remove_options)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .await | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SQLiteWasmDatabaseError::OpfsDeletionFailed(format!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "failed to remove sahpool directory: {}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe_js_value(&e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async fn get_opfs_root() -> Result<FileSystemDirectoryHandle, SQLiteWasmDatabaseError> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let navigator = web_sys::window() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(|w| w.navigator()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .or_else(|| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let global = js_sys::global(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Reflect::get(&global, &JsValue::from_str("navigator")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ok() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .and_then(|n| n.dyn_into::<web_sys::Navigator>().ok()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ok_or_else(|| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SQLiteWasmDatabaseError::OpfsDeletionFailed("navigator not available".into()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let storage = navigator.storage(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsFuture::from(storage.get_directory()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .await | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .and_then(|v| v.dyn_into()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SQLiteWasmDatabaseError::OpfsDeletionFailed(format!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "failed to get OPFS root: {}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe_js_value(&e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async fn get_directory_if_exists( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent: &FileSystemDirectoryHandle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: &str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> Result<Option<FileSystemDirectoryHandle>, SQLiteWasmDatabaseError> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let options = FileSystemGetDirectoryOptions::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options.set_create(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(handle) => handle.dyn_into().map(Some).map_err(|e| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SQLiteWasmDatabaseError::OpfsDeletionFailed(format!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "directory handle type mismatch: {}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe_js_value(&e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Err(e) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(dom_ex) = e.dyn_ref::<web_sys::DomException>() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if dom_ex.name() == "NotFoundError" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Ok(None); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Err(SQLiteWasmDatabaseError::OpfsDeletionFailed(format!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "failed to get directory '{}': {}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe_js_value(&e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's locate and read the opfs.rs file
find . -name "opfs.rs" -type fRepository: rainlanguage/sqlite-web Length of output: 100 🏁 Script executed: # Check the current content of the file around lines 60-75
cat -n packages/sqlite-web/src/opfs.rs | sed -n '50,85p'Repository: rainlanguage/sqlite-web Length of output: 1506 🏁 Script executed: # Search for DomException usage in the codebase
rg "DomException" --type rustRepository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Look for similar error handling patterns with NotFoundError or dyn_ref
rg "NotFoundError\|dyn_ref.*DomException" --type rust -A 3Repository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Check the imports at the top of opfs.rs
head -30 packages/sqlite-web/src/opfs.rsRepository: rainlanguage/sqlite-web Length of output: 1123 🏁 Script executed: # Check how get_directory_if_exists is used in the code
rg "get_directory_if_exists" --type rust -B 2 -A 2Repository: rainlanguage/sqlite-web Length of output: 665 🏁 Script executed: # Look at the broader context - check if web_sys has DomException or similar
rg "web_sys::" packages/sqlite-web/src/opfs.rsRepository: rainlanguage/sqlite-web Length of output: 273 🏁 Script executed: # Check what web_sys exports - look for any exception/error handling
grep -r "DomException\|Exception" packages/sqlite-web/Cargo.tomlRepository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Check Cargo.toml to understand web_sys version
cat packages/sqlite-web/Cargo.toml | head -50Repository: rainlanguage/sqlite-web Length of output: 616 🏁 Script executed: # Look at other error handling in the file to see patterns
rg "Err\(" packages/sqlite-web/src/opfs.rs -A 1Repository: rainlanguage/sqlite-web Length of output: 100 🏁 Script executed: # Check web-sys version and available exports
cat packages/Cargo.toml | grep -A 10 "web-sys"Repository: rainlanguage/sqlite-web Length of output: 118 🏁 Script executed: # Look for any existing error handling with js_sys or exceptions
rg "js_sys::" packages/sqlite-web/src/ -A 2Repository: rainlanguage/sqlite-web Length of output: 16156 🏁 Script executed: # Search the entire codebase for NotFoundError handling pattern
rg "NotFound" --type rustRepository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Check if there's documentation or examples in the repo about error handling
find . -name "*.rs" -type f -exec grep -l "DomException\|get_directory_handle" {} \;Repository: rainlanguage/sqlite-web Length of output: 100 🏁 Script executed: # Let's check web-sys docs indirectly by looking at what's available
# First, check if there's a Cargo.lock file that shows actual versions
head -100 Cargo.lock | grep -A 5 "name = \"web-sys\""Repository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Check the workspace Cargo.toml to see web-sys configuration
cat Cargo.toml | grep -A 20 "web-sys"Repository: rainlanguage/sqlite-web Length of output: 551 🏁 Script executed: # Look at the errors.rs file to understand error handling patterns
cat -n packages/sqlite-web/src/errors.rs | head -50Repository: rainlanguage/sqlite-web Length of output: 1826 🏁 Script executed: # Check if there's any error name checking pattern in the codebase
rg "name()\|\.name\|\.type" packages/sqlite-web/src/ --type rustRepository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Search for how File System API errors are handled elsewhere
rg "get_directory_handle\|get_file_handle\|remove_entry" packages/sqlite-web/src/ --type rust -B 2 -A 5Repository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Check web-sys documentation in a comment or see if there's information about DomException
python3 << 'EOF'
# Query web for web_sys DomException availability
import subprocess
result = subprocess.run(['rg', 'DomException', '--type', 'rust'], cwd='.', capture_output=True, text=True)
print("DomException search result (should be empty):", result.returncode)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
EOFRepository: rainlanguage/sqlite-web Length of output: 153 🏁 Script executed: # Let's check if there's a way to check error names in web-sys
# Look for any pattern matching on error types
rg "dyn_ref\|dyn_into" packages/sqlite-web/src/ --type rust -B 1 -A 1Repository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Check web-sys features more comprehensively
rg "DomException" --type rustRepository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Let's verify if there's an alternative way to check error names
# Search for how errors are handled with Reflect pattern
rg "Reflect::get.*name\|error.*name" --type rust -B 2 -A 2Repository: rainlanguage/sqlite-web Length of output: 49 🏁 Script executed: # Check if there are any examples of checking JS error types in the codebase
cat -n packages/sqlite-web/src/opfs.rs | sed -n '1,120p'Repository: rainlanguage/sqlite-web Length of output: 4969 🏁 Script executed: # Verify web-sys version and check if DomException feature exists
web_sys_version=$(rg 'web-sys.*version' Cargo.toml | head -1)
echo "Web-sys configuration: $web_sys_version"Repository: rainlanguage/sqlite-web Length of output: 131 🌐 Web query:
💡 Result: In [dependencies.web-sys]
version = "0.3"
features = ["DomException"]
Add The current implementation swallows all errors with The suggested fix requires adding the 🛠️ Required changesIn web-sys = { version = "0.3", features = [
"Blob",
"BlobPropertyBag",
"Url",
"Worker",
"WorkerOptions",
"WorkerType",
"BroadcastChannel",
"MessageEvent",
"DedicatedWorkerGlobalScope",
"Navigator",
"Window",
"Location",
"StorageManager",
"FileSystemDirectoryHandle",
"FileSystemHandle",
"FileSystemRemoveOptions",
"FileSystemGetDirectoryOptions",
"DomException"
]}In -use web_sys::{FileSystemDirectoryHandle, FileSystemGetDirectoryOptions, FileSystemRemoveOptions};
+use web_sys::{
+ DomException, FileSystemDirectoryHandle, FileSystemGetDirectoryOptions, FileSystemRemoveOptions,
+};Update match JsFuture::from(parent.get_directory_handle_with_options(name, &options)).await {
Ok(handle) => handle.dyn_into().map(Some).map_err(|e| {
SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
"directory handle type mismatch: {}",
describe_js_value(&e)
))
}),
- Err(_) => Ok(None),
+ Err(err) => {
+ if let Some(dom) = err.dyn_ref::<DomException>() {
+ if dom.name() == "NotFoundError" {
+ return Ok(None);
+ }
+ }
+ Err(SQLiteWasmDatabaseError::OpfsDeletionFailed(format!(
+ "failed to access directory {name}: {}",
+ describe_js_value(&err)
+ )))
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async fn delete_directory_contents( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dir: &FileSystemDirectoryHandle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> Result<(), SQLiteWasmDatabaseError> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let entry_names = collect_entry_names(dir).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for name in entry_names { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let remove_options = FileSystemRemoveOptions::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remove_options.set_recursive(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let _ = JsFuture::from(dir.remove_entry_with_options(&name, &remove_options)).await; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async fn collect_entry_names( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dir: &FileSystemDirectoryHandle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> Result<Vec<String>, SQLiteWasmDatabaseError> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let entries_iter = dir.entries(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut names = Vec::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loop { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let next_fn = Reflect::get(&entries_iter, &JsValue::from_str("next")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ok() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .and_then(|f| f.dyn_into::<js_sys::Function>().ok()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ok_or_else(|| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SQLiteWasmDatabaseError::OpfsDeletionFailed( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "entries iterator missing next method".into(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let next_promise = next_fn.call0(&entries_iter).map_err(|e| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SQLiteWasmDatabaseError::OpfsDeletionFailed(format!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "failed to call next(): {}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe_js_value(&e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let result = JsFuture::from(js_sys::Promise::from(next_promise)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .await | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SQLiteWasmDatabaseError::OpfsDeletionFailed(format!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "iterator next() failed: {}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe_js_value(&e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let done = Reflect::get(&result, &JsValue::from_str("done")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ok() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .and_then(|v| v.as_bool()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .unwrap_or(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if done { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(name) = Reflect::get(&result, &JsValue::from_str("value")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ok() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .and_then(|v| Reflect::get(&v, &JsValue::from_f64(0.0)).ok()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .and_then(|v| v.as_string()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| names.push(name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(names) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.