glashjs 0.14.1 → 0.14.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/README.md +2 -1
- package/bin/glash.mjs +6 -0
- package/package.json +1 -1
- package/src/config.mjs +8 -0
- package/src/create.mjs +2 -1
- package/src/server/html.mjs +12 -2
- package/src/server/server.mjs +8 -2
package/README.md
CHANGED
|
@@ -179,7 +179,8 @@ export default defineConfig({
|
|
|
179
179
|
outDir: '.glash/out',
|
|
180
180
|
stylesheets: ['/app.css'],
|
|
181
181
|
offline: true,
|
|
182
|
-
|
|
182
|
+
favicon: 'node_modules/glashjs/templates/glash_favicon.svg',
|
|
183
|
+
animatedFavicon: false,
|
|
183
184
|
dataPrefixes: ['/api/', '/auth/', '/rest/', '/live', '/stream'],
|
|
184
185
|
});
|
|
185
186
|
```
|
package/bin/glash.mjs
CHANGED
|
@@ -56,6 +56,11 @@ function glashMark() {
|
|
|
56
56
|
];
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function faviconLabel(cfg) {
|
|
60
|
+
const source = String(cfg.favicon || 'glash_favicon.svg');
|
|
61
|
+
return source.split(/[\\/]/).pop() || 'glash_favicon.svg';
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
// LAN IPv4 addresses, so the dev server prints a Network URL you can open from
|
|
60
65
|
// your phone or another device on the same network.
|
|
61
66
|
function lanAddresses() {
|
|
@@ -79,6 +84,7 @@ async function serve(dev) {
|
|
|
79
84
|
console.log('');
|
|
80
85
|
for (const line of glashMark()) console.log(line);
|
|
81
86
|
console.log(` glashjs ${dev ? 'dev' : 'serve'} — "${cfg.name}"`);
|
|
87
|
+
console.log(` favicon ${faviconLabel(cfg)} -> /favicon.svg`);
|
|
82
88
|
if (port !== preferredPort) console.log(` Port ${preferredPort} is in use; using ${port} instead.`);
|
|
83
89
|
console.log(` ${pages} page route(s), ${apis} api route(s)`);
|
|
84
90
|
routes.forEach((r) => console.log(` ${r.isApi ? 'api ' : 'page'} ${r.pattern}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glashjs",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.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": {
|
package/src/config.mjs
CHANGED
|
@@ -25,6 +25,14 @@ export const DEFAULT_CONFIG = {
|
|
|
25
25
|
outDir: '.glash/out',
|
|
26
26
|
themeColor: '#0b0d12',
|
|
27
27
|
offline: true,
|
|
28
|
+
// Built-in IP auto-translation. true = detect the visitor's country (from the
|
|
29
|
+
// edge geo header, falling back to a public IP service) and, if their native
|
|
30
|
+
// language differs from the page, offer a one-tap in-place translation. The
|
|
31
|
+
// framework serves the runtime (/_glash/i18n.js) and a geo endpoint
|
|
32
|
+
// (/api/_glash/geo) automatically — no route or layout wiring per app.
|
|
33
|
+
// true | false | { auto?: boolean, geoEndpoint?: string }
|
|
34
|
+
// auto: translate immediately instead of prompting.
|
|
35
|
+
i18n: true,
|
|
28
36
|
// Requests under these prefixes are treated as live/updated data: network-first
|
|
29
37
|
// in the Service Worker, so offline mode degrades exactly there (no stale live data).
|
|
30
38
|
dataPrefixes: ['/api/', '/rest/', '/auth/', '/realtime', '/live', '/stream'],
|
package/src/create.mjs
CHANGED
|
@@ -136,7 +136,8 @@ export default defineConfig({
|
|
|
136
136
|
publicDir: 'public',
|
|
137
137
|
outDir: '.glash/out',
|
|
138
138
|
offline: true,
|
|
139
|
-
|
|
139
|
+
favicon: 'node_modules/glashjs/templates/glash_favicon.svg',
|
|
140
|
+
animatedFavicon: false,
|
|
140
141
|
stylesheets: ${hasCss ? "['/app.css']" : '[]'},
|
|
141
142
|
dataPrefixes: ['/api/', '/auth/', '/rest/', '/live', '/stream'],
|
|
142
143
|
security: {
|
package/src/server/html.mjs
CHANGED
|
@@ -49,7 +49,7 @@ ${headHtml}
|
|
|
49
49
|
`;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function shellTail({ offline = true, animatedFavicon = false, dev = false, nonce = '' }) {
|
|
52
|
+
function shellTail({ offline = true, animatedFavicon = false, dev = false, nonce = '', i18n = false }) {
|
|
53
53
|
const n = nonce ? ` nonce="${escapeHtml(nonce)}"` : '';
|
|
54
54
|
const fav = animatedFavicon
|
|
55
55
|
? `<script type="module"${n}>try{const m=await import("/glash-favicon.mjs");m.startGlashFavicon&&m.startGlashFavicon();}catch{}</script>`
|
|
@@ -57,6 +57,16 @@ function shellTail({ offline = true, animatedFavicon = false, dev = false, nonce
|
|
|
57
57
|
const off = offline
|
|
58
58
|
? `<script type="module"${n}>try{const m=await import("/glash-offline.mjs");m.registerGlashOffline&&m.registerGlashOffline();}catch{}</script>`
|
|
59
59
|
: '';
|
|
60
|
+
// Built-in IP auto-translation: the framework serves /_glash/i18n.js (the
|
|
61
|
+
// dependency-free runtime). It detects the visitor's country, maps it to their
|
|
62
|
+
// native language, and offers a one-tap in-place translation. CSP-safe: a
|
|
63
|
+
// nonce'd module import of a same-origin URL, no inline logic.
|
|
64
|
+
const i18nOpts = i18n && typeof i18n === 'object'
|
|
65
|
+
? { auto: !!i18n.auto, ...(i18n.geoEndpoint ? { geoEndpoint: i18n.geoEndpoint } : {}) }
|
|
66
|
+
: {};
|
|
67
|
+
const intl = i18n
|
|
68
|
+
? `<script type="module"${n}>try{const m=await import("/_glash/i18n.js");m.glashAutoTranslate&&m.glashAutoTranslate(${JSON.stringify(i18nOpts)});}catch{}</script>`
|
|
69
|
+
: '';
|
|
60
70
|
// Dev HMR: a nonce'd inline script (CSP-safe). On a change event it does an
|
|
61
71
|
// in-place soft refresh (no full reload, keeps scroll/focus/inputs), falling
|
|
62
72
|
// back to a full reload if the nav runtime hasn't loaded yet.
|
|
@@ -65,7 +75,7 @@ function shellTail({ offline = true, animatedFavicon = false, dev = false, nonce
|
|
|
65
75
|
: '';
|
|
66
76
|
// Client-side navigation runtime (external 'self' module, CSP-safe).
|
|
67
77
|
const nav = '<script type="module" src="/_glash/nav.js"></script>';
|
|
68
|
-
return `\n${fav}${off}${hmr}${nav}\n</body>\n</html>`;
|
|
78
|
+
return `\n${fav}${off}${intl}${hmr}${nav}\n</body>\n</html>`;
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
/**
|
package/src/server/server.mjs
CHANGED
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
// service worker, favicons) with Brotli negotiation.
|
|
7
7
|
// Every response carries the secure-by-default headers.
|
|
8
8
|
import http from 'node:http';
|
|
9
|
-
import { promises as fs, existsSync, statSync, watch, createReadStream } from 'node:fs';
|
|
9
|
+
import { promises as fs, existsSync, statSync, watch, createReadStream, readFileSync } from 'node:fs';
|
|
10
10
|
import { Transform } from 'node:stream';
|
|
11
11
|
import { randomBytes } from 'node:crypto';
|
|
12
12
|
import path from 'node:path';
|
|
13
|
-
import { pathToFileURL } from 'node:url';
|
|
13
|
+
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
14
|
+
import { geoRouteHandler } from '../i18n.mjs';
|
|
14
15
|
import { discoverRoutes, matchRoute, findMiddleware } from './router.mjs';
|
|
15
16
|
import { renderDocument, documentParts, renderMeta, escapeHtml } from './html.mjs';
|
|
16
17
|
import { NAV_CLIENT } from './nav-client.mjs';
|
|
@@ -28,6 +29,11 @@ const MIME = {
|
|
|
28
29
|
};
|
|
29
30
|
const mime = (file) => MIME[path.extname(file).toLowerCase()] || 'application/octet-stream';
|
|
30
31
|
|
|
32
|
+
// The built-in i18n runtime served at /_glash/i18n.js. i18n.mjs is dependency-free
|
|
33
|
+
// and valid browser ESM (its server-only exports are harmless in the browser), so
|
|
34
|
+
// we serve it raw — single source of truth, works identically in dev and prod.
|
|
35
|
+
const I18N_CLIENT = readFileSync(fileURLToPath(new URL('../i18n.mjs', import.meta.url)), 'utf8');
|
|
36
|
+
|
|
31
37
|
/** Build the glashjs server. Returns { server, listen, cfg }. */
|
|
32
38
|
export async function createGlashServer({ root = process.cwd(), dev = false } = {}) {
|
|
33
39
|
loadEnvFiles(root);
|