kryptheon 0.1.40 → 0.1.41

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/bin/kryptheon.js CHANGED
@@ -2632,9 +2632,12 @@ function dashboardAlreadyRunning(port) {
2632
2632
  res.on('data', (chunk) => { if (body.length < 5000000) body += chunk; });
2633
2633
  res.on('end', () => {
2634
2634
  try {
2635
- const root = JSON.parse(body).project.root;
2635
+ const state = JSON.parse(body);
2636
+ const root = state.project.root;
2636
2637
  const same = (a) => (process.platform === 'win32' ? path.resolve(a).toLowerCase() : path.resolve(a));
2637
- resolve(res.statusCode === 200 && typeof root === 'string' && same(root) === same(USER_DIR));
2638
+ if (res.statusCode !== 200 || typeof root !== 'string' || same(root) !== same(USER_DIR)) return resolve(false);
2639
+ // Before 0.1.41 a dashboard did not say which it was: an old one.
2640
+ resolve({ version: typeof state.kryptheon === 'string' ? state.kryptheon : null });
2638
2641
  } catch (err) {
2639
2642
  resolve(false);
2640
2643
  }
@@ -2645,6 +2648,49 @@ function dashboardAlreadyRunning(port) {
2645
2648
  });
2646
2649
  }
2647
2650
 
2651
+ /** The process listening on 127.0.0.1:port, or null. */
2652
+ function listenerOn(port) {
2653
+ try {
2654
+ if (process.platform === 'win32') {
2655
+ const out = spawnSync('netstat', ['-ano', '-p', 'TCP'], { encoding: 'utf8', windowsHide: true, timeout: 10000 }).stdout || '';
2656
+ for (const line of out.split(/\r?\n/)) {
2657
+ const m = line.trim().split(/\s+/);
2658
+ if (m.length >= 5 && m[1] === '127.0.0.1:' + port && m[3] === 'LISTENING' && /^\d+$/.test(m[4])) return Number(m[4]);
2659
+ }
2660
+ return null;
2661
+ }
2662
+ const out = spawnSync('lsof', ['-nP', '-iTCP@127.0.0.1:' + port, '-sTCP:LISTEN', '-t'], { encoding: 'utf8', timeout: 10000 }).stdout || '';
2663
+ const pid = Number(out.trim().split(/\s+/)[0]);
2664
+ return pid > 0 ? pid : null;
2665
+ } catch (err) {
2666
+ return null;
2667
+ }
2668
+ }
2669
+
2670
+ /**
2671
+ * An older dashboard for this same folder, still running - from yesterday's
2672
+ * Win+R, or started at sign-in - is stopped so this version takes its place.
2673
+ * Found when the first paying user ran the new version and kept getting the
2674
+ * old page: npx fetched the new one, which then only opened the old one.
2675
+ * Only the process that answered as this folder's Kryptheon dashboard is
2676
+ * stopped. Whether the port came free is the answer.
2677
+ */
2678
+ async function replaceOldDashboard(port) {
2679
+ const pid = listenerOn(port);
2680
+ if (!pid || pid === process.pid) return false;
2681
+ try {
2682
+ if (process.platform === 'win32') spawnSync('taskkill', ['/PID', String(pid), '/F'], { windowsHide: true, timeout: 10000 });
2683
+ else process.kill(pid, 'SIGTERM');
2684
+ } catch (err) {
2685
+ return false;
2686
+ }
2687
+ for (let i = 0; i < 50; i++) {
2688
+ if (listenerOn(port) === null) return true;
2689
+ await new Promise((r) => setTimeout(r, 100));
2690
+ }
2691
+ return false;
2692
+ }
2693
+
2648
2694
  /**
2649
2695
  * The dashboard, started from the home folder: kept in a kryptheon-tests
2650
2696
  * folder there, the one record already uses - set up the first time, asked
@@ -2732,7 +2778,16 @@ async function dashboardCommand() {
2732
2778
  }
2733
2779
  // Running already: open that one rather than a second, which would watch
2734
2780
  // the same project twice. Exit code 0 is how the launcher knows.
2735
- if (await dashboardAlreadyRunning(DASHBOARD_PORT)) {
2781
+ const running = await dashboardAlreadyRunning(DASHBOARD_PORT);
2782
+ const mine = require(path.join(PACKAGE_DIR, 'package.json')).version;
2783
+ if (running && running.version !== mine) {
2784
+ const was = running.version || 'from before 0.1.41';
2785
+ console.log('');
2786
+ console.log(await replaceOldDashboard(DASHBOARD_PORT)
2787
+ ? ' An older Kryptheon dashboard (' + was + ') was running for this folder.\n It was closed, and ' + mine + ' starts in its place.'
2788
+ : ' An older Kryptheon dashboard (' + was + ') is still running and could not be\n closed. This one starts at another address; close the old window when you can.');
2789
+ }
2790
+ if (running && running.version === mine) {
2736
2791
  const address = 'http://127.0.0.1:' + DASHBOARD_PORT + '/';
2737
2792
  const shown = openInBrowser(address);
2738
2793
  console.log('');
@@ -25,6 +25,7 @@ const { spawnSync } = require('child_process');
25
25
  const store = require('./kryptheon-store.js');
26
26
  const changes = require('./kryptheon-changes.js');
27
27
  const autostart = require('./kryptheon-autostart.js');
28
+ const VERSION = require('./package.json').version;
28
29
 
29
30
  /**
30
31
  * Every check the dashboard knows the name of, in the order the person asked
@@ -543,6 +544,9 @@ function buildState(root, env) {
543
544
 
544
545
  return {
545
546
  project: { name: path.basename(root), root: root, storeDir: paths.dir },
547
+ // Which kryptheon this dashboard is: a newer `dashboard` asks, and takes
548
+ // the place of an older one still running, instead of opening it.
549
+ kryptheon: VERSION,
546
550
  looks: looks,
547
551
  proven: proven,
548
552
  checks: checks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kryptheon",
3
- "version": "0.1.40",
3
+ "version": "0.1.41",
4
4
  "description": "Record what you do in your app and get plain-language reports when it breaks.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "commonjs",