cj-tauri 1.1.2 → 2.1.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 (162) hide show
  1. package/README.md +137 -77
  2. package/bin/cj-tauri.js +287 -287
  3. package/cjpm.toml +13 -2
  4. package/cli/cjpm.toml +22 -13
  5. package/cli/src/doctor.cj +308 -304
  6. package/cli/src/main.cj +129 -121
  7. package/cli/src/manifest.cj +632 -0
  8. package/cli/src/manifest_test.cj +87 -0
  9. package/cli/src/project.cj +770 -754
  10. package/cli/src/resolve.cj +348 -348
  11. package/cli/src/scaffold.cj +46 -38
  12. package/cli/src/sync.cj +526 -0
  13. package/cli/src/util.cj +9 -0
  14. package/cli/templates/app/README.md +30 -59
  15. package/cli/templates/app/{ui → assets/ui}/index.html +2 -0
  16. package/cli/templates/app/cj-tauri.toml +32 -0
  17. package/cli/templates/app/cjpm.toml +21 -4
  18. package/cli/templates/app/src/main.cj +29 -16
  19. package/cli/templates/app-react/README.md +34 -45
  20. package/cli/templates/app-react/{ui → assets/ui}/index.html +25 -25
  21. package/cli/templates/app-react/{ui → assets/ui}/src/App.css +67 -67
  22. package/cli/templates/app-react/{ui → assets/ui}/src/App.jsx +124 -124
  23. package/cli/templates/app-react/cj-tauri.toml +33 -0
  24. package/cli/templates/app-react/cjpm.toml +21 -4
  25. package/cli/templates/app-react/src/main.cj +18 -29
  26. package/cli/templates/app-vue/README.md +34 -38
  27. package/cli/templates/app-vue/{ui → assets/ui}/index.html +25 -25
  28. package/cli/templates/app-vue/{ui → assets/ui}/src/App.vue +176 -176
  29. package/cli/templates/app-vue/cj-tauri.toml +33 -0
  30. package/cli/templates/app-vue/cjpm.toml +21 -4
  31. package/cli/templates/app-vue/src/main.cj +18 -29
  32. package/cli/templates/{ohos-pc → ohos-skeleton}/README.md +5 -0
  33. package/cli/templates/{ohos-pc → ohos-skeleton}/build-hap.sh +15 -0
  34. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/cjpm.toml +8 -0
  35. package/cli/templates/ohos-skeleton/entry/src/main/cangjie/dialog_host.cj +217 -0
  36. package/cli/templates/ohos-skeleton/entry/src/main/cangjie/index.cj +527 -0
  37. package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/module.json5 +8 -0
  38. package/native/build_win.bat +62 -62
  39. package/native/core/bridge_core.c +1681 -1626
  40. package/native/core/bridge_core.h +675 -618
  41. package/native/core/bridge_js.h +108 -108
  42. package/native/platforms/harmonyos/README.md +115 -6
  43. package/native/platforms/linux/bridge_linux.c +2787 -2485
  44. package/native/platforms/windows/bridge_win.c +3740 -3248
  45. package/native/tests/gtk_threading_probe.c +143 -143
  46. package/native/tests/test_bridge_core.c +1339 -1228
  47. package/package.json +53 -51
  48. package/prebuilt/linux-x64/cj-tauri +0 -0
  49. package/src/api_system.cj +67 -67
  50. package/src/app.cj +325 -940
  51. package/src/app_events.cj +166 -0
  52. package/src/app_host_ops.cj +287 -0
  53. package/src/app_run.cj +283 -0
  54. package/src/app_spec.cj +76 -0
  55. package/src/arkweb_menu.cj +216 -0
  56. package/src/arkweb_script.cj +0 -12
  57. package/src/capability.cj +409 -409
  58. package/src/clipboard_bridge.cj +178 -0
  59. package/src/dialog_spec.cj +79 -0
  60. package/src/dragdrop.cj +26 -26
  61. package/src/embedded_assets.cj +98 -98
  62. package/src/host.cj +752 -604
  63. package/src/host_arkweb.cj +201 -17
  64. package/src/host_webkit.cj +663 -548
  65. package/src/host_webview2.cj +663 -548
  66. package/src/hotkey.cj +66 -66
  67. package/src/ipc_hub.cj +337 -337
  68. package/src/ipc_message.cj +286 -286
  69. package/src/launch.cj +62 -0
  70. package/src/logging.cj +127 -127
  71. package/src/menu.cj +197 -197
  72. package/src/platform/platform_app_context.cj +77 -0
  73. package/src/platform/platform_assets.cj +33 -0
  74. package/src/platform/platform_discover.cj +176 -0
  75. package/src/platform/platform_env.cj +29 -0
  76. package/src/platform/platform_feature.cj +31 -0
  77. package/src/platform/platform_page_source.cj +87 -0
  78. package/src/platform/platform_target_info.cj +32 -0
  79. package/src/platform_host_provider.cj +87 -0
  80. package/src/plugin.cj +333 -299
  81. package/src/plugins/clipboard/plugin_clipboard.cj +152 -144
  82. package/src/plugins/dialog/plugin_dialog.cj +209 -209
  83. package/src/plugins/fs/plugin_fs.cj +105 -1
  84. package/src/plugins/http/http_backend.cj +131 -0
  85. package/src/plugins/http/http_desktop.cj +195 -0
  86. package/src/plugins/http/http_ohos.cj +204 -0
  87. package/src/plugins/http/plugin_http.cj +310 -459
  88. package/src/plugins/log/plugin_log.cj +105 -105
  89. package/src/plugins/menu/plugin_menu.cj +8 -0
  90. package/src/plugins/notification/plugin_notification.cj +154 -146
  91. package/src/plugins/serial/plugin_serial.cj +17 -1
  92. package/src/plugins/serial/serial_bridge.cj +9 -1
  93. package/src/plugins/serial/serial_manager_ohos.cj +92 -0
  94. package/src/plugins/serial/serial_ohos.cj +529 -0
  95. package/src/plugins/shell/plugin_shell.cj +29 -1
  96. package/src/plugins/sqlite/plugin_sqlite.cj +8 -0
  97. package/src/plugins/ws/plugin_ws.cj +488 -470
  98. package/src/process_hidden.cj +174 -174
  99. package/src/scope.cj +391 -391
  100. package/src/shell_event.cj +69 -69
  101. package/src/tests/app_spec_test.cj +126 -0
  102. package/src/tests/capability_test.cj +243 -243
  103. package/src/tests/dialog_spec_test.cj +73 -0
  104. package/src/tests/fake_host.cj +201 -186
  105. package/src/tests/ipc_hub_test.cj +487 -487
  106. package/src/tests/logging_test.cj +62 -62
  107. package/src/tests/platform_contract_test.cj +251 -0
  108. package/src/tests/plugin_clipboard_test.cj +222 -222
  109. package/src/tests/plugin_dialog_test.cj +237 -237
  110. package/src/tests/plugin_fs_test.cj +207 -0
  111. package/src/tests/plugin_http_test.cj +124 -124
  112. package/src/tests/plugin_notification_test.cj +205 -205
  113. package/src/tests/plugin_serial_test.cj +48 -0
  114. package/src/tests/plugin_test.cj +66 -3
  115. package/src/tests/plugin_ws_test.cj +40 -40
  116. package/src/tests/process_hidden_test.cj +129 -129
  117. package/src/tests/scope_test.cj +350 -350
  118. package/src/tests/system_host_test.cj +99 -81
  119. package/src/tests/urlutil_test.cj +53 -53
  120. package/src/tests/version_test.cj +75 -75
  121. package/src/tests/window_registry_test.cj +123 -123
  122. package/src/tray.cj +90 -90
  123. package/src/urlutil.cj +159 -159
  124. package/src/version.cj +44 -44
  125. package/src/window.cj +51 -51
  126. package/src/window_registry.cj +116 -116
  127. package/cli/templates/ohos-pc/entry/src/main/cangjie/index.cj +0 -167
  128. package/prebuilt/win32-x64/cj-tauri.exe +0 -0
  129. package/src/host_factory.cj +0 -48
  130. /package/cli/templates/app/{capabilities → assets/capabilities}/default.json +0 -0
  131. /package/cli/templates/app-react/{capabilities → assets/capabilities}/default.json +0 -0
  132. /package/cli/templates/app-react/{ui → assets/ui}/package.json +0 -0
  133. /package/cli/templates/app-react/{ui → assets/ui}/src/main.jsx +0 -0
  134. /package/cli/templates/app-react/{ui → assets/ui}/vite.config.js +0 -0
  135. /package/cli/templates/app-vue/{capabilities → assets/capabilities}/default.json +0 -0
  136. /package/cli/templates/app-vue/{ui → assets/ui}/package.json +0 -0
  137. /package/cli/templates/app-vue/{ui → assets/ui}/src/main.js +0 -0
  138. /package/cli/templates/app-vue/{ui → assets/ui}/vite.config.js +0 -0
  139. /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/app.json5 +0 -0
  140. /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/element/string.json +0 -0
  141. /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/background.png +0 -0
  142. /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/foreground.png +0 -0
  143. /package/cli/templates/{ohos-pc → ohos-skeleton}/AppScope/resources/base/media/layered_image.json +0 -0
  144. /package/cli/templates/{ohos-pc → ohos-skeleton}/build-profile.json5 +0 -0
  145. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/build-profile.json5 +0 -0
  146. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/hvigorfile.ts +0 -0
  147. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/oh-package.json5 +0 -0
  148. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/ability_stage.cj +0 -0
  149. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/app_host.cj +0 -0
  150. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/cangjie/main_ability.cj +0 -0
  151. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/color.json +0 -0
  152. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/element/string.json +0 -0
  153. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/background.png +0 -0
  154. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/foreground.png +0 -0
  155. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/layered_image.json +0 -0
  156. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/media/startIcon.png +0 -0
  157. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/base/profile/main_pages.json +0 -0
  158. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/capabilities.json +0 -0
  159. /package/cli/templates/{ohos-pc → ohos-skeleton}/entry/src/main/resources/rawfile/index.html +0 -0
  160. /package/cli/templates/{ohos-pc → ohos-skeleton}/hvigor/hvigor-config.json5 +0 -0
  161. /package/cli/templates/{ohos-pc → ohos-skeleton}/hvigorfile.ts +0 -0
  162. /package/cli/templates/{ohos-pc → ohos-skeleton}/oh-package.json5 +0 -0
@@ -1,129 +1,129 @@
1
- package cjTauri.tests
2
-
3
- import cjTauri.*
4
- import std.unittest.*
5
- import std.unittest.testmacro.*
6
-
7
- /*
8
- * 静默启动的参数线格式(`joinHiddenArgs`)单测。
9
- *
10
- * 真正难的**不是拼接**,是「拼出来还能原样切开」这条不变量:C 桥按 0x1F 把这条串切成 argv,
11
- * 一旦某个参数自身带着 0x1F,它就在桥那边变成两个参数——而调用方拿到的是「启动成功」,
12
- * 错误要到目标进程抱怨「未知选项」时才现身。所以这里有两条硬要求:
13
- * ① 参数**逐字**进线格式(空格 / 引号 / 中文都不在这里转义,转义是平台侧的事);
14
- * ② 参数里出现 0x1F 就当场拒绝,不许拼接。
15
- *
16
- * 起真进程 / 判可见窗口的部分不在这里(需要桌面与平台桥),归实机取证。
17
- */
18
-
19
- /** 线格式分隔符:0x1F(单元分隔符),与 C 桥 `CJ_ARG_SEP` 同值 */
20
- let hiddenSep: String = "\u{1F}"
21
-
22
- /** 拼装成功取回线格式;被拒绝时回一个可比较的哨兵值(避免在断言里直接比 Option) */
23
- func hiddenWireOf(args: Array<String>): String {
24
- let parsed = joinHiddenArgs(args)
25
- var wire = "<rejected>"
26
- match (parsed) {
27
- case Some(w) => wire = w
28
- case None => wire = "<rejected>"
29
- }
30
- return wire
31
- }
32
-
33
- /** 是否被拒绝(0x1F 出现在参数里) */
34
- func hiddenArgsRejected(args: Array<String>): Bool {
35
- let parsed = joinHiddenArgs(args)
36
- var rejected = false
37
- match (parsed) {
38
- case Some(_) => rejected = false
39
- case None => rejected = true
40
- }
41
- return rejected
42
- }
43
-
44
- @Test
45
- func testJoinHiddenArgsKeepsOrderAndUsesUnitSeparator(): Unit {
46
- // 顺序必须与传入一致(桥按位取 argv),分隔符只有一个 0x1F
47
- let wire = hiddenWireOf(["web", "--port", "8765", "--bind", "127.0.0.1"])
48
- @Expect(wire, "web" + hiddenSep + "--port" + hiddenSep + "8765" + hiddenSep + "--bind" +
49
- hiddenSep + "127.0.0.1")
50
- }
51
-
52
- @Test
53
- func testJoinHiddenArgsSingleArgHasNoSeparator(): Unit {
54
- // 只有一个参数时线格式里不该留下尾随分隔符(否则桥会切出一个空 argv)
55
- let wire = hiddenWireOf(["web"])
56
- @Expect(wire, "web")
57
- // indexOf 回 Option<Int64>,这里只关心「有没有」,拆开再断言(@Expect 对不同泛型的 Option 实参推不出类型)
58
- let found = wire.indexOf(hiddenSep)
59
- var hasSep = true
60
- match (found) {
61
- case Some(_) => hasSep = true
62
- case None => hasSep = false
63
- }
64
- @Expect(hasSep, false)
65
- }
66
-
67
- @Test
68
- func testJoinHiddenArgsEmptyArgListIsEmptyWire(): Unit {
69
- // 没参数的启动(如 cjh 只用默认端口)走这条:空线格式,桥侧切成零个 argv
70
- let wire = hiddenWireOf([])
71
- @Expect(wire, "")
72
- }
73
-
74
- @Test
75
- func testJoinHiddenArgsKeepsSpacesQuotesAndChineseVerbatim(): Unit {
76
- // 参数原样上线:不在这里加引号、不转义——那些是平台侧(CreateProcessW / posix_spawn)的事。
77
- // 中文按 UTF-8 逐字节过桥,不做任何改写。
78
- let wire = hiddenWireOf(["hello world", "\"quoted\"", "中文参数", "--path=C:/a b/c"])
79
- @Expect(wire, "hello world" + hiddenSep + "\"quoted\"" + hiddenSep + "中文参数" + hiddenSep +
80
- "--path=C:/a b/c")
81
- }
82
-
83
- @Test
84
- func testJoinHiddenArgsKeepsEmptyArgSlot(): Unit {
85
- // 中间那个空串参数不能被吞:它占一个 argv 位,桥那边必须收到一个空参数
86
- let wire = hiddenWireOf(["a", "", "b"])
87
- @Expect(wire, "a" + hiddenSep + hiddenSep + "b")
88
- }
89
-
90
- @Test
91
- func testJoinHiddenArgsRoundTripsThroughSplit(): Unit {
92
- // 不变量:拼出来还能按同一个分隔符切回去,条数不变、逐条相等
93
- let args = ["web", "--port", "8765", "--bind", "127.0.0.1"]
94
- let back = hiddenWireOf(args).split(hiddenSep)
95
- @Expect(back.size, args.size)
96
- var same = true
97
- for (i in 0..args.size) {
98
- if (back[i] != args[i]) {
99
- same = false
100
- }
101
- }
102
- @Expect(same, true)
103
- }
104
-
105
- @Test
106
- func testJoinHiddenArgsRejectsUnitSeparatorInArg(): Unit {
107
- // 0x1F 混在参数里 → 必须拒绝(拼接成功等于把参数悄悄劈成两个)
108
- @Expect(hiddenArgsRejected(["plain" + hiddenSep + "injected"]), true)
109
- // 位置无关:开头 / 中间 / 末尾都要拒
110
- @Expect(hiddenArgsRejected([hiddenSep + "lead"]), true)
111
- @Expect(hiddenArgsRejected(["--port", "1" + hiddenSep + "2"]), true)
112
- @Expect(hiddenArgsRejected(["trail" + hiddenSep]), true)
113
- // 整个参数就是一个分隔符
114
- @Expect(hiddenArgsRejected([hiddenSep]), true)
115
- // 对照:正常参数不被拒(否则上面的「拒绝」可能只是恒 true)
116
- @Expect(hiddenArgsRejected(["web", "--port", "8765"]), false)
117
- }
118
-
119
- @Test
120
- func testLaunchHiddenRejectsEmptyPathWithoutTouchingBridge(): Unit {
121
- // 空路径在**进桥之前**就返回 None:不需要任何平台产物,也不会留下 pid
122
- let started = launchHidden("", ["web"])
123
- var isNone = false
124
- match (started) {
125
- case Some(_) => isNone = false
126
- case None => isNone = true
127
- }
128
- @Expect(isNone, true)
129
- }
1
+ package cjTauri.tests
2
+
3
+ import cjTauri.*
4
+ import std.unittest.*
5
+ import std.unittest.testmacro.*
6
+
7
+ /*
8
+ * 静默启动的参数线格式(`joinHiddenArgs`)单测。
9
+ *
10
+ * 真正难的**不是拼接**,是「拼出来还能原样切开」这条不变量:C 桥按 0x1F 把这条串切成 argv,
11
+ * 一旦某个参数自身带着 0x1F,它就在桥那边变成两个参数——而调用方拿到的是「启动成功」,
12
+ * 错误要到目标进程抱怨「未知选项」时才现身。所以这里有两条硬要求:
13
+ * ① 参数**逐字**进线格式(空格 / 引号 / 中文都不在这里转义,转义是平台侧的事);
14
+ * ② 参数里出现 0x1F 就当场拒绝,不许拼接。
15
+ *
16
+ * 起真进程 / 判可见窗口的部分不在这里(需要桌面与平台桥),归实机取证。
17
+ */
18
+
19
+ /** 线格式分隔符:0x1F(单元分隔符),与 C 桥 `CJ_ARG_SEP` 同值 */
20
+ let hiddenSep: String = "\u{1F}"
21
+
22
+ /** 拼装成功取回线格式;被拒绝时回一个可比较的哨兵值(避免在断言里直接比 Option) */
23
+ func hiddenWireOf(args: Array<String>): String {
24
+ let parsed = joinHiddenArgs(args)
25
+ var wire = "<rejected>"
26
+ match (parsed) {
27
+ case Some(w) => wire = w
28
+ case None => wire = "<rejected>"
29
+ }
30
+ return wire
31
+ }
32
+
33
+ /** 是否被拒绝(0x1F 出现在参数里) */
34
+ func hiddenArgsRejected(args: Array<String>): Bool {
35
+ let parsed = joinHiddenArgs(args)
36
+ var rejected = false
37
+ match (parsed) {
38
+ case Some(_) => rejected = false
39
+ case None => rejected = true
40
+ }
41
+ return rejected
42
+ }
43
+
44
+ @Test
45
+ func testJoinHiddenArgsKeepsOrderAndUsesUnitSeparator(): Unit {
46
+ // 顺序必须与传入一致(桥按位取 argv),分隔符只有一个 0x1F
47
+ let wire = hiddenWireOf(["web", "--port", "8765", "--bind", "127.0.0.1"])
48
+ @Expect(wire, "web" + hiddenSep + "--port" + hiddenSep + "8765" + hiddenSep + "--bind" +
49
+ hiddenSep + "127.0.0.1")
50
+ }
51
+
52
+ @Test
53
+ func testJoinHiddenArgsSingleArgHasNoSeparator(): Unit {
54
+ // 只有一个参数时线格式里不该留下尾随分隔符(否则桥会切出一个空 argv)
55
+ let wire = hiddenWireOf(["web"])
56
+ @Expect(wire, "web")
57
+ // indexOf 回 Option<Int64>,这里只关心「有没有」,拆开再断言(@Expect 对不同泛型的 Option 实参推不出类型)
58
+ let found = wire.indexOf(hiddenSep)
59
+ var hasSep = true
60
+ match (found) {
61
+ case Some(_) => hasSep = true
62
+ case None => hasSep = false
63
+ }
64
+ @Expect(hasSep, false)
65
+ }
66
+
67
+ @Test
68
+ func testJoinHiddenArgsEmptyArgListIsEmptyWire(): Unit {
69
+ // 没参数的启动(如 cjh 只用默认端口)走这条:空线格式,桥侧切成零个 argv
70
+ let wire = hiddenWireOf([])
71
+ @Expect(wire, "")
72
+ }
73
+
74
+ @Test
75
+ func testJoinHiddenArgsKeepsSpacesQuotesAndChineseVerbatim(): Unit {
76
+ // 参数原样上线:不在这里加引号、不转义——那些是平台侧(CreateProcessW / posix_spawn)的事。
77
+ // 中文按 UTF-8 逐字节过桥,不做任何改写。
78
+ let wire = hiddenWireOf(["hello world", "\"quoted\"", "中文参数", "--path=C:/a b/c"])
79
+ @Expect(wire, "hello world" + hiddenSep + "\"quoted\"" + hiddenSep + "中文参数" + hiddenSep +
80
+ "--path=C:/a b/c")
81
+ }
82
+
83
+ @Test
84
+ func testJoinHiddenArgsKeepsEmptyArgSlot(): Unit {
85
+ // 中间那个空串参数不能被吞:它占一个 argv 位,桥那边必须收到一个空参数
86
+ let wire = hiddenWireOf(["a", "", "b"])
87
+ @Expect(wire, "a" + hiddenSep + hiddenSep + "b")
88
+ }
89
+
90
+ @Test
91
+ func testJoinHiddenArgsRoundTripsThroughSplit(): Unit {
92
+ // 不变量:拼出来还能按同一个分隔符切回去,条数不变、逐条相等
93
+ let args = ["web", "--port", "8765", "--bind", "127.0.0.1"]
94
+ let back = hiddenWireOf(args).split(hiddenSep)
95
+ @Expect(back.size, args.size)
96
+ var same = true
97
+ for (i in 0..args.size) {
98
+ if (back[i] != args[i]) {
99
+ same = false
100
+ }
101
+ }
102
+ @Expect(same, true)
103
+ }
104
+
105
+ @Test
106
+ func testJoinHiddenArgsRejectsUnitSeparatorInArg(): Unit {
107
+ // 0x1F 混在参数里 → 必须拒绝(拼接成功等于把参数悄悄劈成两个)
108
+ @Expect(hiddenArgsRejected(["plain" + hiddenSep + "injected"]), true)
109
+ // 位置无关:开头 / 中间 / 末尾都要拒
110
+ @Expect(hiddenArgsRejected([hiddenSep + "lead"]), true)
111
+ @Expect(hiddenArgsRejected(["--port", "1" + hiddenSep + "2"]), true)
112
+ @Expect(hiddenArgsRejected(["trail" + hiddenSep]), true)
113
+ // 整个参数就是一个分隔符
114
+ @Expect(hiddenArgsRejected([hiddenSep]), true)
115
+ // 对照:正常参数不被拒(否则上面的「拒绝」可能只是恒 true)
116
+ @Expect(hiddenArgsRejected(["web", "--port", "8765"]), false)
117
+ }
118
+
119
+ @Test
120
+ func testLaunchHiddenRejectsEmptyPathWithoutTouchingBridge(): Unit {
121
+ // 空路径在**进桥之前**就返回 None:不需要任何平台产物,也不会留下 pid
122
+ let started = launchHidden("", ["web"])
123
+ var isNone = false
124
+ match (started) {
125
+ case Some(_) => isNone = false
126
+ case None => isNone = true
127
+ }
128
+ @Expect(isNone, true)
129
+ }