glashjs 0.13.0 → 0.13.2

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/glash.mjs CHANGED
@@ -12,7 +12,10 @@ import { createProject } from '../src/create.mjs';
12
12
  import { generateTypedRoutes } from '../src/typed-routes.mjs';
13
13
 
14
14
  const VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
15
- const [, , cmd, ...rest] = process.argv;
15
+ let [, , cmd, ...rest] = process.argv;
16
+ if (cmd === 'run') {
17
+ cmd = rest.shift() || 'dev';
18
+ }
16
19
 
17
20
  function arg(name, fallback) {
18
21
  const i = rest.indexOf(name);
@@ -55,11 +58,12 @@ function lanAddresses() {
55
58
  async function serve(dev) {
56
59
  const root = arg('--root', process.cwd());
57
60
  const { listen, cfg, routes } = await createGlashServer({ root, dev });
58
- const port = Number(arg('--port', cfg.port || 3000));
59
- await listen(port);
61
+ const preferredPort = Number(arg('--port', cfg.port || 3000));
62
+ const port = await listenOnAvailablePort(listen, preferredPort);
60
63
  const pages = routes.filter((r) => !r.isApi).length;
61
64
  const apis = routes.filter((r) => r.isApi).length;
62
65
  console.log(`\nglashjs ${dev ? 'dev' : 'serve'} — "${cfg.name}"`);
66
+ if (port !== preferredPort) console.log(` Port ${preferredPort} is in use; using ${port} instead.`);
63
67
  console.log(` ${pages} page route(s), ${apis} api route(s)`);
64
68
  routes.forEach((r) => console.log(` ${r.isApi ? 'api ' : 'page'} ${r.pattern}`));
65
69
  console.log('');
@@ -69,6 +73,18 @@ async function serve(dev) {
69
73
  console.log('');
70
74
  }
71
75
 
76
+ async function listenOnAvailablePort(listen, preferredPort) {
77
+ for (let port = preferredPort; port < preferredPort + 20; port += 1) {
78
+ try {
79
+ await listen(port);
80
+ return port;
81
+ } catch (error) {
82
+ if (error?.code !== 'EADDRINUSE') throw error;
83
+ }
84
+ }
85
+ throw new Error(`No available port found from ${preferredPort} to ${preferredPort + 19}`);
86
+ }
87
+
72
88
  async function main() {
73
89
  switch (cmd) {
74
90
  case 'create':
@@ -144,6 +160,7 @@ async function main() {
144
160
  console.log(`glashjs — fast, offline-capable, hard-to-hack sites
145
161
 
146
162
  Usage: (run as "glashjs <cmd>"; "glash <cmd>" also works unless the glashdb deploy CLI owns that name)
163
+ glashjs run dev Alias for glashjs dev (also: glash run dev)
147
164
  glashjs create [name] Create a new Glash project (interactive)
148
165
  glashjs dev [--port 3000] Run the dev server (routing, SSR, API, live reload) + Network preview URL
149
166
  glashjs serve [--port 3000] Run the production server over routes/ + built assets
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glashjs",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "glashjs — The Postgres-native full-stack framework for builders who want to ship without DevOps. Framework, hosting, database, auth, and deploy in one GlashDB-native runtime.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,6 +34,7 @@ export async function createGlashServer({ root = process.cwd(), dev = false } =
34
34
  const cfg = await loadConfig(root);
35
35
  const routesDir = path.resolve(root, cfg.routesDir || 'routes');
36
36
  const outDir = path.resolve(root, cfg.outDir);
37
+ const publicDir = path.resolve(root, cfg.publicDir || 'public');
37
38
  const secHeaders = securityHeaders(cfg.security);
38
39
  let routes = await discoverRoutes(routesDir);
39
40
 
@@ -72,6 +73,7 @@ export async function createGlashServer({ root = process.cwd(), dev = false } =
72
73
  // Static first: in production this serves prebuilt /_glash/<id>.js bundles
73
74
  // (written by `glash build`) — no runtime esbuild needed.
74
75
  if (await serveStatic(res, outDir, pathname, req, secHeaders)) return;
76
+ if (dev && publicDir !== outDir && await serveStatic(res, publicDir, pathname, req, secHeaders)) return;
75
77
  // Dynamic hydration bundles (dev, or when not prebuilt): /_glash/<routeId>.js
76
78
  if (pathname.startsWith('/_glash/')) {
77
79
  const id = pathname.slice('/_glash/'.length).replace(/\.js$/, '');
@@ -120,9 +122,21 @@ export async function createGlashServer({ root = process.cwd(), dev = false } =
120
122
  });
121
123
 
122
124
  const listen = (port = cfg.port || 3000, host = '0.0.0.0') =>
123
- new Promise((resolve) => server.listen(port, host, () => resolve({ port, host })));
124
-
125
- return { server, listen, cfg, routes, routesDir, outDir };
125
+ new Promise((resolve, reject) => {
126
+ const onError = (error) => {
127
+ server.off('listening', onListening);
128
+ reject(error);
129
+ };
130
+ const onListening = () => {
131
+ server.off('error', onError);
132
+ resolve({ port, host });
133
+ };
134
+ server.once('error', onError);
135
+ server.once('listening', onListening);
136
+ server.listen(port, host);
137
+ });
138
+
139
+ return { server, listen, cfg, routes, routesDir, outDir, publicDir };
126
140
  }
127
141
 
128
142
  async function handleApi(res, mod, req, ctx, secHeaders) {