@livx.cc/appwrap 0.54.0 → 0.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/package.json +5 -4
  2. package/runtime/app/main-page.ts +0 -2
  3. package/runtime/app/shell/capabilities.manifest.ts +27 -92
  4. package/runtime/app/shell/config.ts +4 -0
  5. package/runtime/app/shell/env.ts +8 -1
  6. package/runtime/app/shell/handlers.ts +2 -2
  7. package/runtime/tests/bridge-response-delivery.test.ts +10 -1
  8. package/runtime/tests/env-scheme.test.ts +50 -0
  9. package/scripts/stage-assets.mjs +1 -1
  10. package/src/cli.ts +424 -621
  11. package/src/config.ts +8 -1
  12. package/src/handlers.ts +1 -1
  13. package/src/packs.ts +174 -0
  14. package/src/testing.ts +173 -0
  15. package/templates/module-pack/README.md +44 -0
  16. package/templates/module-pack/handler.ts +9 -0
  17. package/templates/module-pack/manifest.ts +27 -0
  18. package/templates/module-pack/native-src/__MODULE_NAME__/.gitkeep +0 -0
  19. package/runtime/app/shell/billing-offer.ts +0 -54
  20. package/runtime/app/shell/handlers-billing.ts +0 -662
  21. package/runtime/app/shell/handlers-health.ts +0 -178
  22. package/runtime/app/shell/handlers-widget.ts +0 -97
  23. package/runtime/modules-native/billing/App_Resources/iOS/src/AppwrapManageSubscriptions.swift +0 -44
  24. package/runtime/modules-native/health/App_Resources/Android/src/main/java/cc/livx/appwrap/HealthConnectBridge.kt +0 -74
  25. package/runtime/modules-native/widget/App_Resources/Android/src/main/java/cc/livx/appwrap/AppwrapWidgetProvider.kt +0 -118
  26. package/runtime/modules-native/widget/App_Resources/Android/src/main/res/drawable/appwrap_badge_bg.xml +0 -9
  27. package/runtime/modules-native/widget/App_Resources/Android/src/main/res/layout/appwrap_widget.xml +0 -123
  28. package/runtime/modules-native/widget/App_Resources/Android/src/main/res/values/appwrap_widget_colors.xml +0 -10
  29. package/runtime/modules-native/widget/App_Resources/Android/src/main/res/values/appwrap_widget_styles.xml +0 -67
  30. package/runtime/modules-native/widget/App_Resources/Android/src/main/res/values-night/appwrap_widget_colors.xml +0 -9
  31. package/runtime/modules-native/widget/App_Resources/Android/src/main/res/xml/appwrap_widget_info.xml +0 -17
  32. package/runtime/modules-native/widget/App_Resources/iOS/extensions/AppwrapWidget/AppwrapWidget.entitlements +0 -10
  33. package/runtime/modules-native/widget/App_Resources/iOS/extensions/AppwrapWidget/AppwrapWidget.swift +0 -275
  34. package/runtime/modules-native/widget/App_Resources/iOS/extensions/AppwrapWidget/Info.plist +0 -25
  35. package/runtime/modules-native/widget/App_Resources/iOS/extensions/AppwrapWidget/extension.json +0 -9
  36. package/runtime/modules-native/widget/App_Resources/iOS/src/AppwrapWidgetReload.swift +0 -18
  37. package/runtime/tests/billing-offer.test.ts +0 -61
  38. package/runtime-desktop/autotest/index.html +0 -77
  39. package/runtime-desktop/bridge-shim/build.sh +0 -9
  40. package/runtime-desktop/bridge-shim/content-entry.ts +0 -87
  41. package/runtime-desktop/bridge-shim/shim.ts +0 -132
  42. package/runtime-desktop/chrome/appwrap-browser-chrome.html +0 -185
  43. package/runtime-desktop/crates/webview-control/Cargo.lock +0 -330
  44. package/runtime-desktop/crates/webview-control/Cargo.toml +0 -28
  45. package/runtime-desktop/crates/webview-control/examples/host_harness.rs +0 -362
  46. package/runtime-desktop/crates/webview-control/src/embedded.rs +0 -138
  47. package/runtime-desktop/crates/webview-control/src/lib.rs +0 -100
  48. package/runtime-desktop/crates/webview-control/src/popup.rs +0 -522
  49. package/runtime-desktop/crates/webview-control/src/profiles.rs +0 -57
  50. package/runtime-desktop/crates/webview-control/src/wkwebview_backend.rs +0 -558
  51. package/runtime-desktop/src-tauri/Cargo.lock +0 -5474
  52. package/runtime-desktop/src-tauri/Cargo.toml +0 -45
  53. package/runtime-desktop/src-tauri/build.rs +0 -3
  54. package/runtime-desktop/src-tauri/capabilities/default.json +0 -6
  55. package/runtime-desktop/src-tauri/icons/icon.png +0 -0
  56. package/runtime-desktop/src-tauri/shell_config.json +0 -10
  57. package/runtime-desktop/src-tauri/src/biometrics_mac.rs +0 -138
  58. package/runtime-desktop/src-tauri/src/bridge_mac.rs +0 -424
  59. package/runtime-desktop/src-tauri/src/browser_tab.rs +0 -804
  60. package/runtime-desktop/src-tauri/src/main.rs +0 -1228
  61. package/runtime-desktop/src-tauri/src/notifications_mac.rs +0 -202
  62. package/runtime-desktop/src-tauri/src/oauth_mac.rs +0 -139
  63. package/runtime-desktop/src-tauri/src/plugin_host.rs +0 -522
  64. package/runtime-desktop/src-tauri/src/popup_mac.rs +0 -5
  65. package/runtime-desktop/src-tauri/src/push_mac.rs +0 -193
  66. package/runtime-desktop/src-tauri/src/server.rs +0 -156
  67. package/runtime-desktop/src-tauri/src/sidecar.rs +0 -398
  68. package/runtime-desktop/src-tauri/tauri.conf.json +0 -14
  69. package/src/desktop.ts +0 -300
  70. package/src/plugin/host.ts +0 -242
@@ -1,398 +0,0 @@
1
- //! Per-app extensibility: a Bun sidecar running the app's custom TS handlers (`desktop.handlers`).
2
- //!
3
- //! When `shell_config.json` carries a `handlers` path, `init()` spawns `bun <path>` with piped stdio at
4
- //! setup and reads its one-line `ready` announcement (methods it serves). The main bridge router then
5
- //! forwards any CLAIMED method to the sidecar over NDJSON and blocks on the matching response (per-call
6
- //! timeout). A single reader thread drains stdout and resolves the pending map by id; stderr is prefixed
7
- //! through to the shell's stderr.
8
- //!
9
- //! Wire protocol (appwrap sidecar protocol v1 — one JSON object per line):
10
- //! sidecar → shell (spawn): {"kind":"ready","methods":[…]}
11
- //! shell → sidecar: {"kind":"request","id":"…","method":"…","params":{…}}
12
- //! sidecar → shell: {"kind":"response","id":"…","ok":true,"data":{…}}
13
- //! or: {"kind":"response","id":"…","ok":false,"error":{"code":"…","message":"…"}}
14
- //!
15
- //! DEAD-SIDECAR SEMANTICS: a crash/EOF fails all in-flight calls with NATIVE_ERROR and marks the sidecar
16
- //! dead; subsequent calls to a still-CLAIMED method also return NATIVE_ERROR (not UNSUPPORTED). Rationale:
17
- //! these methods are app-custom — the kit has NO web fallback for them (its hybrid fallback only fires on
18
- //! UNSUPPORTED, and only for built-in kit modules), so returning UNSUPPORTED would gain no graceful
19
- //! web-degrade and would dishonestly report "feature absent" when the truth is "the handler backend
20
- //! crashed". NATIVE_ERROR lets the app detect the failure and degrade on its own terms.
21
-
22
- use serde_json::{json, Value};
23
- use std::collections::{HashMap, HashSet};
24
- use std::io::{BufRead, BufReader, Write};
25
- use std::process::{Child, ChildStdin, Command, Stdio};
26
- use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
27
- use std::sync::mpsc::{self, Receiver, Sender, SyncSender};
28
- use std::sync::{Arc, Mutex, OnceLock};
29
- use std::time::Duration;
30
-
31
- /// A dynamic-code error the app-custom sidecar can return (kit codes are open-ended app-side, so unlike
32
- /// the built-in handlers' `&'static str` codes we keep the sidecar's code string verbatim).
33
- pub type SidecarResult = Result<Value, (String, String)>;
34
-
35
- const READY_TIMEOUT: Duration = Duration::from_secs(5);
36
- const CALL_TIMEOUT: Duration = Duration::from_secs(30);
37
-
38
- struct Sidecar {
39
- methods: HashSet<String>,
40
- alive: Arc<AtomicBool>,
41
- stdin: Mutex<ChildStdin>,
42
- pending: Arc<Mutex<HashMap<String, Sender<Value>>>>,
43
- child: Mutex<Child>,
44
- next_id: AtomicU64,
45
- }
46
-
47
- static SIDECAR: OnceLock<Sidecar> = OnceLock::new();
48
-
49
- /// Parse one sidecar stdout line into a routable outcome. Pure — unit-tested.
50
- enum Line {
51
- Ready(Vec<String>),
52
- Response { id: String, value: Value },
53
- Other,
54
- }
55
-
56
- fn parse_line(line: &str) -> Line {
57
- let v: Value = match serde_json::from_str(line) {
58
- Ok(v) => v,
59
- Err(_) => return Line::Other,
60
- };
61
- match v["kind"].as_str() {
62
- Some("ready") => {
63
- let methods = v["methods"]
64
- .as_array()
65
- .map(|a| a.iter().filter_map(|m| m.as_str().map(str::to_string)).collect())
66
- .unwrap_or_default();
67
- Line::Ready(methods)
68
- }
69
- Some("response") => match v["id"].as_str() {
70
- Some(id) => Line::Response { id: id.to_string(), value: v },
71
- None => Line::Other,
72
- },
73
- _ => Line::Other,
74
- }
75
- }
76
-
77
- /// Translate a sidecar `response` envelope into a router result. Pure — unit-tested.
78
- fn response_outcome(resp: &Value) -> SidecarResult {
79
- if resp["ok"].as_bool() == Some(true) {
80
- Ok(resp.get("data").cloned().unwrap_or(Value::Null))
81
- } else {
82
- let code = resp["error"]["code"].as_str().unwrap_or("NATIVE_ERROR").to_string();
83
- let message = resp["error"]["message"].as_str().unwrap_or("handler error").to_string();
84
- Err((code, message))
85
- }
86
- }
87
-
88
- /// Resolve which Bun binary to spawn: the CLI-stamped absolute runtime (`handlersRuntime` =
89
- /// `process.execPath` of the bun that built the app) when it still exists, else a bare `bun`
90
- /// PATH lookup. The stamped path matters for GUI launches: Finder/launchd PATH is
91
- /// /usr/bin:/bin:/usr/sbin:/sbin — no ~/.bun/bin — so `bun` alone is unresolvable there.
92
- fn resolve_runtime(stamped: &str) -> String {
93
- if !stamped.is_empty() && std::path::Path::new(stamped).exists() {
94
- return stamped.to_string();
95
- }
96
- if !stamped.is_empty() {
97
- eprintln!("[sidecar] stamped runtime missing ({stamped}) — falling back to `bun` (PATH lookup; GUI-launched apps have a minimal launchd PATH and may fail to spawn)");
98
- } else {
99
- eprintln!("[sidecar] no stamped runtime — falling back to `bun` (PATH lookup; GUI-launched apps have a minimal launchd PATH and may fail to spawn)");
100
- }
101
- "bun".to_string()
102
- }
103
-
104
- /// Drop any claimed method that falls under a built-in namespace so an app handler can never shadow
105
- /// a built-in (e.g. `storage.get`). Pure — unit-tested. Warns with the rejected list.
106
- fn filter_claims(methods: Vec<String>) -> Vec<String> {
107
- let (rejected, kept): (Vec<String>, Vec<String>) = methods
108
- .into_iter()
109
- .partition(|m| crate::BUILTIN_METHOD_PREFIXES.iter().any(|p| m.starts_with(p)));
110
- if !rejected.is_empty() {
111
- eprintln!("[sidecar] rejecting handler method(s) that shadow built-ins: {rejected:?} — rename them outside the built-in namespaces");
112
- }
113
- kept
114
- }
115
-
116
- /// Spawn the Bun sidecar and block (≤5s, bounded by READY_TIMEOUT) on its `ready` line — intentional:
117
- /// sidecar methods must be claimable before the webview's first invoke. On timeout / malformed / spawn
118
- /// failure the error is logged and the shell runs WITHOUT a sidecar (the feature degrades to inert).
119
- /// Idempotent-ish: only the first successful init wins (OnceLock).
120
- pub fn init(path: &str, runtime: &str) {
121
- if SIDECAR.get().is_some() {
122
- return;
123
- }
124
- let bun = resolve_runtime(runtime);
125
- let mut child = match Command::new(&bun)
126
- .arg(path)
127
- .stdin(Stdio::piped())
128
- .stdout(Stdio::piped())
129
- .stderr(Stdio::piped())
130
- .spawn()
131
- {
132
- Ok(c) => c,
133
- Err(e) => {
134
- eprintln!("[sidecar] spawn `{bun} {path}` failed: {e} — running without custom handlers");
135
- return;
136
- }
137
- };
138
-
139
- let stdin = child.stdin.take().expect("piped stdin");
140
- let stdout = child.stdout.take().expect("piped stdout");
141
- let stderr = child.stderr.take();
142
-
143
- let alive = Arc::new(AtomicBool::new(true));
144
- let pending: Arc<Mutex<HashMap<String, Sender<Value>>>> = Arc::new(Mutex::new(HashMap::new()));
145
-
146
- // stderr → shell stderr (prefixed).
147
- if let Some(stderr) = stderr {
148
- std::thread::spawn(move || {
149
- for line in BufReader::new(stderr).lines().map_while(Result::ok) {
150
- eprintln!("[sidecar:err] {line}");
151
- }
152
- });
153
- }
154
-
155
- // Reader thread: first line → ready (via ready_tx), then resolve responses by id.
156
- let (ready_tx, ready_rx): (SyncSender<Option<Vec<String>>>, Receiver<Option<Vec<String>>>) =
157
- mpsc::sync_channel(1);
158
- let r_alive = alive.clone();
159
- let r_pending = pending.clone();
160
- std::thread::spawn(move || {
161
- let mut reader = BufReader::new(stdout);
162
- let mut got_ready = false;
163
- let mut line = String::new();
164
- loop {
165
- line.clear();
166
- match reader.read_line(&mut line) {
167
- Ok(0) | Err(_) => break, // EOF or read error → sidecar gone
168
- Ok(_) => {}
169
- }
170
- let trimmed = line.trim();
171
- if trimmed.is_empty() {
172
- continue;
173
- }
174
- match parse_line(trimmed) {
175
- Line::Ready(methods) => {
176
- if !got_ready {
177
- got_ready = true;
178
- let _ = ready_tx.send(Some(methods));
179
- }
180
- }
181
- Line::Response { id, value } => {
182
- if let Some(tx) = r_pending.lock().unwrap().remove(&id) {
183
- let _ = tx.send(value);
184
- }
185
- }
186
- Line::Other => eprintln!("[sidecar] ignoring unrecognized line: {trimmed}"),
187
- }
188
- }
189
- // Sidecar gone: mark dead and fail every in-flight call (dropping the senders makes the
190
- // waiting recv_timeout return Disconnected → NATIVE_ERROR immediately).
191
- r_alive.store(false, Ordering::SeqCst);
192
- r_pending.lock().unwrap().clear();
193
- if !got_ready {
194
- let _ = ready_tx.send(None); // never announced → unblock init with a failure
195
- }
196
- eprintln!("[sidecar] reader exited — sidecar marked dead");
197
- });
198
-
199
- let methods = match ready_rx.recv_timeout(READY_TIMEOUT) {
200
- Ok(Some(methods)) => filter_claims(methods),
201
- Ok(None) => {
202
- eprintln!("[sidecar] exited before announcing `ready` — running without custom handlers");
203
- let _ = child.kill();
204
- return;
205
- }
206
- Err(_) => {
207
- eprintln!("[sidecar] no `ready` within {READY_TIMEOUT:?} — killing, running without custom handlers");
208
- let _ = child.kill();
209
- return;
210
- }
211
- };
212
-
213
- eprintln!("[sidecar] ready — {} method(s): {:?}", methods.len(), methods);
214
- let _ = SIDECAR.set(Sidecar {
215
- methods: methods.into_iter().collect(),
216
- alive,
217
- stdin: Mutex::new(stdin),
218
- pending,
219
- child: Mutex::new(child),
220
- next_id: AtomicU64::new(1),
221
- });
222
- }
223
-
224
- /// True if a sidecar exists and ADVERTISED this method (regardless of liveness — a dead sidecar's
225
- /// methods still route here so they return NATIVE_ERROR, not UNSUPPORTED; see the module note).
226
- pub fn claims(method: &str) -> bool {
227
- SIDECAR.get().is_some_and(|s| s.methods.contains(method))
228
- }
229
-
230
- /// Forward a claimed method to the sidecar and block on its response (≤30s). Called from a bridge
231
- /// worker thread (already off the main thread), so blocking here is fine.
232
- pub fn request(method: &str, params: &Value) -> SidecarResult {
233
- let s = SIDECAR.get().ok_or_else(|| ("NATIVE_ERROR".into(), "no sidecar".into()))?;
234
- if !s.alive.load(Ordering::SeqCst) {
235
- return Err(("NATIVE_ERROR".into(), format!("handler sidecar is not running ({method})")));
236
- }
237
- let id = format!("sc-{}", s.next_id.fetch_add(1, Ordering::SeqCst));
238
- let (tx, rx) = mpsc::channel::<Value>();
239
- s.pending.lock().unwrap().insert(id.clone(), tx);
240
-
241
- let envelope = json!({ "kind": "request", "id": id, "method": method, "params": params });
242
- let line = format!("{envelope}\n");
243
- if let Err(e) = (|| {
244
- let mut w = s.stdin.lock().unwrap();
245
- w.write_all(line.as_bytes())?;
246
- w.flush()
247
- })() {
248
- s.pending.lock().unwrap().remove(&id);
249
- return Err(("NATIVE_ERROR".into(), format!("sidecar write failed: {e}")));
250
- }
251
-
252
- match rx.recv_timeout(CALL_TIMEOUT) {
253
- Ok(resp) => response_outcome(&resp),
254
- Err(mpsc::RecvTimeoutError::Timeout) => {
255
- s.pending.lock().unwrap().remove(&id);
256
- Err(("NATIVE_ERROR".into(), format!("sidecar timeout after {CALL_TIMEOUT:?} ({method})")))
257
- }
258
- Err(mpsc::RecvTimeoutError::Disconnected) => {
259
- // Reader thread dropped our sender = sidecar died mid-call.
260
- Err(("NATIVE_ERROR".into(), format!("handler sidecar died during call ({method})")))
261
- }
262
- }
263
- }
264
-
265
- /// Kill the sidecar child (idempotent). Called on main-window teardown so the Bun process never outlives
266
- /// the shell. (`SIDECAR` is a `'static` OnceLock, so a Drop guard would never run at process exit — this
267
- /// explicit call is the reliable teardown; killing an already-dead child is a harmless no-op.)
268
- pub fn shutdown() {
269
- if let Some(s) = SIDECAR.get() {
270
- let _ = s.child.lock().unwrap().kill();
271
- }
272
- }
273
-
274
- #[cfg(test)]
275
- mod tests {
276
- use super::*;
277
-
278
- #[test]
279
- fn parse_ready_extracts_methods() {
280
- match parse_line(r#"{"kind":"ready","methods":["a.b","a.c"]}"#) {
281
- Line::Ready(m) => assert_eq!(m, vec!["a.b".to_string(), "a.c".to_string()]),
282
- _ => panic!("expected Ready"),
283
- }
284
- }
285
-
286
- #[test]
287
- fn parse_ready_missing_methods_is_empty() {
288
- match parse_line(r#"{"kind":"ready"}"#) {
289
- Line::Ready(m) => assert!(m.is_empty()),
290
- _ => panic!("expected Ready"),
291
- }
292
- }
293
-
294
- #[test]
295
- fn parse_response_carries_id_and_value() {
296
- match parse_line(r#"{"kind":"response","id":"sc-1","ok":true,"data":{"x":1}}"#) {
297
- Line::Response { id, value } => {
298
- assert_eq!(id, "sc-1");
299
- assert_eq!(value["data"]["x"], 1);
300
- }
301
- _ => panic!("expected Response"),
302
- }
303
- }
304
-
305
- #[test]
306
- fn parse_malformed_or_unknown_is_other() {
307
- assert!(matches!(parse_line("not json"), Line::Other));
308
- assert!(matches!(parse_line(r#"{"kind":"noise"}"#), Line::Other));
309
- // response without id can't be routed → Other
310
- assert!(matches!(parse_line(r#"{"kind":"response","ok":true}"#), Line::Other));
311
- }
312
-
313
- #[test]
314
- fn outcome_ok_returns_data() {
315
- let resp = json!({"kind":"response","id":"x","ok":true,"data":{"n":42}});
316
- assert_eq!(response_outcome(&resp).unwrap(), json!({"n":42}));
317
- }
318
-
319
- #[test]
320
- fn outcome_ok_missing_data_is_null() {
321
- let resp = json!({"kind":"response","id":"x","ok":true});
322
- assert_eq!(response_outcome(&resp).unwrap(), Value::Null);
323
- }
324
-
325
- #[test]
326
- fn outcome_error_preserves_code_and_message() {
327
- let resp = json!({"kind":"response","id":"x","ok":false,"error":{"code":"DENIED","message":"nope"}});
328
- let (code, msg) = response_outcome(&resp).unwrap_err();
329
- assert_eq!(code, "DENIED");
330
- assert_eq!(msg, "nope");
331
- }
332
-
333
- #[test]
334
- fn outcome_error_defaults_when_fields_absent() {
335
- let resp = json!({"kind":"response","id":"x","ok":false});
336
- let (code, msg) = response_outcome(&resp).unwrap_err();
337
- assert_eq!(code, "NATIVE_ERROR");
338
- assert_eq!(msg, "handler error");
339
- }
340
-
341
- /// Pending-map resolution: a response by id wakes exactly the matching waiter.
342
- #[test]
343
- fn pending_map_resolves_by_id() {
344
- let pending: Arc<Mutex<HashMap<String, Sender<Value>>>> = Arc::new(Mutex::new(HashMap::new()));
345
- let (tx, rx) = mpsc::channel::<Value>();
346
- pending.lock().unwrap().insert("sc-1".into(), tx);
347
-
348
- // Simulate the reader draining a response line for sc-1.
349
- if let Line::Response { id, value } = parse_line(r#"{"kind":"response","id":"sc-1","ok":true,"data":5}"#) {
350
- if let Some(t) = pending.lock().unwrap().remove(&id) {
351
- t.send(value).unwrap();
352
- }
353
- }
354
- let got = rx.recv_timeout(Duration::from_secs(1)).unwrap();
355
- assert_eq!(response_outcome(&got).unwrap(), json!(5));
356
- assert!(pending.lock().unwrap().is_empty());
357
- }
358
-
359
- /// Claims under a built-in namespace are dropped; app-namespaced ones survive.
360
- #[test]
361
- fn filter_claims_rejects_builtin_shadowing() {
362
- let kept = filter_claims(vec![
363
- "storage.get".into(), // shadows built-in storage.*
364
- "ui.alert".into(), // shadows built-in ui.*
365
- "app.handshake".into(), // shadows built-in app.*
366
- "push.x".into(), // shadows built-in push.*
367
- "biometrics.available".into(), // shadows built-in biometrics.*
368
- "myapp.doThing".into(), // fine
369
- "storagex.get".into(), // NOT under "storage." (prefix includes the dot)
370
- ]);
371
- assert_eq!(kept, vec!["myapp.doThing".to_string(), "storagex.get".to_string()]);
372
- }
373
-
374
- #[test]
375
- fn filter_claims_keeps_all_when_no_shadowing() {
376
- let kept = filter_claims(vec!["a.b".into(), "a.c".into()]);
377
- assert_eq!(kept, vec!["a.b".to_string(), "a.c".to_string()]);
378
- }
379
-
380
- /// Runtime resolution: existing stamped path wins; missing/empty falls back to `bun`.
381
- #[test]
382
- fn resolve_runtime_prefers_existing_stamped_path() {
383
- assert_eq!(resolve_runtime("/bin/ls"), "/bin/ls"); // any existing file
384
- assert_eq!(resolve_runtime("/nonexistent/bun-xyz"), "bun");
385
- assert_eq!(resolve_runtime(""), "bun");
386
- }
387
-
388
- /// Dropping the pending sender (sidecar death) makes the waiter observe Disconnected.
389
- #[test]
390
- fn dropped_sender_disconnects_waiter() {
391
- let (tx, rx) = mpsc::channel::<Value>();
392
- drop(tx);
393
- assert!(matches!(
394
- rx.recv_timeout(Duration::from_millis(50)),
395
- Err(mpsc::RecvTimeoutError::Disconnected)
396
- ));
397
- }
398
- }
@@ -1,14 +0,0 @@
1
- {
2
- "$schema": "https://schema.tauri.app/config/2",
3
- "productName": "hello-pwa-desktop",
4
- "version": "0.1.0",
5
- "identifier": "cc.livx.hellopwa.desktop",
6
- "build": {
7
- "frontendDist": "../../examples/hello-pwa/dist"
8
- },
9
- "app": {
10
- "withGlobalTauri": true,
11
- "security": { "csp": null }
12
- },
13
- "bundle": { "active": false }
14
- }