glashjs 0.13.2 → 0.13.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/glash.mjs CHANGED
@@ -42,6 +42,20 @@ function firstPositional(args = rest) {
42
42
  return undefined;
43
43
  }
44
44
 
45
+ // A compact terminal rendering of the glashjs favicon — the metallic glash mark
46
+ // (a rounded square). Shown in the dev/serve banner so the brand favicon is
47
+ // visible right in the CLI. ANSI-colored on a TTY; plain blocks otherwise.
48
+ function glashMark() {
49
+ const tty = process.stdout.isTTY;
50
+ const hi = (s) => (tty ? `\x1b[97m${s}\x1b[0m` : s); // bright white highlight
51
+ const mid = (s) => (tty ? `\x1b[38;5;250m${s}\x1b[0m` : s); // light grey shading
52
+ return [
53
+ ' ' + hi('▟██████▙'),
54
+ ' ' + hi('██') + mid('████') + hi('██'),
55
+ ' ' + hi('▜██████▛'),
56
+ ];
57
+ }
58
+
45
59
  // LAN IPv4 addresses, so the dev server prints a Network URL you can open from
46
60
  // your phone or another device on the same network.
47
61
  function lanAddresses() {
@@ -62,7 +76,9 @@ async function serve(dev) {
62
76
  const port = await listenOnAvailablePort(listen, preferredPort);
63
77
  const pages = routes.filter((r) => !r.isApi).length;
64
78
  const apis = routes.filter((r) => r.isApi).length;
65
- console.log(`\nglashjs ${dev ? 'dev' : 'serve'} — "${cfg.name}"`);
79
+ console.log('');
80
+ for (const line of glashMark()) console.log(line);
81
+ console.log(` glashjs ${dev ? 'dev' : 'serve'} — "${cfg.name}"`);
66
82
  if (port !== preferredPort) console.log(` Port ${preferredPort} is in use; using ${port} instead.`);
67
83
  console.log(` ${pages} page route(s), ${apis} api route(s)`);
68
84
  routes.forEach((r) => console.log(` ${r.isApi ? 'api ' : 'page'} ${r.pattern}`));
@@ -157,7 +173,7 @@ async function main() {
157
173
  console.log('glashjs ' + VERSION);
158
174
  break;
159
175
  default:
160
- console.log(`glashjs — fast, offline-capable, hard-to-hack sites
176
+ console.log(`glashjs — fast, offline-capable, secure-by-default sites
161
177
 
162
178
  Usage: (run as "glashjs <cmd>"; "glash <cmd>" also works unless the glashdb deploy CLI owns that name)
163
179
  glashjs run dev Alias for glashjs dev (also: glash run dev)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glashjs",
3
- "version": "0.13.2",
3
+ "version": "0.13.4",
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": {
package/src/create.mjs CHANGED
@@ -4,9 +4,10 @@
4
4
 
5
5
  import { spawn } from 'node:child_process';
6
6
  import { randomBytes } from 'node:crypto';
7
- import { promises as fs } from 'node:fs';
7
+ import { promises as fs, readFileSync } from 'node:fs';
8
8
  import { createInterface } from 'node:readline/promises';
9
9
  import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
10
11
  import { generateTypedRoutes } from './typed-routes.mjs';
11
12
 
12
13
  const CSS_CHOICES = new Set(['tailwind', 'plain', 'none']);
@@ -475,12 +476,18 @@ function tailwindInput() {
475
476
  }
476
477
 
477
478
  function faviconSvg() {
478
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
479
- <rect width="64" height="64" rx="14" fill="#050505"/>
480
- <circle cx="32" cy="32" r="17" fill="#f97316"/>
481
- <circle cx="32" cy="32" r="7" fill="#050505"/>
479
+ // Use the bundled glash mark (the official metallic glash favicon) so a new
480
+ // app ships the real brand favicon, not a placeholder. Single source of truth:
481
+ // glashjs/templates/favicon.svg.
482
+ try {
483
+ return readFileSync(fileURLToPath(new URL('../templates/favicon.svg', import.meta.url)), 'utf8');
484
+ } catch {
485
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
486
+ <rect width="64" height="64" rx="14" fill="#0a0a0a"/>
487
+ <rect x="13" y="13" width="38" height="38" rx="11" fill="#d4d4d4"/>
482
488
  </svg>
483
489
  `;
490
+ }
484
491
  }
485
492
 
486
493
  function gitignore() {
@@ -0,0 +1,16 @@
1
+ // Minimal next/dynamic compatibility for migrated client components.
2
+ import { h } from 'preact';
3
+ import { lazy, Suspense } from 'preact/compat';
4
+
5
+ export default function dynamic(loader, options = {}) {
6
+ const Lazy = lazy(async () => {
7
+ const mod = await loader();
8
+ return { default: mod.default || mod };
9
+ });
10
+ const Loading = options.loading;
11
+
12
+ return function DynamicComponent(props) {
13
+ const fallback = Loading ? h(Loading, props) : null;
14
+ return h(Suspense, { fallback }, h(Lazy, props));
15
+ };
16
+ }
@@ -0,0 +1,23 @@
1
+ // Minimal next/headers compatibility for migrated API helpers.
2
+ // Glash handlers should prefer ctx.headers/cookies, but these no-op shims keep
3
+ // shared Next-era utilities importable during migration.
4
+
5
+ function emptyCookieStore() {
6
+ return {
7
+ get: () => undefined,
8
+ getAll: () => [],
9
+ has: () => false,
10
+ set: () => {},
11
+ delete: () => {},
12
+ clear: () => {},
13
+ toString: () => '',
14
+ };
15
+ }
16
+
17
+ export function cookies() {
18
+ return emptyCookieStore();
19
+ }
20
+
21
+ export function headers() {
22
+ return new Headers();
23
+ }
@@ -0,0 +1,50 @@
1
+ // Minimal Next navigation compatibility for migrated Glash routes.
2
+ // These browser-first shims keep common client components running without
3
+ // dragging the Next runtime into the Glash server bundle.
4
+
5
+ function hrefOf(href) {
6
+ if (typeof href === 'string') return href;
7
+ if (href && typeof href === 'object') {
8
+ const pathname = href.pathname || '';
9
+ const query = href.query ? `?${new URLSearchParams(href.query).toString()}` : '';
10
+ const hash = href.hash || '';
11
+ return `${pathname}${query}${hash}`;
12
+ }
13
+ return '/';
14
+ }
15
+
16
+ export function useRouter() {
17
+ return {
18
+ push: (href) => { if (typeof window !== 'undefined') window.location.assign(hrefOf(href)); },
19
+ replace: (href) => { if (typeof window !== 'undefined') window.location.replace(hrefOf(href)); },
20
+ refresh: () => { if (typeof window !== 'undefined') window.location.reload(); },
21
+ back: () => { if (typeof window !== 'undefined') window.history.back(); },
22
+ forward: () => { if (typeof window !== 'undefined') window.history.forward(); },
23
+ prefetch: () => Promise.resolve(),
24
+ };
25
+ }
26
+
27
+ export function usePathname() {
28
+ return typeof window === 'undefined' ? '/' : window.location.pathname;
29
+ }
30
+
31
+ export function useSearchParams() {
32
+ return new URLSearchParams(typeof window === 'undefined' ? '' : window.location.search);
33
+ }
34
+
35
+ export function useParams() {
36
+ return {};
37
+ }
38
+
39
+ export function redirect(href) {
40
+ if (typeof window !== 'undefined') window.location.replace(hrefOf(href));
41
+ const error = new Error(`NEXT_REDIRECT:${hrefOf(href)}`);
42
+ error.digest = `NEXT_REDIRECT;replace;${hrefOf(href)};307;`;
43
+ throw error;
44
+ }
45
+
46
+ export function notFound() {
47
+ const error = new Error('NEXT_NOT_FOUND');
48
+ error.status = 404;
49
+ throw error;
50
+ }
@@ -16,7 +16,7 @@
16
16
  import { promises as fs, existsSync } from 'node:fs';
17
17
  import path from 'node:path';
18
18
  import { createHash } from 'node:crypto';
19
- import { pathToFileURL } from 'node:url';
19
+ import { fileURLToPath, pathToFileURL } from 'node:url';
20
20
 
21
21
  const MISSING = 'JSX/TSX routes need the optional peers: npm i esbuild preact preact-render-to-string';
22
22
 
@@ -38,13 +38,20 @@ async function preactRuntime() {
38
38
  return { h: _h, renderToString: _renderToString };
39
39
  }
40
40
 
41
- // Make migrated React/Next components compile + run under Preact.
41
+ const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
42
+
43
+ // Make migrated React/Next components compile + run under Preact/Glash.
42
44
  const REACT_ALIAS = {
43
45
  react: 'preact/compat',
44
46
  'react-dom': 'preact/compat',
45
47
  'react-dom/client': 'preact/compat',
46
48
  'react/jsx-runtime': 'preact/jsx-runtime',
47
49
  'react/jsx-dev-runtime': 'preact/jsx-runtime',
50
+ 'next/link': path.join(pkgRoot, 'components/link.mjs'),
51
+ 'next/image': path.join(pkgRoot, 'components/image.mjs'),
52
+ 'next/navigation': path.join(pkgRoot, 'next/navigation.mjs'),
53
+ 'next/dynamic': path.join(pkgRoot, 'next/dynamic.mjs'),
54
+ 'next/headers': path.join(pkgRoot, 'next/headers.mjs'),
48
55
  };
49
56
 
50
57
  export function isComponentRoute(file) {