pake-cli 3.16.2 โ 3.16.4
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/dist/cli.js +51 -17
- package/package.json +1 -1
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/src/app/window.rs +40 -1
- package/src-tauri/src/lib.rs +10 -2
- package/src-tauri/src/util.rs +39 -0
- package/src-tauri/tauri.conf.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ import * as psl from 'psl';
|
|
|
19
19
|
import { InvalidArgumentError, program as program$1, Option } from 'commander';
|
|
20
20
|
|
|
21
21
|
var name = "pake-cli";
|
|
22
|
-
var version = "3.16.
|
|
22
|
+
var version = "3.16.4";
|
|
23
23
|
var description = "๐คฑ๐ป Turn any webpage into a desktop app with one command. ๐คฑ๐ป ไธ้ฎๆๅ
็ฝ้กต็ๆ่ฝป้ๆก้ขๅบ็จใ";
|
|
24
24
|
var engines = {
|
|
25
25
|
node: ">=20.9.0"
|
|
@@ -538,15 +538,8 @@ async function acquireBuildCache(targetDirectory) {
|
|
|
538
538
|
async function enterBuildWorkspace() {
|
|
539
539
|
const previousTarget = process.env.CARGO_TARGET_DIR;
|
|
540
540
|
const targetDirectory = path.resolve(packageDirectory, previousTarget || 'src-tauri/target');
|
|
541
|
-
const
|
|
542
|
-
let
|
|
543
|
-
try {
|
|
544
|
-
directory = await createBuildWorkspace(packageDirectory);
|
|
545
|
-
}
|
|
546
|
-
catch (error) {
|
|
547
|
-
await release();
|
|
548
|
-
throw error;
|
|
549
|
-
}
|
|
541
|
+
const directory = await createBuildWorkspace(packageDirectory);
|
|
542
|
+
let release;
|
|
550
543
|
setBuildDirectory(directory);
|
|
551
544
|
process.env.CARGO_TARGET_DIR = targetDirectory;
|
|
552
545
|
const leave = async () => {
|
|
@@ -567,7 +560,7 @@ async function enterBuildWorkspace() {
|
|
|
567
560
|
}
|
|
568
561
|
finally {
|
|
569
562
|
try {
|
|
570
|
-
await release();
|
|
563
|
+
await release?.();
|
|
571
564
|
}
|
|
572
565
|
catch (error) {
|
|
573
566
|
logger.warn(`Could not release the compilation cache lock: ${String(error)}`);
|
|
@@ -578,7 +571,11 @@ async function enterBuildWorkspace() {
|
|
|
578
571
|
await leave();
|
|
579
572
|
throwIfBuildCancelled();
|
|
580
573
|
}
|
|
581
|
-
return leave
|
|
574
|
+
return Object.assign(leave, {
|
|
575
|
+
async lockCache() {
|
|
576
|
+
release = await acquireBuildCache(targetDirectory);
|
|
577
|
+
},
|
|
578
|
+
});
|
|
582
579
|
}
|
|
583
580
|
|
|
584
581
|
async function terminateBuildTree(pid) {
|
|
@@ -1663,9 +1660,14 @@ class BaseBuilder {
|
|
|
1663
1660
|
await shellExec({ executable: packageManager, args });
|
|
1664
1661
|
}
|
|
1665
1662
|
async buildAndCopy(url, target, logSuccess = true) {
|
|
1666
|
-
|
|
1663
|
+
await this.prepareBuild(url);
|
|
1664
|
+
await this.runBuildCommand(this.getBuildCommand(await detectPackageManager()), target);
|
|
1665
|
+
await this.copyBuildArtifacts(target, logSuccess);
|
|
1666
|
+
}
|
|
1667
|
+
async prepareBuild(url) {
|
|
1667
1668
|
await mergeConfig(url, this.options, structuredClone(tauriConfig));
|
|
1668
|
-
|
|
1669
|
+
}
|
|
1670
|
+
async runBuildCommand(buildCommand, target) {
|
|
1669
1671
|
// Build app
|
|
1670
1672
|
const buildSpinner = getSpinner('Building app...');
|
|
1671
1673
|
buildSpinner.stop();
|
|
@@ -1684,7 +1686,6 @@ class BaseBuilder {
|
|
|
1684
1686
|
if (isLinuxAppImage && !buildEnv.NO_STRIP && this.options.debug) {
|
|
1685
1687
|
logger.warn('โ AppImage strip step can fail on glibc 2.38+; Pake will auto-retry with NO_STRIP=1.');
|
|
1686
1688
|
}
|
|
1687
|
-
const buildCommand = this.getBuildCommand(packageManager);
|
|
1688
1689
|
const buildTimeout = getBuildTimeout();
|
|
1689
1690
|
try {
|
|
1690
1691
|
await shellExec(buildCommand, buildTimeout, resolveExecEnv());
|
|
@@ -1711,6 +1712,9 @@ class BaseBuilder {
|
|
|
1711
1712
|
throw retryError;
|
|
1712
1713
|
}
|
|
1713
1714
|
}
|
|
1715
|
+
}
|
|
1716
|
+
async copyBuildArtifacts(target, logSuccess = true) {
|
|
1717
|
+
const { name = 'pake-app' } = this.options;
|
|
1714
1718
|
// With --no-bundle there is no installer to copy; surface the raw
|
|
1715
1719
|
// executable the build produced instead.
|
|
1716
1720
|
if (this.options.bundle === false) {
|
|
@@ -2083,6 +2087,7 @@ class LinuxBuilder extends BaseBuilder {
|
|
|
2083
2087
|
constructor(options) {
|
|
2084
2088
|
super(options);
|
|
2085
2089
|
this.currentBuildType = '';
|
|
2090
|
+
this.compiled = false;
|
|
2086
2091
|
const target = options.targets || 'deb';
|
|
2087
2092
|
if (target.includes('-arm64')) {
|
|
2088
2093
|
this.buildFormat = target.replace('-arm64', '');
|
|
@@ -2118,6 +2123,8 @@ class LinuxBuilder extends BaseBuilder {
|
|
|
2118
2123
|
return `${name}_${version}_${arch}`;
|
|
2119
2124
|
}
|
|
2120
2125
|
async build(url) {
|
|
2126
|
+
this.compiled = false;
|
|
2127
|
+
this.currentBuildType = '';
|
|
2121
2128
|
// --no-bundle: build the executable once with no per-format packaging loop.
|
|
2122
2129
|
if (this.options.bundle === false) {
|
|
2123
2130
|
await this.buildAndCopy(url, 'deb');
|
|
@@ -2128,6 +2135,13 @@ class LinuxBuilder extends BaseBuilder {
|
|
|
2128
2135
|
throw new Error(`No valid Linux target in "${this.options.targets}". Valid targets: ${LINUX_TARGET_TYPES.join(', ')}.`);
|
|
2129
2136
|
}
|
|
2130
2137
|
const useTemporaryDebForZst = needsTemporaryDebForZst(targets);
|
|
2138
|
+
if (targets.length > 1) {
|
|
2139
|
+
await this.prepareBuild(url);
|
|
2140
|
+
const command = this.getBuildCommand(await detectPackageManager());
|
|
2141
|
+
command.args.push('--no-bundle');
|
|
2142
|
+
await this.runBuildCommand(command, 'compile');
|
|
2143
|
+
this.compiled = true;
|
|
2144
|
+
}
|
|
2131
2145
|
// With a single explicit target, fail fast. With multiple targets (the
|
|
2132
2146
|
// distro-aware default, or an explicit comma list) keep building the rest
|
|
2133
2147
|
// when one fails, so a usable installer is still produced, e.g. AppImage
|
|
@@ -2293,7 +2307,24 @@ post_remove() {
|
|
|
2293
2307
|
// Override buildAndCopy to ensure currentBuildType is synced if called directly, though the loop above handles it most of the time.
|
|
2294
2308
|
async buildAndCopy(url, target, logSuccess = true) {
|
|
2295
2309
|
this.currentBuildType = target;
|
|
2296
|
-
|
|
2310
|
+
if (!this.compiled) {
|
|
2311
|
+
await super.buildAndCopy(url, target, logSuccess);
|
|
2312
|
+
return;
|
|
2313
|
+
}
|
|
2314
|
+
const packageManager = await detectPackageManager();
|
|
2315
|
+
const args = ['run', 'tauri'];
|
|
2316
|
+
if (packageManager === 'npm')
|
|
2317
|
+
args.push('--');
|
|
2318
|
+
args.push('bundle', '--config', path.join('src-tauri', '.pake', 'tauri.conf.json'));
|
|
2319
|
+
args.push('--bundles', target, '--features', this.getBuildFeatures().join(','));
|
|
2320
|
+
if (this.options.debug)
|
|
2321
|
+
args.push('--debug');
|
|
2322
|
+
if (this.options.debug || target === 'appimage' || process.env.PAKE_VERBOSE)
|
|
2323
|
+
args.push('--verbose');
|
|
2324
|
+
if (this.buildArch === 'arm64')
|
|
2325
|
+
args.push('--target', this.getTauriTarget('arm64', 'linux'));
|
|
2326
|
+
await this.runBuildCommand({ executable: packageManager, args }, target);
|
|
2327
|
+
await this.copyBuildArtifacts(target, logSuccess);
|
|
2297
2328
|
}
|
|
2298
2329
|
getBuildCommand(packageManager = 'pnpm') {
|
|
2299
2330
|
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
|
|
@@ -3847,7 +3878,8 @@ program.action(async (urlArg, options) => {
|
|
|
3847
3878
|
}
|
|
3848
3879
|
endCancellation = beginBuildCancellation();
|
|
3849
3880
|
phase = 'prepare';
|
|
3850
|
-
|
|
3881
|
+
const workspace = await enterBuildWorkspace();
|
|
3882
|
+
leaveWorkspace = workspace;
|
|
3851
3883
|
phase = 'input';
|
|
3852
3884
|
const appOptions = await handleOptions(options, url);
|
|
3853
3885
|
throwIfBuildCancelled();
|
|
@@ -3857,6 +3889,8 @@ program.action(async (urlArg, options) => {
|
|
|
3857
3889
|
await builder.prepare();
|
|
3858
3890
|
throwIfBuildCancelled();
|
|
3859
3891
|
phase = 'build';
|
|
3892
|
+
await workspace.lockCache();
|
|
3893
|
+
throwIfBuildCancelled();
|
|
3860
3894
|
await builder.build(url);
|
|
3861
3895
|
throwIfBuildCancelled();
|
|
3862
3896
|
await leaveWorkspace();
|
package/package.json
CHANGED
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED
|
@@ -405,6 +405,27 @@ fn build_window(
|
|
|
405
405
|
))
|
|
406
406
|
})?;
|
|
407
407
|
|
|
408
|
+
let restored_url = if label == "pake"
|
|
409
|
+
&& window_config.url_type == "web"
|
|
410
|
+
&& !window_config.incognito
|
|
411
|
+
{
|
|
412
|
+
match app.path().app_data_dir() {
|
|
413
|
+
Ok(directory) => match crate::util::read_last_url(&directory.join("last-url.txt")) {
|
|
414
|
+
Ok(url) => url,
|
|
415
|
+
Err(error) => {
|
|
416
|
+
eprintln!("[Pake] Could not read the last URL: {error}");
|
|
417
|
+
None
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
Err(error) => {
|
|
421
|
+
eprintln!("[Pake] Could not locate the last URL: {error}");
|
|
422
|
+
None
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
None
|
|
427
|
+
};
|
|
428
|
+
|
|
408
429
|
// On macOS both HTTP Basic auth and certificate bypass use the same
|
|
409
430
|
// navigation-delegate proxy. Start on a neutral page so the proxy is in
|
|
410
431
|
// place before the target can issue its first authentication challenge.
|
|
@@ -413,11 +434,15 @@ fn build_window(
|
|
|
413
434
|
&& window_config.url_type == "web"
|
|
414
435
|
&& (config.basic_auth || window_config.ignore_certificate_errors)
|
|
415
436
|
{
|
|
416
|
-
|
|
437
|
+
restored_url
|
|
438
|
+
.clone()
|
|
439
|
+
.or_else(|| Url::parse(&window_config.url).ok())
|
|
417
440
|
} else {
|
|
418
441
|
None
|
|
419
442
|
};
|
|
420
443
|
|
|
444
|
+
let url = restored_url.map(WebviewUrl::External).unwrap_or(url);
|
|
445
|
+
|
|
421
446
|
// The delegate must be installed before the first TLS challenge. Start on
|
|
422
447
|
// a neutral page, then navigate from the with_webview callback below.
|
|
423
448
|
#[cfg(target_os = "macos")]
|
|
@@ -821,6 +846,20 @@ fn build_window(
|
|
|
821
846
|
Ok(window)
|
|
822
847
|
}
|
|
823
848
|
|
|
849
|
+
pub fn save_last_url(app: &AppHandle) {
|
|
850
|
+
let Some(window) = app.get_webview_window("pake") else {
|
|
851
|
+
return;
|
|
852
|
+
};
|
|
853
|
+
let result = (|| -> Result<(), Box<dyn std::error::Error>> {
|
|
854
|
+
let path = app.path().app_data_dir()?.join("last-url.txt");
|
|
855
|
+
crate::util::write_last_url(&path, &window.url()?)?;
|
|
856
|
+
Ok(())
|
|
857
|
+
})();
|
|
858
|
+
if let Err(error) = result {
|
|
859
|
+
eprintln!("[Pake] Could not save the last URL: {error}");
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
824
863
|
#[cfg(all(test, target_os = "windows"))]
|
|
825
864
|
mod proxy_arg_tests {
|
|
826
865
|
use super::*;
|
package/src-tauri/src/lib.rs
CHANGED
|
@@ -32,8 +32,8 @@ use app::{
|
|
|
32
32
|
},
|
|
33
33
|
setup::{set_global_shortcut, set_system_tray},
|
|
34
34
|
window::{
|
|
35
|
-
open_additional_window_safe, reapply_window_icon, reveal_built_window,
|
|
36
|
-
MultiWindowState,
|
|
35
|
+
open_additional_window_safe, reapply_window_icon, reveal_built_window, save_last_url,
|
|
36
|
+
set_window, MultiWindowState,
|
|
37
37
|
},
|
|
38
38
|
};
|
|
39
39
|
use util::get_pake_config;
|
|
@@ -201,6 +201,8 @@ pub fn run_app() {
|
|
|
201
201
|
|
|
202
202
|
let show_system_tray = pake_config.show_system_tray();
|
|
203
203
|
let hide_on_close = pake_config.windows[0].hide_on_close;
|
|
204
|
+
let remember_url =
|
|
205
|
+
pake_config.windows[0].url_type == "web" && !pake_config.windows[0].incognito;
|
|
204
206
|
let activation_shortcut = pake_config.windows[0].activation_shortcut.clone();
|
|
205
207
|
let init_fullscreen = pake_config.windows[0].fullscreen;
|
|
206
208
|
let start_to_tray = pake_config.windows[0].start_to_tray && show_system_tray; // Only valid when tray is enabled
|
|
@@ -357,6 +359,9 @@ pub fn run_app() {
|
|
|
357
359
|
})
|
|
358
360
|
.on_window_event(move |_window, _event| {
|
|
359
361
|
if let tauri::WindowEvent::CloseRequested { api, .. } = _event {
|
|
362
|
+
if remember_url && _window.label() == "pake" {
|
|
363
|
+
save_last_url(_window.app_handle());
|
|
364
|
+
}
|
|
360
365
|
if hide_on_close && _window.label() == "pake" {
|
|
361
366
|
// User dismissed the window; do not let startup reveal reopen it.
|
|
362
367
|
cancel_startup_reveal(&close_revealed);
|
|
@@ -395,6 +400,9 @@ pub fn run_app() {
|
|
|
395
400
|
std::process::exit(1);
|
|
396
401
|
})
|
|
397
402
|
.run(move |_app, _event| {
|
|
403
|
+
if remember_url && matches!(&_event, tauri::RunEvent::Exit) {
|
|
404
|
+
save_last_url(_app);
|
|
405
|
+
}
|
|
398
406
|
// Handle macOS dock icon click to reopen hidden window
|
|
399
407
|
#[cfg(target_os = "macos")]
|
|
400
408
|
if let tauri::RunEvent::Reopen {
|
package/src-tauri/src/util.rs
CHANGED
|
@@ -48,6 +48,26 @@ pub fn get_data_dir(app: &AppHandle, package_name: String) -> std::io::Result<Pa
|
|
|
48
48
|
Ok(data_dir)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
pub fn read_last_url(path: &Path) -> std::io::Result<Option<tauri::Url>> {
|
|
52
|
+
match std::fs::read_to_string(path) {
|
|
53
|
+
Ok(value) => Ok(tauri::Url::parse(&value)
|
|
54
|
+
.ok()
|
|
55
|
+
.filter(|url| matches!(url.scheme(), "http" | "https"))),
|
|
56
|
+
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
|
57
|
+
Err(error) => Err(error),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pub fn write_last_url(path: &Path, url: &tauri::Url) -> std::io::Result<()> {
|
|
62
|
+
if matches!(url.scheme(), "http" | "https") {
|
|
63
|
+
if let Some(parent) = path.parent() {
|
|
64
|
+
std::fs::create_dir_all(parent)?;
|
|
65
|
+
}
|
|
66
|
+
std::fs::write(path, url.as_str())?;
|
|
67
|
+
}
|
|
68
|
+
Ok(())
|
|
69
|
+
}
|
|
70
|
+
|
|
51
71
|
/// Both native and IPC downloads use the trusted, packaged configuration.
|
|
52
72
|
pub fn get_download_dir(app: &AppHandle) -> Result<PathBuf, String> {
|
|
53
73
|
let state = app
|
|
@@ -257,6 +277,25 @@ mod tests {
|
|
|
257
277
|
dir
|
|
258
278
|
}
|
|
259
279
|
|
|
280
|
+
#[test]
|
|
281
|
+
fn last_url_round_trip_preserves_full_address() {
|
|
282
|
+
let path = temp_path("state/last-url.txt");
|
|
283
|
+
assert_eq!(read_last_url(&path).unwrap(), None);
|
|
284
|
+
for address in [
|
|
285
|
+
"https://acme.example/projects/42?view=board#activity",
|
|
286
|
+
"https://login.example/callback?code=example#result",
|
|
287
|
+
] {
|
|
288
|
+
let url = tauri::Url::parse(address).unwrap();
|
|
289
|
+
write_last_url(&path, &url).unwrap();
|
|
290
|
+
assert_eq!(read_last_url(&path).unwrap(), Some(url));
|
|
291
|
+
}
|
|
292
|
+
write_last_url(&path, &tauri::Url::parse("about:blank").unwrap()).unwrap();
|
|
293
|
+
assert!(read_last_url(&path).unwrap().is_some());
|
|
294
|
+
std::fs::write(&path, "invalid URL").unwrap();
|
|
295
|
+
assert_eq!(read_last_url(&path).unwrap(), None);
|
|
296
|
+
fs::remove_dir_all(path.parent().unwrap().parent().unwrap()).unwrap();
|
|
297
|
+
}
|
|
298
|
+
|
|
260
299
|
#[test]
|
|
261
300
|
fn expand_download_dir_preserves_absolute_paths_without_home() {
|
|
262
301
|
let absolute = temp_path("My Downloads");
|