pake-cli 3.15.0 โ 3.15.1
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 +95 -12
- package/package.json +1 -1
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/src/inject/event.js +17 -2
- package/src-tauri/tauri.conf.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ import * as psl from 'psl';
|
|
|
20
20
|
import { InvalidArgumentError, program as program$1, Option } from 'commander';
|
|
21
21
|
|
|
22
22
|
var name = "pake-cli";
|
|
23
|
-
var version = "3.15.
|
|
23
|
+
var version = "3.15.1";
|
|
24
24
|
var description = "๐คฑ๐ป Turn any webpage into a desktop app with one command. ๐คฑ๐ป ไธ้ฎๆๅ
็ฝ้กต็ๆ่ฝป้ๆก้ขๅบ็จใ";
|
|
25
25
|
var engines = {
|
|
26
26
|
node: ">=18.0.0"
|
|
@@ -680,15 +680,63 @@ async function copyTemplateConfigs() {
|
|
|
680
680
|
async function stageLocalTree(sourceDir) {
|
|
681
681
|
const distDir = path.join(npmDirectory, 'dist');
|
|
682
682
|
const distBakDir = path.join(npmDirectory, 'dist_bak');
|
|
683
|
-
|
|
683
|
+
// Resolve symlinked input up front: staging must produce a real copy, or
|
|
684
|
+
// the cli.js copy-back below would write through the link into the user's
|
|
685
|
+
// own directory.
|
|
686
|
+
const resolvedSource = await fsExtra.realpath(sourceDir);
|
|
687
|
+
const resolvedPackage = await fsExtra
|
|
688
|
+
.realpath(npmDirectory)
|
|
689
|
+
.catch(() => path.resolve(npmDirectory));
|
|
690
|
+
const packageDist = path.join(resolvedPackage, 'dist');
|
|
691
|
+
if (resolvedSource === resolvedPackage ||
|
|
692
|
+
resolvedPackage.startsWith(resolvedSource + path.sep) ||
|
|
693
|
+
resolvedSource === packageDist ||
|
|
694
|
+
resolvedSource.startsWith(packageDist + path.sep)) {
|
|
695
|
+
throw new PakeError(`Local input "${sourceDir}" contains the Pake CLI installation itself.`, {
|
|
696
|
+
code: 'INVALID_INPUT',
|
|
697
|
+
hint: 'Point Pake at your built output directory, not at a directory containing pake-cli.',
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
try {
|
|
701
|
+
if (await fsExtra.pathExists(distBakDir)) {
|
|
702
|
+
fsExtra.removeSync(distDir);
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
fsExtra.moveSync(distDir, distBakDir);
|
|
706
|
+
}
|
|
707
|
+
fsExtra.copySync(resolvedSource, distDir, {
|
|
708
|
+
overwrite: true,
|
|
709
|
+
dereference: true,
|
|
710
|
+
});
|
|
711
|
+
const filesToCopyBack = ['cli.js'];
|
|
712
|
+
await Promise.all(filesToCopyBack.map((file) => fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file))));
|
|
713
|
+
}
|
|
714
|
+
catch (error) {
|
|
715
|
+
// Never leave the package without its own dist/: cli.js lives there and
|
|
716
|
+
// every later `pake` invocation would fail until a manual reinstall.
|
|
717
|
+
restoreLocalTree();
|
|
718
|
+
throw error;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
// Put the package's original dist/ back once a local-input run is over (or
|
|
722
|
+
// failed). Tauri bakes `frontendDist: ../dist` into every binary, so a stale
|
|
723
|
+
// staged tree would leak this user's files into the next app built from the
|
|
724
|
+
// same install. Safe to call on any run: a present dist_bak always holds the
|
|
725
|
+
// original package dist, including one stranded by an older crashed run.
|
|
726
|
+
function restoreLocalTree() {
|
|
727
|
+
const distDir = path.join(npmDirectory, 'dist');
|
|
728
|
+
const distBakDir = path.join(npmDirectory, 'dist_bak');
|
|
729
|
+
if (!fsExtra.pathExistsSync(distBakDir)) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
try {
|
|
684
733
|
fsExtra.removeSync(distDir);
|
|
734
|
+
fsExtra.moveSync(distBakDir, distDir);
|
|
685
735
|
}
|
|
686
|
-
|
|
687
|
-
|
|
736
|
+
catch (error) {
|
|
737
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
738
|
+
logger.warn(`Failed to restore the CLI's original dist/ from dist_bak: ${detail}`);
|
|
688
739
|
}
|
|
689
|
-
fsExtra.copySync(sourceDir, distDir, { overwrite: true });
|
|
690
|
-
const filesToCopyBack = ['cli.js'];
|
|
691
|
-
await Promise.all(filesToCopyBack.map((file) => fsExtra.copy(path.join(distBakDir, file), path.join(distDir, file))));
|
|
692
740
|
}
|
|
693
741
|
// Exported for unit tests (web fallback and directory entry guard).
|
|
694
742
|
async function handleLocalFile(url, useLocalFile, tauriConf) {
|
|
@@ -3175,6 +3223,15 @@ ${green('|_| \\__,_|_|\\_\\___| can turn any webpage into a desktop app with
|
|
|
3175
3223
|
const REJECTED_KEYS = new Set(['config', 'json', 'version']);
|
|
3176
3224
|
// Optional CLI options that have no entry in DEFAULT_PAKE_OPTIONS.
|
|
3177
3225
|
const EXTRA_STRING_KEYS = new Set(['name', 'title', 'identifier']);
|
|
3226
|
+
// Numeric fields share the CLI flag ranges (see cli-program.ts validators),
|
|
3227
|
+
// so a config file cannot smuggle a value the same flag would reject.
|
|
3228
|
+
const NUMBER_RANGES = {
|
|
3229
|
+
width: { min: 0 },
|
|
3230
|
+
height: { min: 0 },
|
|
3231
|
+
minWidth: { min: 0 },
|
|
3232
|
+
minHeight: { min: 0 },
|
|
3233
|
+
zoom: { min: 50, max: 200 },
|
|
3234
|
+
};
|
|
3178
3235
|
function expectedTypeFor(key) {
|
|
3179
3236
|
if (key === 'inject')
|
|
3180
3237
|
return 'string[]';
|
|
@@ -3252,6 +3309,20 @@ async function loadConfigFile(configPath, validKeys) {
|
|
|
3252
3309
|
hint: 'See schema/pake.schema.json for field types.',
|
|
3253
3310
|
});
|
|
3254
3311
|
}
|
|
3312
|
+
if (typeof value === 'number') {
|
|
3313
|
+
const range = NUMBER_RANGES[key];
|
|
3314
|
+
const min = range?.min ?? 0;
|
|
3315
|
+
const max = range?.max;
|
|
3316
|
+
if (!Number.isFinite(value) ||
|
|
3317
|
+
value < min ||
|
|
3318
|
+
(max !== undefined && value > max)) {
|
|
3319
|
+
const bounds = max !== undefined ? `${min}-${max}` : `>= ${min}`;
|
|
3320
|
+
throw new PakeError(`Config field "${key}" must be a finite number (${bounds}).`, {
|
|
3321
|
+
code: 'INVALID_INPUT',
|
|
3322
|
+
hint: 'See schema/pake.schema.json for field ranges.',
|
|
3323
|
+
});
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3255
3326
|
if (!expected && (typeof value === 'object' || value === null)) {
|
|
3256
3327
|
throw new PakeError(`Config field "${key}" must be a string, number, or boolean.`, {
|
|
3257
3328
|
code: 'INVALID_INPUT',
|
|
@@ -3314,6 +3385,9 @@ program.action(async (urlArg, options) => {
|
|
|
3314
3385
|
let appName = null;
|
|
3315
3386
|
let url = urlArg;
|
|
3316
3387
|
try {
|
|
3388
|
+
// Heal a dist_bak stranded by an earlier crashed local-input run before
|
|
3389
|
+
// building, or this build would embed that run's staged files.
|
|
3390
|
+
restoreLocalTree();
|
|
3317
3391
|
if (!jsonMode) {
|
|
3318
3392
|
await checkUpdateTips();
|
|
3319
3393
|
}
|
|
@@ -3379,7 +3453,7 @@ program.action(async (urlArg, options) => {
|
|
|
3379
3453
|
// program.help() and --help/--version throw under exitOverride with
|
|
3380
3454
|
// exitCode 0; a clean commander exit is not a failure.
|
|
3381
3455
|
if (isCommanderExit(error) && error.exitCode === 0) {
|
|
3382
|
-
|
|
3456
|
+
return;
|
|
3383
3457
|
}
|
|
3384
3458
|
const classified = classifyError(error, phase);
|
|
3385
3459
|
if (jsonMode) {
|
|
@@ -3408,14 +3482,22 @@ program.action(async (urlArg, options) => {
|
|
|
3408
3482
|
else {
|
|
3409
3483
|
console.error(chalk.red(`โ Unexpected error: ${String(error)}`));
|
|
3410
3484
|
}
|
|
3411
|
-
process.exit
|
|
3485
|
+
// exitCode + natural exit instead of process.exit: lets the finally
|
|
3486
|
+
// restore run and guarantees the JSON result is flushed on piped stdout.
|
|
3487
|
+
process.exitCode = ERROR_EXIT_CODES[classified.code];
|
|
3488
|
+
}
|
|
3489
|
+
finally {
|
|
3490
|
+
// A local-input run replaces the package's own dist/ during staging; put
|
|
3491
|
+
// it back so the CLI stays intact and later builds cannot embed this
|
|
3492
|
+
// user's files.
|
|
3493
|
+
restoreLocalTree();
|
|
3412
3494
|
}
|
|
3413
3495
|
});
|
|
3414
3496
|
program.parseAsync().catch((error) => {
|
|
3415
3497
|
if (isCommanderExit(error)) {
|
|
3416
3498
|
// --help / --version and friends exit clean; commander already printed.
|
|
3417
3499
|
if (error.exitCode === 0) {
|
|
3418
|
-
|
|
3500
|
+
return;
|
|
3419
3501
|
}
|
|
3420
3502
|
// Parse errors (unknown option, invalid argument, missing value) are
|
|
3421
3503
|
// invalid input. Commander already printed the message to stderr; in
|
|
@@ -3435,7 +3517,8 @@ program.parseAsync().catch((error) => {
|
|
|
3435
3517
|
},
|
|
3436
3518
|
});
|
|
3437
3519
|
}
|
|
3438
|
-
process.
|
|
3520
|
+
process.exitCode = ERROR_EXIT_CODES.INVALID_INPUT;
|
|
3521
|
+
return;
|
|
3439
3522
|
}
|
|
3440
3523
|
if (error instanceof Error) {
|
|
3441
3524
|
console.error(chalk.red(`โ ${error.message}`));
|
|
@@ -3443,5 +3526,5 @@ program.parseAsync().catch((error) => {
|
|
|
3443
3526
|
else {
|
|
3444
3527
|
console.error(chalk.red(`โ Unexpected error: ${String(error)}`));
|
|
3445
3528
|
}
|
|
3446
|
-
process.
|
|
3529
|
+
process.exitCode = 1;
|
|
3447
3530
|
});
|
package/package.json
CHANGED
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED
|
@@ -207,6 +207,11 @@ function insertTextIntoEditableElement(element, text) {
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
let clipboardPasteFallbackTarget;
|
|
210
|
+
let clipboardPasteFallbackArmedAt = 0;
|
|
211
|
+
// An armed fallback older than this is a leftover from a keyup the window
|
|
212
|
+
// never saw (alt-tab mid-press); firing it on a later plain "v" keyup would
|
|
213
|
+
// paste unexpectedly.
|
|
214
|
+
const CLIPBOARD_PASTE_FALLBACK_TTL_MS = 5000;
|
|
210
215
|
|
|
211
216
|
function pasteClipboardText(activeElement) {
|
|
212
217
|
const readText = navigator.clipboard?.readText;
|
|
@@ -253,8 +258,16 @@ function handleClipboardShortcut(event) {
|
|
|
253
258
|
if (key === "v" && canPasteIntoEditableElement(activeElement)) {
|
|
254
259
|
// Let the native WebView paste event run first so images, files, and rich
|
|
255
260
|
// clipboard formats remain intact. If the platform does not emit paste,
|
|
256
|
-
// keyup applies the existing text-only fallback.
|
|
257
|
-
|
|
261
|
+
// keyup applies the existing text-only fallback. Key-repeat must not
|
|
262
|
+
// re-arm: after a native paste already fired and disarmed the fallback,
|
|
263
|
+
// a repeat keydown re-arming it would make keyup paste text a second
|
|
264
|
+
// time. Repeats only refresh the TTL of a still-armed target.
|
|
265
|
+
if (!event.repeat) {
|
|
266
|
+
clipboardPasteFallbackTarget = activeElement;
|
|
267
|
+
clipboardPasteFallbackArmedAt = Date.now();
|
|
268
|
+
} else if (clipboardPasteFallbackTarget === activeElement) {
|
|
269
|
+
clipboardPasteFallbackArmedAt = Date.now();
|
|
270
|
+
}
|
|
258
271
|
return false;
|
|
259
272
|
}
|
|
260
273
|
|
|
@@ -276,9 +289,11 @@ function handleClipboardPasteFallback(event) {
|
|
|
276
289
|
}
|
|
277
290
|
|
|
278
291
|
const activeElement = clipboardPasteFallbackTarget;
|
|
292
|
+
const armedAt = clipboardPasteFallbackArmedAt;
|
|
279
293
|
clipboardPasteFallbackTarget = undefined;
|
|
280
294
|
if (
|
|
281
295
|
!activeElement ||
|
|
296
|
+
Date.now() - armedAt > CLIPBOARD_PASTE_FALLBACK_TTL_MS ||
|
|
282
297
|
document.activeElement !== activeElement ||
|
|
283
298
|
!canPasteIntoEditableElement(activeElement)
|
|
284
299
|
) {
|