@lazyneoaz/testfca 1.0.5 → 1.0.6

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/index.js CHANGED
@@ -1,2 +1,9 @@
1
1
  "use strict";
2
+
3
+ // Inject Node's Mozilla CA bundle into SSL_CERT_FILE *before* any module loads
4
+ // the Go native binary (meta-messenger.js / messagix.so). Go reads SSL_CERT_FILE
5
+ // lazily on the first TLS dial — setting it here, at library entry, guarantees
6
+ // it is always in place in every hosting environment (Railway, Docker, Render …).
7
+ require('./src/utils/goCerts').ensureGoCerts();
8
+
2
9
  module.exports = require('./src/engine/client');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyneoaz/testfca",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "type": "commonjs",
5
5
  "description": "Advanced Facebook Chat API client for building Messenger bots — supports real-time messaging, thread management, MQTT, session stability, anti-automation protection, and real E2EE via Signal Protocol.",
6
6
  "main": "index.js",
@@ -1,24 +1,24 @@
1
1
  "use strict";
2
2
 
3
3
  /**
4
- * Postinstall: replace meta-messenger.js's binary with our TLS-fixed build.
4
+ * Postinstall: ensure meta-messenger.js has a working native binary.
5
5
  *
6
- * WHY this is needed:
7
- * The meta-messenger.js binary lacks InsecureTLS=true in its messagix HTTP
8
- * client. In sandboxed/containerised environments (Replit, Docker, CI) the
9
- * Go runtime cannot locate system CA certificates, so every prekey-bundle
10
- * HTTPS request to Facebook fails silently. Without prekeys, Signal sessions
11
- * are never established and sendE2EEMessage always times out.
6
+ * meta-messenger.js's own postinstall runs first (npm guarantees dependency
7
+ * scripts run before the dependent's postinstall). It places the binary via:
8
+ * 1. A prebuilt shipped in its npm tarball (if present)
9
+ * 2. A download from GitHub Releases
10
+ * 3. A local `go build` (if MESSAGIX_BUILD_FROM_SOURCE=true)
12
11
  *
13
- * Our prebuilt/ ships the same binary compiled with InsecureTLS=true.
12
+ * This script validates that a binary was placed. If for any reason it was
13
+ * not (e.g. the GitHub release download failed, npm ci --ignore-scripts was
14
+ * used), we copy the prebuilt binary we ship in prebuilt/ as a fallback.
14
15
  *
15
- * INSTALL ORDER (guaranteed by npm):
16
- * 1. meta-messenger.js postinstall runs first (copies/downloads their binary)
17
- * 2. Our postinstall runs second and overwrites it with ours.
18
- *
19
- * PATH RESOLUTION:
20
- * Uses require.resolve() so the path is correct whether meta-messenger.js is
21
- * at the project root node_modules/ or nested — works in all npm versions.
16
+ * TLS NOTE:
17
+ * TLS certificate errors in containerized environments (Railway, Docker, etc.)
18
+ * are fixed at runtime by src/utils/goCerts.js not by a patched binary.
19
+ * Go reads SSL_CERT_FILE lazily on the first TLS dial. We set it to Node's
20
+ * built-in Mozilla CA bundle before any connection is attempted.
21
+ * No binary modification is required for this fix.
22
22
  */
23
23
 
24
24
  const fs = require('fs');
@@ -35,7 +35,7 @@ function platformKey() {
35
35
  return null;
36
36
  }
37
37
 
38
- function ext() {
38
+ function binaryExt() {
39
39
  if (process.platform === 'win32') return '.dll';
40
40
  if (process.platform === 'darwin') return '.dylib';
41
41
  return '.so';
@@ -43,7 +43,6 @@ function ext() {
43
43
 
44
44
  function findMetaMessengerBuildDir() {
45
45
  try {
46
- // resolve() finds the package regardless of hoisting depth
47
46
  const pkgJson = require.resolve('meta-messenger.js/package.json');
48
47
  return path.join(path.dirname(pkgJson), 'build');
49
48
  } catch (_) {
@@ -59,39 +58,43 @@ function main() {
59
58
 
60
59
  const key = platformKey();
61
60
  if (!key) {
62
- console.log('[nkxfca] postinstall: unsupported platform (' + process.platform + '/' + process.arch + '), skipping binary patch.');
61
+ console.log('[nkxfca] postinstall: unsupported platform (' + process.platform + '/' + process.arch + '), skipping.');
63
62
  return;
64
63
  }
65
64
 
66
- const binaryExt = ext();
67
- const ourBin = path.join(__dirname, '..', 'prebuilt', key, 'messagix' + binaryExt);
65
+ const ext = binaryExt();
66
+ const mmBuildDir = findMetaMessengerBuildDir();
68
67
 
69
- if (!fs.existsSync(ourBin)) {
70
- console.log('[nkxfca] postinstall: no prebuilt for ' + key + ', skipping binary patch.');
68
+ if (!mmBuildDir) {
69
+ console.log('[nkxfca] postinstall: meta-messenger.js not found skipping binary check.');
71
70
  return;
72
71
  }
73
72
 
74
- const mmBuildDir = findMetaMessengerBuildDir();
75
- if (!mmBuildDir) {
76
- console.log('[nkxfca] postinstall: meta-messenger.js not found via require.resolve, skipping patch.');
73
+ const mmBin = path.join(mmBuildDir, 'messagix' + ext);
74
+
75
+ // Happy path: meta-messenger.js's own postinstall already placed the binary
76
+ if (fs.existsSync(mmBin) && fs.statSync(mmBin).size > 0) {
77
+ console.log('[nkxfca] postinstall: meta-messenger.js binary present — OK.');
77
78
  return;
78
79
  }
79
80
 
80
- const mmBin = path.join(mmBuildDir, 'messagix' + binaryExt);
81
+ // Fallback: meta-messenger.js postinstall failed (download error, no internet, etc.)
82
+ // Use the prebuilt we ship as insurance.
83
+ const ourBin = path.join(__dirname, '..', 'prebuilt', key, 'messagix' + ext);
84
+ if (!fs.existsSync(ourBin)) {
85
+ console.warn('[nkxfca] postinstall: no prebuilt for ' + key + ' and meta-messenger.js binary is missing.');
86
+ console.warn('[nkxfca] E2EE will not work. Try: MESSAGIX_BUILD_FROM_SOURCE=true npm install');
87
+ return;
88
+ }
81
89
 
82
- // Ensure the build directory exists (meta-messenger.js postinstall creates it,
83
- // but guard against edge cases where it hasn't run yet)
84
90
  try {
85
91
  fs.mkdirSync(mmBuildDir, { recursive: true });
86
- } catch (_) {}
87
-
88
- try {
89
92
  fs.copyFileSync(ourBin, mmBin);
90
- console.log('[nkxfca] postinstall: patched meta-messenger.js binary with TLS-fixed build (' + key + ')');
93
+ fs.chmodSync(mmBin, 0o755);
94
+ console.log('[nkxfca] postinstall: installed fallback prebuilt binary for ' + key);
91
95
  } catch (err) {
92
- // Non-fatal: warn but don't fail the install
93
- console.warn('[nkxfca] postinstall: could not patch binary: ' + err.message);
94
- console.warn('[nkxfca] E2EE messaging may not work in sandboxed environments.');
96
+ console.warn('[nkxfca] postinstall: could not copy fallback binary: ' + err.message);
97
+ console.warn('[nkxfca] E2EE may not work. Try reinstalling meta-messenger.js manually.');
95
98
  }
96
99
  }
97
100
 
@@ -3,95 +3,39 @@
3
3
  /**
4
4
  * Lightweight E2EE bridge — delegates to the `meta-messenger.js` npm package.
5
5
  *
6
- * Our postinstall replaces meta-messenger.js's binary with our TLS-fixed build
7
- * so that prekey HTTPS requests succeed in sandboxed environments (Replit, Docker,
8
- * Railway, etc.). We also patch at runtime (ensureTlsFixedBinary) so the fix
9
- * applies even when postinstall was skipped (npm ci --ignore-scripts, Railway
10
- * production installs, etc.).
6
+ * meta-messenger.js ships a native Go shared library (messagix.so) compiled
7
+ * from bridge-go/ Go source. The library uses Go's standard TLS stack
8
+ * (x509.SystemCertPool) which requires OS CA certificates to verify Facebook's
9
+ * TLS chain. Many hosting environments (Railway, Docker Alpine, Render, etc.)
10
+ * ship no CA bundle, causing every outbound TLS connection to fail with:
11
+ * "x509: certificate signed by unknown authority"
12
+ *
13
+ * THE FIX — SSL_CERT_FILE (see src/utils/goCerts.js):
14
+ * We set SSL_CERT_FILE to a temp file containing Node's built-in Mozilla CA
15
+ * bundle BEFORE the Go binary is ever loaded. ensureGoCerts() is called from
16
+ * index.js (library entry point) so it fires on the very first require().
17
+ * It is also called here at module evaluation time as a second guarantee.
11
18
  *
12
19
  * Key design decisions:
13
20
  * - enableE2EE: false → we call connectE2EE() explicitly; no auto-start race
14
- * - e2eeMemoryOnly: false → Signal sessions are persisted to devicePath on disk
21
+ * - e2eeMemoryOnly: false → Signal sessions persisted to devicePath on disk
15
22
  * - devicePath defaults to ".nkxfca_e2ee_device.json" in cwd when not provided
16
23
  */
17
24
 
18
25
  const fs = require('fs');
19
26
  const path = require('path');
20
27
 
21
- // ── Runtime binary patcher ────────────────────────────────────────────────────
22
- // Ensures the TLS-fixed binary is in place before meta-messenger.js is loaded.
23
- // Runs once; safe to call multiple times.
24
-
25
- let _patchDone = false;
26
-
27
- function _platformKey() {
28
- const p = process.platform;
29
- const a = process.arch;
30
- if (p === 'linux' && a === 'x64') return 'linux-x64-gnu';
31
- if (p === 'linux' && a === 'arm64') return 'linux-arm64-gnu';
32
- if (p === 'darwin' && a === 'x64') return 'darwin-x64';
33
- if (p === 'darwin' && a === 'arm64') return 'darwin-arm64';
34
- if (p === 'win32' && a === 'x64') return 'win32-x64-msvc';
35
- return null;
36
- }
37
-
38
- function _binaryExt() {
39
- if (process.platform === 'win32') return '.dll';
40
- if (process.platform === 'darwin') return '.dylib';
41
- return '.so';
42
- }
43
-
44
- function ensureTlsFixedBinary() {
45
- if (_patchDone) return;
46
- _patchDone = true;
47
-
48
- if (process.env.NKXFCA_SKIP_POSTINSTALL === 'true') return;
49
-
50
- const key = _platformKey();
51
- if (!key) return;
52
-
53
- const ext = _binaryExt();
54
- // bridge.js lives at src/e2ee/bridge.js → prebuilt/ is two levels up
55
- const ourBin = path.join(__dirname, '..', '..', 'prebuilt', key, 'messagix' + ext);
56
- if (!fs.existsSync(ourBin)) return;
57
-
58
- try {
59
- const pkgJson = require.resolve('meta-messenger.js/package.json');
60
- const buildDir = path.join(path.dirname(pkgJson), 'build');
61
- const mmBin = path.join(buildDir, 'messagix' + ext);
62
-
63
- fs.mkdirSync(buildDir, { recursive: true });
64
-
65
- // Only copy if sizes differ (avoids unnecessary writes on every boot)
66
- let needsCopy = true;
67
- if (fs.existsSync(mmBin)) {
68
- needsCopy = fs.statSync(ourBin).size !== fs.statSync(mmBin).size;
69
- }
70
-
71
- if (needsCopy) {
72
- fs.copyFileSync(ourBin, mmBin);
73
- fs.chmodSync(mmBin, 0o755);
74
- console.log('[nkxfca] runtime patch: applied TLS-fixed binary (' + key + ')');
75
- }
76
- } catch (err) {
77
- // Non-fatal — warn so the user knows why E2EE may not work
78
- console.warn('[nkxfca] runtime patch failed (' + err.message + '). E2EE TLS errors may occur in sandboxed environments.');
79
- }
80
- }
28
+ // Ensure Go TLS certs are ready — second guarantee (index.js is the primary)
29
+ require('../utils/goCerts').ensureGoCerts();
81
30
 
82
31
  // ─────────────────────────────────────────────────────────────────────────────
83
32
 
84
- let _Client = null;
33
+ let _Client = null;
85
34
  let _loadError = null;
86
35
 
87
36
  function loadClient() {
88
37
  if (_Client) return _Client;
89
38
  if (_loadError) throw _loadError;
90
-
91
- // Patch binary BEFORE the first require('meta-messenger.js') so the
92
- // TLS-fixed .so/.dylib/.dll is loaded by Node rather than the original.
93
- ensureTlsFixedBinary();
94
-
95
39
  try {
96
40
  const mm = require('meta-messenger.js');
97
41
  _Client = mm.Client || mm.default?.Client;
@@ -123,9 +67,9 @@ function newClient(cfg) {
123
67
  const cookies = cfg.cookies || {};
124
68
 
125
69
  const opts = {
126
- platform: cfg.platform || 'facebook',
127
- logLevel: cfg.logLevel || 'warn',
128
- enableE2EE: false, // we call connectE2EE() explicitly
70
+ platform: cfg.platform || 'facebook',
71
+ logLevel: cfg.logLevel || 'warn',
72
+ enableE2EE: false, // we call connectE2EE() explicitly
129
73
  e2eeMemoryOnly: false, // always persist sessions to disk
130
74
  autoReconnect: cfg.autoReconnect !== false,
131
75
  };
@@ -139,7 +83,7 @@ function newClient(cfg) {
139
83
 
140
84
  const client = new Client(cookies, opts);
141
85
  const handle = String(seq++);
142
- const entry = { client, eventBuffer: [], resolveWaiter: null, lastDeviceData: null };
86
+ const entry = { client, eventBuffer: [], resolveWaiter: null, lastDeviceData: null };
143
87
 
144
88
  for (const type of EVENT_TYPES) {
145
89
  client.on(type, (data) => {
@@ -228,7 +172,7 @@ function toBigInt(val) {
228
172
  return BigInt(String(val).split('@')[0]);
229
173
  }
230
174
 
231
- // ── E2EE operations ────────────────────────────────────────────────────────────
175
+ // ── E2EE operations ───────────────────────────────────────────────────────────
232
176
 
233
177
  async function sendE2EEMessage(handle, chatJid, text, replyToId, replyToSenderJid) {
234
178
  const opts = replyToId ? { replyToId, replyToSenderJid } : undefined;
@@ -292,7 +236,7 @@ async function uploadMedia(handle, options) {
292
236
  async function sendImage(handle, options) {
293
237
  const { chatJid, data, filename, caption, replyToId } = options;
294
238
  const opts = {};
295
- if (caption) opts.caption = caption;
239
+ if (caption) opts.caption = caption;
296
240
  if (replyToId) opts.replyToId = replyToId;
297
241
  return getEntry(handle).client.sendImage(
298
242
  toBigInt(chatJid), toBuffer(data), filename || 'image.jpg', Object.keys(opts).length ? opts : undefined
@@ -302,7 +246,7 @@ async function sendImage(handle, options) {
302
246
  async function sendVideo(handle, options) {
303
247
  const { chatJid, data, filename, caption, replyToId } = options;
304
248
  const opts = {};
305
- if (caption) opts.caption = caption;
249
+ if (caption) opts.caption = caption;
306
250
  if (replyToId) opts.replyToId = replyToId;
307
251
  return getEntry(handle).client.sendVideo(
308
252
  toBigInt(chatJid), toBuffer(data), filename || 'video.mp4', Object.keys(opts).length ? opts : undefined
@@ -320,7 +264,7 @@ async function sendVoice(handle, options) {
320
264
  async function sendFile(handle, options) {
321
265
  const { chatJid, data, filename, mimeType, caption, replyToId } = options;
322
266
  const opts = {};
323
- if (caption) opts.caption = caption;
267
+ if (caption) opts.caption = caption;
324
268
  if (replyToId) opts.replyToId = replyToId;
325
269
  return getEntry(handle).client.sendFile(
326
270
  toBigInt(chatJid), toBuffer(data), filename || 'file', mimeType || 'application/octet-stream',
@@ -361,7 +305,7 @@ function unload() {
361
305
  try { entry.client.disconnect(); } catch (_) {}
362
306
  }
363
307
  registry.clear();
364
- _Client = null;
308
+ _Client = null;
365
309
  _loadError = null;
366
310
  }
367
311
 
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * ensureGoCerts — inject Node's built-in Mozilla CA bundle into the process
5
+ * environment so that the Go binary inside meta-messenger.js can verify TLS
6
+ * certificates in any hosting environment.
7
+ *
8
+ * WHY this is needed:
9
+ * The Go binary (messagix.so) uses Go's standard TLS stack, which calls
10
+ * x509.SystemCertPool() to verify server certificates. In many hosting
11
+ * environments — Railway, Render, Docker Alpine, Heroku, etc. — the OS
12
+ * ships no CA certificate bundle (no /etc/ssl/certs), so SystemCertPool()
13
+ * returns an empty pool. Every outbound TLS connection the binary makes
14
+ * (Facebook's E2EE WebSocket, prekey HTTPS requests) then fails with:
15
+ * "x509: certificate signed by unknown authority"
16
+ *
17
+ * THE FIX:
18
+ * Go reads the SSL_CERT_FILE environment variable when calling
19
+ * x509.SystemCertPool(). If we set it to a valid PEM file before the first
20
+ * TLS connection, Go finds a full CA bundle. Node.js ships Mozilla's CA
21
+ * bundle as tls.rootCertificates — we write that to a temp file and point
22
+ * SSL_CERT_FILE at it. DigiCert (Facebook), Let's Encrypt, and all major
23
+ * CAs are included.
24
+ *
25
+ * TIMING:
26
+ * Must run before any TLS dial in the Go binary. We call it from index.js
27
+ * (library entry point) so it fires the moment anyone requires nkxfca,
28
+ * long before the Go binary is loaded or any connection is attempted.
29
+ *
30
+ * IDEMPOTENT:
31
+ * Safe to call multiple times — exits immediately on subsequent calls or
32
+ * when the host already provides SSL_CERT_FILE / SSL_CERT_DIR.
33
+ */
34
+
35
+ const fs = require('fs');
36
+ const path = require('path');
37
+ const os = require('os');
38
+
39
+ let _done = false;
40
+
41
+ function ensureGoCerts() {
42
+ if (_done) return;
43
+
44
+ // Respect host-provided cert configuration — don't override it
45
+ if (process.env.SSL_CERT_FILE || process.env.SSL_CERT_DIR) {
46
+ _done = true;
47
+ return;
48
+ }
49
+
50
+ try {
51
+ const { rootCertificates } = require('tls');
52
+ if (!rootCertificates || !rootCertificates.length) return;
53
+
54
+ const certFile = path.join(os.tmpdir(), '.nkxfca-cacert.pem');
55
+
56
+ // Write the bundle once per container/OS session (not per process start)
57
+ if (!fs.existsSync(certFile) || fs.statSync(certFile).size === 0) {
58
+ fs.writeFileSync(certFile, rootCertificates.join('\n'), 'utf8');
59
+ }
60
+
61
+ process.env.SSL_CERT_FILE = certFile;
62
+ _done = true;
63
+ } catch (_) {
64
+ // Never crash — worst case, Go falls back to the system cert store
65
+ }
66
+ }
67
+
68
+ module.exports = { ensureGoCerts };
@@ -1,61 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import { existsSync, mkdirSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- import { detectPlatform } from "./detect-platform.mjs";
7
- import { packageJson } from "./package.mjs";
8
-
9
- const __dirname = dirname(fileURLToPath(import.meta.url));
10
- const { ext } = detectPlatform();
11
- const { name } = packageJson;
12
- const bridgeDir = join(__dirname, "..", "bridge-e2ee", "bridge-go");
13
- const vendorDir = join(bridgeDir, "vendor");
14
- const hasVendor = existsSync(vendorDir);
15
-
16
- function runGo(args) {
17
- const res = spawnSync(process.env.GO_BIN || "go", args, {
18
- cwd: bridgeDir,
19
- stdio: "inherit",
20
- env: {
21
- ...process.env,
22
- CGO_ENABLED: "1",
23
- // Prevent Go from downloading a newer toolchain when go.mod specifies
24
- // a version higher than what is locally installed. The local compiler
25
- // should always be used in sandboxed / offline environments.
26
- GOTOOLCHAIN: "local",
27
- },
28
- });
29
- if (res.error) {
30
- console.error(`[${name}] Failed to spawn Go: ${res.error.message}`);
31
- process.exit(1);
32
- }
33
- if (res.status !== 0) process.exit(res.status || 1);
34
- }
35
-
36
- const buildDir = join(__dirname, "..", "build");
37
- if (!existsSync(buildDir)) mkdirSync(buildDir, { recursive: true });
38
-
39
- // Skip mod tidy when vendor directory is present — tidy can break vendor/go.mod consistency
40
- // and would overwrite the patched prekeys.go with the unpatched upstream version.
41
- if (!hasVendor) {
42
- console.log(`[${name}] Tidying Go modules...`);
43
- runGo(["mod", "tidy"]);
44
- } else {
45
- console.log(`[${name}] Vendor directory present — skipping mod tidy to preserve patches.`);
46
- }
47
-
48
- const buildArgs = [
49
- "build",
50
- ...(hasVendor ? ["-mod=vendor"] : []),
51
- "-buildmode=c-shared",
52
- "-ldflags=-s -w",
53
- "-o",
54
- join("..", "..", "build", `messagix.${ext}`),
55
- ".",
56
- ];
57
-
58
- console.log(`[${name}] Building native library (release mode)...`);
59
- runGo(buildArgs);
60
-
61
- console.log(`[${name}] Built native: build/messagix.${ext}`);
@@ -1,36 +0,0 @@
1
- import { execSync } from "node:child_process";
2
-
3
- export function detectPlatform() {
4
- const { platform } = process;
5
- const { arch } = process;
6
- const isMusl = detectMusl();
7
-
8
- const libc = platform === "linux" ? (isMusl ? "musl" : "gnu") : "";
9
- const triplet = platform === "linux" ? `${platform}-${arch}-${libc}` : `${platform}-${arch}`;
10
-
11
- const ext = platform === "win32" ? "dll" : platform === "darwin" ? "dylib" : "so";
12
-
13
- return { platform, arch, libc, triplet, ext };
14
- }
15
-
16
- function detectMusl() {
17
- try {
18
- if (process.platform !== "linux") return false;
19
- if (process.report && typeof process.report.getReport === "function") {
20
- const rep = process.report.getReport();
21
- const glibc = rep.header && rep.header.glibcVersionRuntime;
22
- return !glibc;
23
- }
24
- } catch {
25
- //
26
- }
27
- try {
28
- const out = execSync("ldd --version 2>&1 || true", { encoding: "utf8" });
29
- return /musl/i.test(out);
30
- } catch {
31
- //
32
- }
33
- return false;
34
- }
35
-
36
- export default detectPlatform;
@@ -1,108 +0,0 @@
1
- import { createWriteStream } from "node:fs";
2
- import { mkdir, rename, unlink } from "node:fs/promises";
3
- import https from "node:https";
4
- import { dirname, join } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
-
7
- import { detectPlatform } from "./detect-platform.mjs";
8
- import { packageJson as pkg } from "./package.mjs";
9
-
10
- const __dirname = dirname(fileURLToPath(import.meta.url));
11
-
12
- function buildBaseURL() {
13
- // yumi-team/meta-messenger.js hosts prebuilt messagix binaries for all
14
- // platforms except linux-x64-gnu, which ships directly in this package's
15
- // prebuilt/ directory and is therefore never fetched from here.
16
- const repo = "yumi-team/meta-messenger.js";
17
- const tag = "v1.1.3";
18
- return `https://github.com/${repo}/releases/download/${tag}`;
19
- }
20
-
21
- function httpGet(url, redirectCount = 0) {
22
- console.log(`[${pkg.name}] HTTP GET: ${url}${redirectCount > 0 ? ` (redirect #${redirectCount})` : ""}`);
23
- const reqStart = Date.now();
24
- return new Promise((resolve, reject) => {
25
- https
26
- .get(url, res => {
27
- console.log(`[${pkg.name}] Response: HTTP ${res.statusCode} in ${Date.now() - reqStart}ms`);
28
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
29
- console.log(`[${pkg.name}] Following redirect to: ${res.headers.location}`);
30
- return resolve(httpGet(res.headers.location, redirectCount + 1));
31
- }
32
- if (res.statusCode !== 200) {
33
- return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
34
- }
35
- resolve(res);
36
- })
37
- .on("error", err => {
38
- console.log(`[${pkg.name}] HTTP error after ${Date.now() - reqStart}ms:`, err?.message);
39
- reject(err);
40
- });
41
- });
42
- }
43
-
44
- async function downloadTo(url, dstPath) {
45
- console.log(`[${pkg.name}] Downloading to: ${dstPath}`);
46
- await mkdir(dirname(dstPath), { recursive: true });
47
- const tmp = `${dstPath}.download`;
48
- try {
49
- await unlink(tmp);
50
- } catch {
51
- //
52
- }
53
- const res = await httpGet(url);
54
- console.log(`[${pkg.name}] Starting file write...`);
55
- const writeStart = Date.now();
56
- let bytesWritten = 0;
57
- await new Promise((resolve, reject) => {
58
- const out = createWriteStream(tmp);
59
- res.on("data", chunk => {
60
- bytesWritten += chunk.length;
61
- });
62
- res.pipe(out);
63
- res.on("error", reject);
64
- out.on("error", reject);
65
- out.on("finish", () => {
66
- console.log(`[${pkg.name}] File write completed: ${bytesWritten} bytes in ${Date.now() - writeStart}ms`);
67
- res.destroy();
68
- resolve();
69
- });
70
- });
71
- await rename(tmp, dstPath);
72
- console.log(`[${pkg.name}] File renamed to final destination`);
73
- }
74
-
75
- export async function downloadPrebuilt() {
76
- console.log(`[${pkg.name}] downloadPrebuilt() started`);
77
- const { triplet, ext } = detectPlatform();
78
- const baseURL = buildBaseURL();
79
- const filename = `messagix-${triplet}.${ext}`;
80
- const url = `${baseURL}/${filename}`;
81
- console.log(`[${pkg.name}] Target URL: ${url}`);
82
-
83
- const out = join(__dirname, "..", "build", `messagix.${ext}`);
84
- const totalStart = Date.now();
85
- try {
86
- await downloadTo(url, out);
87
- console.log(`[${pkg.name}] Downloaded prebuilt from ${url} in ${Date.now() - totalStart}ms`);
88
- return true;
89
- } catch (err) {
90
- console.warn(
91
- `[${pkg.name}] No remote prebuilt found at ${url} (after ${Date.now() - totalStart}ms):`,
92
- err?.message || String(err),
93
- );
94
- return false;
95
- }
96
- }
97
-
98
- const isMain = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
99
- if (isMain) {
100
- downloadPrebuilt()
101
- .then(ok => {
102
- if (!ok) process.exit(1);
103
- })
104
- .catch(err => {
105
- console.error(`[${pkg.name}] download-prebuilt failed:`, err?.message || String(err));
106
- process.exit(1);
107
- });
108
- }
@@ -1,6 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
-
5
- const __dirname = dirname(fileURLToPath(import.meta.url));
6
- export const packageJson = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
@@ -1,95 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { copyFile, mkdir } from "node:fs/promises";
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- import { detectPlatform } from "./detect-platform.mjs";
7
- import { downloadPrebuilt } from "./download-prebuilt.mjs";
8
- import { packageJson as pkg } from "./package.mjs";
9
-
10
- const __dirname = dirname(fileURLToPath(import.meta.url));
11
-
12
- async function copyIfExists(src, dst) {
13
- try {
14
- await mkdir(dirname(dst), { recursive: true });
15
- await copyFile(src, dst);
16
- return true;
17
- } catch (err) {
18
- if (err?.code === "ENOENT") return false;
19
- throw err;
20
- }
21
- }
22
-
23
- async function run() {
24
- console.log(`[${pkg.name}] postinstall started`);
25
- const startTime = Date.now();
26
-
27
- if (process.env.MESSAGIX_SKIP_POSTINSTALL === "true") {
28
- console.log(`[${pkg.name}] Skipping postinstall (MESSAGIX_SKIP_POSTINSTALL=true)`);
29
- return;
30
- }
31
-
32
- console.log(`[${pkg.name}] Detecting platform...`);
33
- const { triplet, ext } = detectPlatform();
34
- console.log(`[${pkg.name}] Platform: ${triplet}, ext: ${ext}`);
35
-
36
- const buildOut = join(__dirname, "..", "build", `messagix.${ext}`);
37
- console.log(`[${pkg.name}] Build output path: ${buildOut}`);
38
-
39
- if (existsSync(buildOut)) {
40
- console.log(`[${pkg.name}] Native library already present at build/messagix.${ext}`);
41
- console.log(`[${pkg.name}] postinstall completed in ${Date.now() - startTime}ms`);
42
- return;
43
- }
44
-
45
- // 1) Prefer local prebuilt shipped in npm tarball
46
- const prebuiltDir = join(__dirname, "..", "prebuilt", triplet);
47
- const prebuilt = join(prebuiltDir, `messagix.${ext}`);
48
- console.log(`[${pkg.name}] Checking local prebuilt at: ${prebuilt}`);
49
- if (await copyIfExists(prebuilt, buildOut)) {
50
- console.log(`[${pkg.name}] Using local prebuilt for ${triplet}`);
51
- // Keep the prebuilt copy so reinstalls (e.g. npm rebuild, CI caches) don't
52
- // lose the patched binary and fall back to downloading an unpatched one.
53
- console.log(`[${pkg.name}] postinstall completed in ${Date.now() - startTime}ms`);
54
- return;
55
- }
56
- console.log(`[${pkg.name}] No local prebuilt found`);
57
-
58
- // 2) Try remote prebuilt from GitHub Releases
59
- console.log(`[${pkg.name}] Attempting to download remote prebuilt...`);
60
- const downloadStart = Date.now();
61
- try {
62
- if (await downloadPrebuilt()) {
63
- console.log(`[${pkg.name}] Download completed in ${Date.now() - downloadStart}ms`);
64
- console.log(`[${pkg.name}] postinstall completed in ${Date.now() - startTime}ms`);
65
- return;
66
- }
67
- } catch (err) {
68
- console.log(
69
- `[${pkg.name}] Download failed after ${Date.now() - downloadStart}ms:`,
70
- err?.message || String(err),
71
- );
72
- }
73
-
74
- console.warn(`[${pkg.name}] ──────────────────────────────────────────────────────────`);
75
- console.warn(`[${pkg.name}] No prebuilt E2EE bridge found for your platform.`);
76
- console.warn(`[${pkg.name}] Platform detected: ${triplet}`);
77
- console.warn(`[${pkg.name}]`);
78
- console.warn(`[${pkg.name}] Supported out-of-the-box:`);
79
- console.warn(`[${pkg.name}] linux-x64-gnu (ships inside the npm package)`);
80
- console.warn(`[${pkg.name}] linux-x64-musl / darwin-x64 / darwin-arm64 / win32-x64`);
81
- console.warn(`[${pkg.name}] (downloaded automatically from yumi-team/meta-messenger.js)`);
82
- console.warn(`[${pkg.name}]`);
83
- console.warn(`[${pkg.name}] If the download failed, check your internet connection and retry:`);
84
- console.warn(`[${pkg.name}] npm install`);
85
- console.warn(`[${pkg.name}]`);
86
- console.warn(`[${pkg.name}] Non-E2EE features (sendMessage, listen, etc.) work without the bridge.`);
87
- console.warn(`[${pkg.name}] ──────────────────────────────────────────────────────────`);
88
- }
89
-
90
- run()
91
- .then(() => process.exit(0))
92
- .catch(err => {
93
- console.error(`[${pkg.name}] postinstall failed:`, err?.message || String(err));
94
- process.exit(1);
95
- });