@bobfrankston/msger 0.1.198 → 0.1.200
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.
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,34 +1,76 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* msger-native postinstall
|
|
4
|
-
*
|
|
3
|
+
* msger-native postinstall:
|
|
4
|
+
* 1. Copy binary from target/release/ to bin/ (if newer — after cargo build)
|
|
5
|
+
* 2. Create timestamped copy (so new installs don't conflict with running old version)
|
|
6
|
+
* 3. Clean up old timestamped copies (skip if locked by running process)
|
|
7
|
+
* 4. Set binary permissions on Linux/Mac
|
|
5
8
|
*/
|
|
9
|
+
import fs from "fs";
|
|
6
10
|
import path from "path";
|
|
7
11
|
import { fileURLToPath } from "url";
|
|
8
12
|
|
|
9
13
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const
|
|
14
|
+
const nativeDir = path.join(__dirname, "..");
|
|
15
|
+
const binDir = path.join(nativeDir, "bin");
|
|
11
16
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
darwin: "msgernative",
|
|
15
|
-
darwinArm64: "msgernative-arm64",
|
|
16
|
-
linux: "msgernative",
|
|
17
|
-
linuxArm64: "msgernative-linux-aarch64",
|
|
18
|
-
};
|
|
17
|
+
const isWindows = process.platform === "win32";
|
|
18
|
+
const arch = process.arch;
|
|
19
19
|
|
|
20
|
+
let baseName, ext;
|
|
21
|
+
if (isWindows) {
|
|
22
|
+
baseName = "msgernative";
|
|
23
|
+
ext = ".exe";
|
|
24
|
+
} else if (process.platform === "darwin") {
|
|
25
|
+
baseName = arch === "arm64" ? "msgernative-arm64" : "msgernative";
|
|
26
|
+
ext = "";
|
|
27
|
+
} else {
|
|
28
|
+
baseName = arch === "arm64" ? "msgernative-linux-aarch64" : "msgernative";
|
|
29
|
+
ext = "";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const srcBinary = path.join(binDir, `${baseName}${ext}`);
|
|
33
|
+
const buildBinary = path.join(nativeDir, "target", "release", `${baseName}${ext}`);
|
|
34
|
+
|
|
35
|
+
// Step 1: Copy from target/release/ → bin/ if build output is newer
|
|
36
|
+
if (fs.existsSync(buildBinary)) {
|
|
37
|
+
const buildTime = fs.statSync(buildBinary).mtimeMs;
|
|
38
|
+
const srcTime = fs.existsSync(srcBinary) ? fs.statSync(srcBinary).mtimeMs : 0;
|
|
39
|
+
if (buildTime > srcTime) {
|
|
40
|
+
try {
|
|
41
|
+
fs.copyFileSync(buildBinary, srcBinary);
|
|
42
|
+
console.log(` msger: updated ${baseName}${ext} from build`);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
// Source might be locked by running process — that's OK, timestamped copy will use what we have
|
|
45
|
+
console.log(` msger: could not update ${baseName}${ext} (locked?) — using existing`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Step 2: Create timestamped copy
|
|
51
|
+
const stamp = Date.now();
|
|
52
|
+
const versionedBinary = path.join(binDir, `${baseName}-${stamp}${ext}`);
|
|
53
|
+
if (fs.existsSync(srcBinary)) {
|
|
54
|
+
try {
|
|
55
|
+
fs.copyFileSync(srcBinary, versionedBinary);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
console.error(` msger: failed to create versioned binary: ${e.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Step 3: Clean up old timestamped copies (keep the one we just created)
|
|
20
62
|
try {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
try { fs.chmodSync(bin, 0o755); } catch { /*
|
|
63
|
+
const pattern = new RegExp(`^${baseName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}-(\\d+)${ext.replace('.', '\\.')}$`);
|
|
64
|
+
for (const f of fs.readdirSync(binDir)) {
|
|
65
|
+
if (f.match(pattern) && f !== path.basename(versionedBinary)) {
|
|
66
|
+
try { fs.unlinkSync(path.join(binDir, f)); } catch { /* locked — cleaned up next time */ }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch { /* ignore */ }
|
|
70
|
+
|
|
71
|
+
// Step 4: Set permissions on Linux/Mac
|
|
72
|
+
if (!isWindows) {
|
|
73
|
+
for (const bin of [srcBinary, versionedBinary]) {
|
|
74
|
+
try { fs.chmodSync(bin, 0o755); } catch { /* */ }
|
|
33
75
|
}
|
|
34
76
|
}
|
package/package.json
CHANGED
package/shower.d.ts
CHANGED
|
@@ -41,8 +41,10 @@ export interface MessageBoxOptions {
|
|
|
41
41
|
debug?: boolean; /** Return debug information (HTML, size) in result (default: false) */
|
|
42
42
|
showVersion?: boolean; /** Show version in window title (default: false) */
|
|
43
43
|
appUserModelId?: string; /** Windows AppUserModelID for taskbar pinning (internal use) */
|
|
44
|
-
initScript?: string; /** Custom JS injected into WebView alongside msger-api.js */
|
|
44
|
+
initScript?: string; /** Custom JS injected into WebView alongside msger-api.js (inline) */
|
|
45
|
+
initScriptPath?: string; /** Path to JS file injected into WebView (avoids large JSON) */
|
|
45
46
|
service?: boolean; /** Service mode: bidirectional IPC with parent. Stdin/stdout stay open. */
|
|
47
|
+
contentDir?: string; /** Base directory for custom protocol file serving (avoids file:// URLs) */
|
|
46
48
|
}
|
|
47
49
|
export interface MessageBoxResult {
|
|
48
50
|
button: string;
|
package/shower.js
CHANGED
|
@@ -47,21 +47,41 @@ export function closeMessageBox(pid) {
|
|
|
47
47
|
* @param options Message box configuration
|
|
48
48
|
* @returns MessageBoxHandle with result promise
|
|
49
49
|
*/
|
|
50
|
-
|
|
50
|
+
/** Resolve the native binary path — uses timestamped name (e.g., msgernative-1712345678901.exe)
|
|
51
|
+
* so a new version can be installed while the old one is still running. */
|
|
52
|
+
function resolveBinaryPath() {
|
|
51
53
|
const isWindows = platform() === 'win32';
|
|
52
54
|
const arch = process.arch;
|
|
53
|
-
|
|
54
|
-
let
|
|
55
|
+
const binDir = path.join(import.meta.dirname, 'msger-native', 'bin');
|
|
56
|
+
let baseName;
|
|
57
|
+
let ext;
|
|
55
58
|
if (isWindows) {
|
|
56
|
-
|
|
59
|
+
baseName = 'msgernative';
|
|
60
|
+
ext = '.exe';
|
|
57
61
|
}
|
|
58
62
|
else if (arch === 'arm64') {
|
|
59
|
-
|
|
63
|
+
baseName = 'msgernative-linux-aarch64';
|
|
64
|
+
ext = '';
|
|
60
65
|
}
|
|
61
66
|
else {
|
|
62
|
-
|
|
67
|
+
baseName = 'msgernative';
|
|
68
|
+
ext = '';
|
|
69
|
+
}
|
|
70
|
+
// Find the latest timestamped binary, fall back to unversioned
|
|
71
|
+
try {
|
|
72
|
+
const pattern = new RegExp(`^${baseName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}-(\\d+)${ext.replace('.', '\\.')}$`);
|
|
73
|
+
const matches = fs.readdirSync(binDir)
|
|
74
|
+
.filter(f => pattern.test(f))
|
|
75
|
+
.sort()
|
|
76
|
+
.reverse();
|
|
77
|
+
if (matches.length > 0)
|
|
78
|
+
return path.join(binDir, matches[0]);
|
|
63
79
|
}
|
|
64
|
-
|
|
80
|
+
catch { /* fall through */ }
|
|
81
|
+
return path.join(binDir, `${baseName}${ext}`);
|
|
82
|
+
}
|
|
83
|
+
function createMessageBoxHandle(options) {
|
|
84
|
+
const binaryPath = resolveBinaryPath();
|
|
65
85
|
// Declare variables that will be used outside the Promise
|
|
66
86
|
const childEnv = { ...process.env };
|
|
67
87
|
if ('NODE_OPTIONS' in childEnv) {
|
|
@@ -77,7 +97,7 @@ function createMessageBoxHandle(options) {
|
|
|
77
97
|
return;
|
|
78
98
|
}
|
|
79
99
|
// On Unix systems, check if binary is executable
|
|
80
|
-
if (
|
|
100
|
+
if (platform() !== 'win32') {
|
|
81
101
|
try {
|
|
82
102
|
fs.accessSync(binaryPath, fs.constants.X_OK);
|
|
83
103
|
}
|
|
@@ -316,16 +336,7 @@ export class ServiceHandle {
|
|
|
316
336
|
*/
|
|
317
337
|
export function showService(options) {
|
|
318
338
|
const serviceOptions = { ...options, service: true, detach: false };
|
|
319
|
-
const
|
|
320
|
-
const arch = process.arch;
|
|
321
|
-
let binaryName;
|
|
322
|
-
if (isWindows)
|
|
323
|
-
binaryName = "msgernative.exe";
|
|
324
|
-
else if (arch === "arm64")
|
|
325
|
-
binaryName = "msgernative-linux-aarch64";
|
|
326
|
-
else
|
|
327
|
-
binaryName = "msgernative";
|
|
328
|
-
const binaryPath = path.join(import.meta.dirname, "msger-native", "bin", binaryName);
|
|
339
|
+
const binaryPath = resolveBinaryPath();
|
|
329
340
|
const childEnv = { ...process.env };
|
|
330
341
|
if ("NODE_OPTIONS" in childEnv)
|
|
331
342
|
delete childEnv.NODE_OPTIONS;
|
|
@@ -345,14 +356,20 @@ export function showService(options) {
|
|
|
345
356
|
rustOptions.height = rustOptions.size.height;
|
|
346
357
|
delete rustOptions.size;
|
|
347
358
|
}
|
|
348
|
-
// Convert local paths to file:// URLs (
|
|
349
|
-
if (rustOptions.url && !rustOptions.url.startsWith("http://") && !rustOptions.url.startsWith("https://") && !rustOptions.url.startsWith("file://")) {
|
|
359
|
+
// Convert local paths to file:// URLs when no custom protocol (contentDir) is used
|
|
360
|
+
if (!rustOptions.contentDir && rustOptions.url && !rustOptions.url.startsWith("http://") && !rustOptions.url.startsWith("https://") && !rustOptions.url.startsWith("file://")) {
|
|
350
361
|
const absolutePath = path.resolve(rustOptions.url);
|
|
351
362
|
if (!fs.existsSync(absolutePath)) {
|
|
352
363
|
throw new Error(`File not found: ${absolutePath}`);
|
|
353
364
|
}
|
|
354
365
|
rustOptions.url = pathToFileURL(absolutePath).href;
|
|
355
366
|
}
|
|
356
|
-
|
|
367
|
+
// Resolve contentDir to absolute path
|
|
368
|
+
if (rustOptions.contentDir) {
|
|
369
|
+
rustOptions.contentDir = path.resolve(rustOptions.contentDir);
|
|
370
|
+
}
|
|
371
|
+
const jsonToSend = JSON.stringify(rustOptions);
|
|
372
|
+
console.error(`[service] Sending to Rust: ${jsonToSend.slice(0, 200)}`);
|
|
373
|
+
child.stdin?.write(jsonToSend + "\n");
|
|
357
374
|
return new ServiceHandle(child);
|
|
358
375
|
}
|