feega 0.1.3 → 0.1.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/bin/feega.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn, spawnSync } from 'node:child_process';
3
- import { createWriteStream, mkdirSync, realpathSync, rmSync } from 'node:fs';
3
+ import { createWriteStream, mkdirSync, readFileSync, realpathSync, rmSync } from 'node:fs';
4
4
  import { homedir, tmpdir } from 'node:os';
5
5
  import { join } from 'node:path';
6
6
  import { Readable } from 'node:stream';
7
7
  import { pipeline } from 'node:stream/promises';
8
8
 
9
- import { appDir, bundleSource, cachedVersions, isCached, latestAppVersion, markCached, parseArgs, pickVersion } from '../launcher.mjs';
9
+ import { USAGE, appDir, bundleSource, boundUrl, cachedVersions, freePort, isCached, latestAppVersion, markCached, parseArgs, pickVersion } from '../launcher.mjs';
10
10
 
11
11
  const MIN_NODE_MAJOR = 22;
12
12
  const WEBSTORAGE_NODE_MAJOR = 25;
@@ -88,14 +88,26 @@ async function main() {
88
88
  throw new Error(`Node ${MIN_NODE_MAJOR}+ is required (found ${process.versions.node})`);
89
89
  }
90
90
 
91
- const { file, port, browser } = parseArgs(process.argv.slice(2), process.cwd());
91
+ const args = parseArgs(process.argv.slice(2), process.cwd());
92
+ if (args.command === 'help') {
93
+ console.log(USAGE);
94
+ return;
95
+ }
96
+
97
+ if (args.command === 'version') {
98
+ console.log(JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version);
99
+ return;
100
+ }
101
+
102
+ const { file, browser } = args;
103
+ const port = await freePort(args.port);
92
104
  const { version, action } = await chooseVersion(homedir());
93
105
  const dir = appDir(homedir(), version);
94
106
  if (action === 'download') {
95
107
  await install(dir, version);
96
108
  }
97
109
 
98
- const url = `http://localhost:${port}`;
110
+ const url = boundUrl(port);
99
111
  console.log(`feega: saving to ${file}`);
100
112
  console.log(`feega: open ${url}`);
101
113
 
package/launcher.mjs CHANGED
@@ -1,8 +1,11 @@
1
1
  import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
2
+ import { createServer } from 'node:net';
2
3
  import { join, resolve } from 'node:path';
3
4
 
4
5
  const DEFAULT_FILE = 'canvas.feega';
5
- const DEFAULT_PORT = 5173;
6
+ const DEFAULT_PORT = 0;
7
+ export const LOOPBACK = '127.0.0.1';
8
+ export const USAGE = `usage: feega [file.feega] [--port <n>] [--no-open] [--help] [--version]`;
6
9
  const CACHED_MARKER = '.complete';
7
10
  const APP_TAG = 'app-v';
8
11
  const RELEASES = 'https://github.com/andreabuttarelli/feega/releases/download';
@@ -25,6 +28,14 @@ export function parseArgs(argv, cwd) {
25
28
 
26
29
  while (rest.length) {
27
30
  const arg = rest.shift();
31
+ if (arg === '--help' || arg === '-h') {
32
+ return { command: 'help' };
33
+ }
34
+
35
+ if (arg === '--version') {
36
+ return { command: 'version' };
37
+ }
38
+
28
39
  if (arg === '--no-open') {
29
40
  browser = 'skip';
30
41
  continue;
@@ -40,10 +51,14 @@ export function parseArgs(argv, cwd) {
40
51
  continue;
41
52
  }
42
53
 
54
+ if (arg.startsWith('-')) {
55
+ throw new Error(`unknown option: ${arg}\n${USAGE}`);
56
+ }
57
+
43
58
  files.push(arg);
44
59
  }
45
60
 
46
- return { file: resolve(cwd, files[0] ?? DEFAULT_FILE), port, browser };
61
+ return { command: 'run', file: resolve(cwd, files[0] ?? DEFAULT_FILE), port, browser };
47
62
  }
48
63
 
49
64
  export const appDir = (home, version) => join(home, '.feega', 'app', version);
@@ -94,3 +109,15 @@ export function pickVersion(latest, cached) {
94
109
 
95
110
  return { version: cached[0], action: 'cache' };
96
111
  }
112
+
113
+ export const boundUrl = (port) => `http://${LOOPBACK}:${port}`;
114
+
115
+ export const freePort = (port) =>
116
+ new Promise((done, fail) => {
117
+ const probe = createServer();
118
+ probe.once('error', fail);
119
+ probe.listen(port, LOOPBACK, () => {
120
+ const { port: bound } = probe.address();
121
+ probe.close(() => done(bound));
122
+ });
123
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feega",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Run feega's infinite canvas locally, saved to a .feega file. No account, no database.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",