kreftjs 0.0.6-beta → 0.0.9-beta
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 +5 -31
- package/bin/artisan-more.js +2 -0
- package/bin/artisan.js +51 -49
- package/bin/backend/config.js +2 -2
- package/bin/backend/http.js +5 -5
- package/bin/backend/index.js +2 -2
- package/bin/backend/mcp.js +3 -5
- package/bin/backend/migrator.js +1 -3
- package/bin/backend/query.js +1 -1
- package/bin/backend/queue.js +8 -9
- package/bin/backend/redis.js +1 -1
- package/bin/backend/typescript.js +1 -1
- package/bin/build.js +23 -19
- package/bin/db-api.js +2 -2
- package/bin/db.js +4 -4
- package/bin/deps.js +1 -1
- package/bin/electron.js +3 -5
- package/bin/kreftjs-config.js +1 -6
- package/bin/kreftjs.js +34 -35
- package/bin/layout.js +1 -1
- package/bin/no-auth.js +21 -19
- package/bin/prerender.js +1 -1
- package/bin/ssr.js +3 -3
- package/bin/update.js +10 -10
- package/docs/{Docs.kreftjs → Docs.kreft} +1 -1
- package/docs/README.md +90 -102
- package/editors/vscode/README.md +3 -3
- package/editors/vscode/package.json +3 -4
- package/editors/vscode/snippets/kreftjs.json +2 -2
- package/kreftjs.d.ts +15 -27
- package/kreftjs.js +69 -79
- package/package.json +4 -4
- package/templates/app/README.md +11 -11
- package/templates/app/_env.example +4 -2
- package/templates/app/_vscode/settings.json +3 -2
- package/templates/app/backend/app/Http/Controllers/Admin/DashboardController.js +2 -2
- package/templates/app/backend/app/Http/Controllers/Admin/UserController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/ApiTokenController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/AuthenticatedSessionController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/ConfirmablePasswordController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/EmailVerificationController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/NewPasswordController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/PasswordResetLinkController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/RegisteredUserController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/SessionController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/TwoFactorController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/ProfileController.js +1 -1
- package/templates/app/backend/app/Http/Middleware/RequirePassword.js +1 -1
- package/templates/app/frontend/{App.kreftjs → App.kreft} +1 -1
- package/templates/app/frontend/components/profile/{ApiTokens.kreftjs → ApiTokens.kreft} +1 -1
- package/templates/app/frontend/components/profile/{Sessions.kreftjs → Sessions.kreft} +1 -1
- package/templates/app/frontend/components/profile/{TwoFactor.kreftjs → TwoFactor.kreft} +1 -1
- package/templates/app/frontend/errors/{ErrorMonitor.kreftjs → ErrorMonitor.kreft} +6 -4
- package/templates/app/frontend/index.html +8 -8
- package/templates/app/frontend/kreftjs.d.ts +15 -27
- package/templates/app/frontend/kreftjs.js +69 -79
- package/templates/app/frontend/layouts/{app.kreftjs → app.kreft} +7 -1
- package/templates/app/frontend/layouts/{docs.kreftjs → docs.kreft} +1 -1
- package/templates/app/frontend/pages/{Dashboard.kreftjs → Dashboard.kreft} +4 -2
- package/templates/app/frontend/pages/{ErrorMonitor.kreftjs → ErrorMonitor.kreft} +2 -2
- package/templates/app/frontend/pages/{Home.kreftjs → Home.kreft} +10 -9
- package/templates/app/frontend/pages/{Profile.kreftjs → Profile.kreft} +3 -3
- package/templates/app/frontend/routes/web.kreft +34 -0
- package/templates/app/frontend/tests/components/{Counter.kreftjs → Counter.kreft} +1 -1
- package/templates/app/frontend/tests/pages/About.kreft +12 -0
- package/templates/app/frontend/tests/pages/{Test.kreftjs → Test.kreft} +1 -1
- package/templates/errors/{401.kreftjs → 401.kreft} +2 -2
- package/templates/errors/{402.kreftjs → 402.kreft} +2 -2
- package/templates/errors/{403.kreftjs → 403.kreft} +2 -2
- package/templates/errors/{404.kreftjs → 404.kreft} +2 -2
- package/templates/errors/{419.kreftjs → 419.kreft} +2 -2
- package/templates/errors/{429.kreftjs → 429.kreft} +2 -2
- package/templates/errors/{500.kreftjs → 500.kreft} +2 -2
- package/templates/errors/{503.kreftjs → 503.kreft} +2 -2
- package/templates/errors/{ErrorMonitor.kreftjs → ErrorMonitor.kreft} +17 -14
- package/templates/errors/{error.kreftjs → error.kreft} +2 -2
- package/templates/errors/{minimal.kreftjs → minimal.kreft} +1 -1
- package/tools/dev-client.js +2 -2
- package/tools/vite-plugin-kreftjs.d.ts +3 -5
- package/tools/vite-plugin-kreftjs.js +12 -14
- package/templates/app/frontend/routes/web.kreftjs +0 -34
- package/templates/app/frontend/tests/pages/About.kreftjs +0 -12
- /package/templates/app/frontend/components/{Field.kreftjs → Field.kreft} +0 -0
- /package/templates/app/frontend/components/{StatCard.kreftjs → StatCard.kreft} +0 -0
- /package/templates/app/frontend/layouts/{guest.kreftjs → guest.kreft} +0 -0
- /package/templates/app/frontend/pages/{Users.kreftjs → Users.kreft} +0 -0
- /package/templates/app/frontend/pages/auth/{ConfirmPassword.kreftjs → ConfirmPassword.kreft} +0 -0
- /package/templates/app/frontend/pages/auth/{ForgotPassword.kreftjs → ForgotPassword.kreft} +0 -0
- /package/templates/app/frontend/pages/auth/{Login.kreftjs → Login.kreft} +0 -0
- /package/templates/app/frontend/pages/auth/{Register.kreftjs → Register.kreft} +0 -0
- /package/templates/app/frontend/pages/auth/{ResetPassword.kreftjs → ResetPassword.kreft} +0 -0
- /package/templates/app/frontend/pages/auth/{VerifyEmail.kreftjs → VerifyEmail.kreft} +0 -0
package/bin/build.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* kreft build --no-htaccess do not write dist/.htaccess (the Apache / Hostinger page-address rules)
|
|
12
12
|
*
|
|
13
13
|
* What it does:
|
|
14
|
-
* 1. Compiles every .
|
|
14
|
+
* 1. Compiles every .kreft file ahead of time into app.js (no .kreft files are downloaded in production).
|
|
15
15
|
* 2. Turns every expression in the views into a real function, so the app runs without
|
|
16
16
|
* eval / new Function — it works under a strict Content-Security-Policy.
|
|
17
17
|
* 3. Collects all <style> blocks (scoped ones already scoped) into app.css.
|
|
@@ -28,10 +28,10 @@ const { expandBlocks, prepareTemplate, splitKreft, splitInterp, exactBrace, stri
|
|
|
28
28
|
scopeCss, hashString, propType, simplePathRE, fnExpRE, forAliasRE, stripTypeScript } = Kreftjs.__internal;
|
|
29
29
|
|
|
30
30
|
const deps = require('./deps.js');
|
|
31
|
-
// component files: .
|
|
32
|
-
const isComp = (f) => /\.(?:kreftjs|kreft
|
|
31
|
+
// component files: .kreft, or .kreftjs in a project from the 0.0.x betas
|
|
32
|
+
const isComp = (f) => /\.(?:kreftjs|kreft)$/i.test(f);
|
|
33
33
|
const SERVER_DIRS = new Set();
|
|
34
|
-
const SKIP = new Set(['node_modules', '.git', '.vscode', '.idea', 'package.json', 'package-lock.json', '.DS_Store']);
|
|
34
|
+
const SKIP = new Set(['node_modules', 'dist', 'dist-electron', '.git', '.vscode', '.idea', 'package.json', 'package-lock.json', '.DS_Store']);
|
|
35
35
|
|
|
36
36
|
module.exports = function build(args) {
|
|
37
37
|
let folder = null, out = null, base = null, prerender = null, urlsFile = null, htaccess = true;
|
|
@@ -59,9 +59,9 @@ module.exports = function build(args) {
|
|
|
59
59
|
// server code never goes into the built site: server/, backend app/ and routes/
|
|
60
60
|
if (!lay.split) for (const dir of [root, project]) ['server', 'app', 'routes'].forEach((d) => SERVER_DIRS.add(path.join(dir, d)));
|
|
61
61
|
SERVER_DIRS.add(lay.backend === lay.project ? path.join(project, 'server') : lay.backend);
|
|
62
|
-
const bare = new Set(), cssLinks = []; // npm packages used by the .
|
|
62
|
+
const bare = new Set(), cssLinks = []; // npm packages used by the .kreft files, and imported CSS files
|
|
63
63
|
|
|
64
|
-
// ---- 1. find and compile every .
|
|
64
|
+
// ---- 1. find and compile every .kreft file
|
|
65
65
|
const kreftFiles = walk(root, dist).filter((f) => isComp(f));
|
|
66
66
|
const fns = new Map(); // precompiled expression functions: key → source
|
|
67
67
|
const components = [];
|
|
@@ -70,7 +70,7 @@ module.exports = function build(args) {
|
|
|
70
70
|
|
|
71
71
|
addRouterTemplates(fns); // the router's own <kreft-link> and <kreft-view> templates
|
|
72
72
|
|
|
73
|
-
// .
|
|
73
|
+
// .kreft files in --include folders too: they land in the output as <folder>/…, so that is their path there
|
|
74
74
|
const jobs = kreftFiles.map((file) => [file, toUrlPath(path.relative(root, file)), root]);
|
|
75
75
|
for (const dir of include) {
|
|
76
76
|
const from = path.resolve(dir);
|
|
@@ -84,7 +84,7 @@ module.exports = function build(args) {
|
|
|
84
84
|
const templateBar = createProgressBar(jobs.length, { label: 'Compiling templates' });
|
|
85
85
|
for (const [file, rel, base] of jobs) {
|
|
86
86
|
templateBar.tick(rel);
|
|
87
|
-
// routes/web.
|
|
87
|
+
// routes/web.kreft and other route files: plain JavaScript with Route in scope, kept as a function
|
|
88
88
|
if (/^routes\//.test(rel)) {
|
|
89
89
|
const src = stripTypeScript(fs.readFileSync(file, 'utf8'));
|
|
90
90
|
try { new Function('Route', src); } catch (e) { errors.push(` ${rel}: ${e.message}`); continue; }
|
|
@@ -115,7 +115,7 @@ module.exports = function build(args) {
|
|
|
115
115
|
const langInject = Object.keys(langData).length ? `Kreftjs.translations(${JSON.stringify(langData)});\n` : '';
|
|
116
116
|
|
|
117
117
|
const precompiled = [...fns].map(([key, src]) => `F[${JSON.stringify(key)}] = ${src};`).join('\n');
|
|
118
|
-
const appJs = `/* Built by kreft build — do not edit. Rebuild from your .
|
|
118
|
+
const appJs = `/* Built by kreft build — do not edit. Rebuild from your .kreft files instead. */
|
|
119
119
|
${env.envScript(env.publicEnv())}
|
|
120
120
|
(function (Kreftjs) {
|
|
121
121
|
if (!Kreftjs) throw new Error('Load kreftjs.js before app.js');
|
|
@@ -168,7 +168,7 @@ ${indent(components.join('\n\n'))}
|
|
|
168
168
|
|
|
169
169
|
// ---- 3. index.html: kreftjs.js + app.js + app.css, inline scripts moved to main.js
|
|
170
170
|
let html = fs.readFileSync(path.join(root, 'index.html'), 'utf8');
|
|
171
|
-
// an included folder that sits next to the app ('../docs/Docs.
|
|
171
|
+
// an included folder that sits next to the app ('../docs/Docs.kreft') is inside the output: 'docs/Docs.kreft'
|
|
172
172
|
for (const dir of include) {
|
|
173
173
|
const up = toUrlPath(path.relative(root, path.resolve(dir)));
|
|
174
174
|
if (up.startsWith('../')) html = html.split(`'${up}/`).join(`'${path.basename(up)}/`).split(`"${up}/`).join(`"${path.basename(up)}/`);
|
|
@@ -177,7 +177,7 @@ ${indent(components.join('\n\n'))}
|
|
|
177
177
|
let sawKreft = false, placedMain = false;
|
|
178
178
|
html = html.replace(/<script\b([^>]*)>([\s\S]*?)<\/script>/gi, (tag, attrs, body) => {
|
|
179
179
|
const src = (attrs.match(/\bsrc\s*=\s*["']([^"']+)["']/i) || [])[1];
|
|
180
|
-
if (src && /(^|\/)(?:kreftjs|kreft
|
|
180
|
+
if (src && /(^|\/)(?:kreftjs|kreft)(\.min)?\.js$/i.test(src)) {
|
|
181
181
|
sawKreft = true;
|
|
182
182
|
return '<script src="kreftjs.js"></script>\n <script src="app.js"></script>';
|
|
183
183
|
}
|
|
@@ -252,8 +252,10 @@ ${indent(components.join('\n\n'))}
|
|
|
252
252
|
report();
|
|
253
253
|
};
|
|
254
254
|
|
|
255
|
-
// Apache (Hostinger, cPanel …) answers /docs with a 404, because only index.html exists on disk. These rules send every address
|
|
256
|
-
// that is not a real file or folder to index.html, so reloading a page works.
|
|
255
|
+
// Apache (Hostinger, cPanel …) answers /docs with a 404, because only index.html exists on disk. These rules send every page address
|
|
256
|
+
// (no file extension, not under /api/) that is not a real file or folder to index.html, so reloading a page works. A missing file such as
|
|
257
|
+
// app.js, or an /api/ address the host cannot answer, stays a real 404: sending it index.html would hand the page's HTML to code
|
|
258
|
+
// that expects data (a list, JSON). An .htaccess you keep in public/ is left alone;
|
|
257
259
|
// a relative base (./, as the desktop build uses) has no server and gets no file.
|
|
258
260
|
function writeHtaccess(dist, html) {
|
|
259
261
|
const m = /<base\b[^>]*\bhref="([^"]*)"/i.exec(html);
|
|
@@ -266,6 +268,8 @@ function writeHtaccess(dist, html) {
|
|
|
266
268
|
RewriteRule ^index\\.html$ - [L]
|
|
267
269
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
268
270
|
RewriteCond %{REQUEST_FILENAME} !-d
|
|
271
|
+
RewriteCond %{REQUEST_URI} !^${base}api/
|
|
272
|
+
RewriteCond %{REQUEST_URI} !\\.[A-Za-z0-9]+$
|
|
269
273
|
RewriteRule . ${base}index.html [L]
|
|
270
274
|
</IfModule>
|
|
271
275
|
`);
|
|
@@ -278,12 +282,12 @@ function tsToJs(file, project) {
|
|
|
278
282
|
return code.replace(/((?:from|import)\s*\(?\s*['"])(\.{1,2}\/[^'"]+?)\.ts(['"])/g, '$1$2.js$3');
|
|
279
283
|
}
|
|
280
284
|
|
|
281
|
-
/* ------------------------------------------------------------------ one .
|
|
285
|
+
/* ------------------------------------------------------------------ one .kreft file → JS */
|
|
282
286
|
function compileFile(file, rel, root) {
|
|
283
287
|
const sp = splitKreft(fs.readFileSync(file, 'utf8'), rel);
|
|
284
288
|
const name = sp.name;
|
|
285
289
|
|
|
286
|
-
// the script must parse; report the .
|
|
290
|
+
// the script must parse; report the .kreft line if it doesn't
|
|
287
291
|
const run = `function ($scope, $h) { with ($scope) {\n${sp.code}\n} }`;
|
|
288
292
|
try { new Function('return ' + run); } catch (e) { throw new Error(`script error: ${e.message}`); }
|
|
289
293
|
|
|
@@ -297,13 +301,13 @@ function compileFile(file, rel, root) {
|
|
|
297
301
|
return `${JSON.stringify(p)}: { ${type ? `type: ${type.name}, ` : ''}default: function () { return (${expr}); } }`;
|
|
298
302
|
});
|
|
299
303
|
|
|
300
|
-
// imports: other .
|
|
304
|
+
// imports: other .kreft files become component references; .js modules load with import()
|
|
301
305
|
const comps = [], values = [], bare = [], cssImports = [];
|
|
302
306
|
for (const stmt of sp.imports) {
|
|
303
307
|
const m = stmt.match(/^import\s+(?:([\s\S]+?)\s+from\s+)?(['"])(.+?)\2/);
|
|
304
308
|
if (!m) throw new Error(`can't read import: ${stmt}`);
|
|
305
309
|
const [, what, , spec] = m;
|
|
306
|
-
if (/^(?:kreftjs|kreft
|
|
310
|
+
if (/^(?:kreftjs|kreft)(\.js)?$/.test(spec)) continue;
|
|
307
311
|
let target = /^\.{1,2}\//.test(spec) ? toUrlPath(path.relative(root, path.resolve(path.dirname(file), spec))) : spec;
|
|
308
312
|
if (/^\.{1,2}\//.test(spec) && /\.ts$/i.test(spec)) target = target.replace(/\.ts$/i, '.js'); // TypeScript files are built into .js
|
|
309
313
|
if (isComp(spec)) {
|
|
@@ -351,7 +355,7 @@ function compileFile(file, rel, root) {
|
|
|
351
355
|
// `import { a, b as c } from './x.js'` → loaded with import() when the component is first used
|
|
352
356
|
function loadValuesSource(values) {
|
|
353
357
|
const lines = values.map(({ what, target, local }, i) => {
|
|
354
|
-
// files in the app resolve from <base href>, like .
|
|
358
|
+
// files in the app resolve from <base href>, like .kreft paths; URLs and package names stay as written
|
|
355
359
|
const url = local ? `new URL(${JSON.stringify(target)}, document.baseURI).href` : JSON.stringify(target);
|
|
356
360
|
const picks = [];
|
|
357
361
|
if (what) what.split(/,(?![^{]*\})/).map((s) => s.trim()).forEach((part) => {
|
|
@@ -540,7 +544,7 @@ function walk(dir, exclude) {
|
|
|
540
544
|
if (SKIP.has(entry.name)) continue;
|
|
541
545
|
if (entry.name === 'storage' && (entry.isSymbolicLink() || entry.isDirectory())) continue;
|
|
542
546
|
// the database, its settings and its migrations stay on the server, never in the built site
|
|
543
|
-
if (/\.(db|sqlite3?)(-wal|-shm)?$/i.test(entry.name) || entry.name === 'kreftjs.config.json' || entry.name === 'kreft.config.json' ||
|
|
547
|
+
if (/\.(db|sqlite3?)(-wal|-shm)?$/i.test(entry.name) || entry.name === 'kreftjs.config.json' || entry.name === 'kreft.config.json' || (entry.isDirectory() && entry.name === 'database')) continue;
|
|
544
548
|
const full = path.join(dir, entry.name);
|
|
545
549
|
if (exclude && full === exclude) continue;
|
|
546
550
|
if (SERVER_DIRS.has(full)) continue; // server/ code runs on the server only
|
package/bin/db-api.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* A REST API made from the tables of a database (used by `kreft dev --db` and `kreft serve --db`).
|
|
3
3
|
*
|
|
4
4
|
* GET /api the tables you can use
|
|
5
|
-
* GET /api/posts rows ?userId=3 ?title_like=
|
|
5
|
+
* GET /api/posts rows ?userId=3 ?title_like=news ?likes_gte=5 ?q=text
|
|
6
6
|
* ?_sort=-created_at,title ?_limit=20&_page=2 (header X-Total-Count)
|
|
7
7
|
* GET /api/posts/1 one row
|
|
8
8
|
* POST /api/posts create (201, the new row)
|
|
@@ -28,7 +28,7 @@ function createDbApi(db, { tables: allow = null, production = false, prefix = '/
|
|
|
28
28
|
const all = await db.tables();
|
|
29
29
|
const out = new Map();
|
|
30
30
|
for (const t of all) {
|
|
31
|
-
if (t.name === '
|
|
31
|
+
if (t.name === 'kreft_migrations') continue;
|
|
32
32
|
let rules = allow ? allow[t.name] : true;
|
|
33
33
|
if (rules == null || rules === false) continue;
|
|
34
34
|
if (rules === true) rules = {};
|
package/bin/db.js
CHANGED
|
@@ -250,14 +250,14 @@ function splitSql(sql) {
|
|
|
250
250
|
|
|
251
251
|
/* ---------------------------------------------------------------- migrations
|
|
252
252
|
* database/migrations/001_create_posts.sql, 002_add_likes.sql … run once each, in name order,
|
|
253
|
-
* remembered in the table
|
|
253
|
+
* remembered in the table kreft_migrations .
|
|
254
254
|
* database/seed.sql fills in starting data.
|
|
255
255
|
*/
|
|
256
256
|
async function migrate(db, dir, log = () => {}) {
|
|
257
257
|
if (!fs.existsSync(dir)) return [];
|
|
258
258
|
const idType = db.kind === 'postgres' ? 'TEXT' : 'VARCHAR(255)';
|
|
259
|
-
await db.exec(`CREATE TABLE IF NOT EXISTS
|
|
260
|
-
const done = new Set((await db.query('SELECT name FROM
|
|
259
|
+
await db.exec(`CREATE TABLE IF NOT EXISTS kreft_migrations (name ${idType} PRIMARY KEY, ran_at ${idType} NOT NULL)`);
|
|
260
|
+
const done = new Set((await db.query('SELECT name FROM kreft_migrations')).map((r) => r.name));
|
|
261
261
|
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.sql')).sort();
|
|
262
262
|
const ran = [];
|
|
263
263
|
for (const f of files) {
|
|
@@ -265,7 +265,7 @@ async function migrate(db, dir, log = () => {}) {
|
|
|
265
265
|
const sql = fs.readFileSync(path.join(dir, f), 'utf8');
|
|
266
266
|
try { await db.exec(sql); }
|
|
267
267
|
catch (e) { throw new Error(`Migration ${f} failed: ${e.message}`); }
|
|
268
|
-
await db.run('INSERT INTO
|
|
268
|
+
await db.run('INSERT INTO kreft_migrations (name, ran_at) VALUES (?, ?)', [f, new Date().toISOString()]);
|
|
269
269
|
log(f);
|
|
270
270
|
ran.push(f);
|
|
271
271
|
}
|
package/bin/deps.js
CHANGED
package/bin/electron.js
CHANGED
|
@@ -38,7 +38,7 @@ function getDevToolsConfig() {
|
|
|
38
38
|
(process.argv.includes('--devtools') ? 'right' : null) ||
|
|
39
39
|
(process.argv.includes('--no-devtools') ? 'false' : null) ||
|
|
40
40
|
process.env.ELECTRON_DEVTOOLS ||
|
|
41
|
-
|
|
41
|
+
process.env.KREFT_ELECTRON_DEVTOOLS ||
|
|
42
42
|
'';
|
|
43
43
|
const val = String(raw).trim().toLowerCase();
|
|
44
44
|
if (!val) return { show: false, mode: 'right' };
|
|
@@ -225,7 +225,7 @@ app.on('window-all-closed', () => {
|
|
|
225
225
|
|
|
226
226
|
preload: () => `const { contextBridge, ipcRenderer } = require('electron');
|
|
227
227
|
|
|
228
|
-
// Expose safe desktop bridge onto window.kreftDesktop
|
|
228
|
+
// Expose safe desktop bridge onto window.kreftDesktop
|
|
229
229
|
const desktop = {
|
|
230
230
|
isElectron: true,
|
|
231
231
|
getInfo: () => ipcRenderer.invoke('kreft:app-info'),
|
|
@@ -240,10 +240,8 @@ const desktop = {
|
|
|
240
240
|
showSaveDialog: (options) => ipcRenderer.invoke('kreft:dialog:save', options),
|
|
241
241
|
};
|
|
242
242
|
contextBridge.exposeInMainWorld('kreftDesktop', desktop);
|
|
243
|
-
contextBridge.exposeInMainWorld('somDesktop', desktop);
|
|
244
243
|
|
|
245
244
|
contextBridge.exposeInMainWorld('__KREFT_ELECTRON__', true);
|
|
246
|
-
contextBridge.exposeInMainWorld('__SOM_ELECTRON__', true);
|
|
247
245
|
`,
|
|
248
246
|
};
|
|
249
247
|
|
|
@@ -388,7 +386,7 @@ function startDev(args, port) {
|
|
|
388
386
|
const bin = resolveElectronBinary(root);
|
|
389
387
|
|
|
390
388
|
// Extract devtools settings from CLI args or environment
|
|
391
|
-
let devToolsEnv = process.env.ELECTRON_DEVTOOLS ||
|
|
389
|
+
let devToolsEnv = process.env.ELECTRON_DEVTOOLS || process.env.KREFT_ELECTRON_DEVTOOLS || '';
|
|
392
390
|
const devToolsArg = args.find((a) => a === '--devtools' || a.startsWith('--devtools=') || a === '--no-devtools');
|
|
393
391
|
if (devToolsArg) {
|
|
394
392
|
if (devToolsArg === '--devtools') devToolsEnv = 'right';
|
package/bin/kreftjs-config.js
CHANGED
|
@@ -14,17 +14,12 @@ function readConfig(start) {
|
|
|
14
14
|
path.join(dir, 'config', 'kreftjs.json'),
|
|
15
15
|
path.join(dir, 'Config', 'kreftjs.config.json'),
|
|
16
16
|
path.join(dir, 'Config', 'kreftjs.json'),
|
|
17
|
-
// the earlier
|
|
17
|
+
// the earlier name: kreft.config.json
|
|
18
18
|
path.join(dir, 'kreft.config.json'),
|
|
19
19
|
path.join(dir, 'config', 'kreft.config.json'),
|
|
20
20
|
path.join(dir, 'config', 'kreft.json'),
|
|
21
21
|
path.join(dir, 'Config', 'kreft.config.json'),
|
|
22
22
|
path.join(dir, 'Config', 'kreft.json'),
|
|
23
|
-
path.join(dir, 'som.config.json'),
|
|
24
|
-
path.join(dir, 'config', 'som.config.json'),
|
|
25
|
-
path.join(dir, 'config', 'som.json'),
|
|
26
|
-
path.join(dir, 'Config', 'som.config.json'),
|
|
27
|
-
path.join(dir, 'Config', 'som.json'),
|
|
28
23
|
];
|
|
29
24
|
for (const file of candidates) {
|
|
30
25
|
if (fs.existsSync(file)) {
|
package/bin/kreftjs.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* kreft dev --port 3000 another port
|
|
8
8
|
* kreft dev ./site another folder
|
|
9
9
|
* kreft build build this folder for production into ./dist
|
|
10
|
-
* kreft vscode install colors and snippets for .
|
|
10
|
+
* kreft vscode install colors and snippets for .kreft files in VS Code
|
|
11
11
|
* kreft serve dist run the built app in production (with --db: its database API)
|
|
12
12
|
* kreft db migrate run new database/migrations/*.sql (also: seed, tables, sql "…")
|
|
13
13
|
* kreft help show this help
|
|
@@ -87,7 +87,7 @@ function help() {
|
|
|
87
87
|
--no-auth …without login, register, profile and users (--auth: with them, the default)
|
|
88
88
|
--redis …configure Redis for caching, background queues and pub/sub
|
|
89
89
|
--ui <library> …with a CSS library from a CDN: tailwind, bootstrap, bulma or pico
|
|
90
|
-
--ts …use TypeScript across frontend (.
|
|
90
|
+
--ts …use TypeScript across frontend (.kreft, .ts) and backend (.ts)
|
|
91
91
|
--js …use JavaScript (default when non-interactive)
|
|
92
92
|
--electron …with Electron desktop app support (main.js, preload.js, packaging)
|
|
93
93
|
--vite …with Vite.js bundling & instant HMR (vite.config.js, npm run vite)
|
|
@@ -140,7 +140,7 @@ function help() {
|
|
|
140
140
|
kreft make:request StorePostRequest app/Http/Requests/StorePostRequest.js (--model Post)
|
|
141
141
|
kreft make:resource PostResource app/Http/Resources/PostResource.js
|
|
142
142
|
kreft make:rule Uppercase app/Rules/Uppercase.js
|
|
143
|
-
kreft make:page Post frontend/pages/Post.
|
|
143
|
+
kreft make:page Post frontend/pages/Post.kreft (--model Post, -r, --route=file, --auth)
|
|
144
144
|
kreft make:factory PostFactory database/factories/PostFactory.js (--model Post)
|
|
145
145
|
kreft make:migration create_posts_table
|
|
146
146
|
kreft make:seeder PostSeeder
|
|
@@ -155,7 +155,7 @@ function help() {
|
|
|
155
155
|
kreft make:command SendReport app/Console/Commands/SendReport.js (--command report:send)
|
|
156
156
|
kreft make:errors The error pages in errors/ (401, 403, 404, 419, 429, 500, 503…)
|
|
157
157
|
kreft make:admin <email> Let that user open the admin panel at /admin (--remove: take it away)
|
|
158
|
-
kreft make:routes admin A new route file for pages: frontend/routes/admin.
|
|
158
|
+
kreft make:routes admin A new route file for pages: frontend/routes/admin.kreft (--api: backend/routes/admin.js)
|
|
159
159
|
kreft db tables List tables and columns
|
|
160
160
|
kreft db sql "<SQL>" Run one SQL statement and print the result
|
|
161
161
|
kreft build [folder] Build for production (default: this folder)
|
|
@@ -175,7 +175,7 @@ function help() {
|
|
|
175
175
|
--check Check for outdated packages (npm outdated)
|
|
176
176
|
--core Update only Kreft runtime files (kreftjs.js, kreftjs.d.ts, server.d.ts)
|
|
177
177
|
--npm Update only npm dependencies
|
|
178
|
-
kreft vscode Install .
|
|
178
|
+
kreft vscode Install .kreft colors and snippets in VS Code (also Cursor, VSCodium)
|
|
179
179
|
|
|
180
180
|
Examples:
|
|
181
181
|
kreft new shop
|
|
@@ -256,7 +256,7 @@ async function createProject(args) {
|
|
|
256
256
|
fs.copyFileSync(path.join(__dirname, '..', 'kreftjs.js'), path.join(front, 'kreftjs.js'));
|
|
257
257
|
fs.copyFileSync(path.join(__dirname, '..', 'kreftjs.d.ts'), path.join(front, 'kreftjs.d.ts')); // editor hints
|
|
258
258
|
fs.copyFileSync(path.join(__dirname, '..', 'server.d.ts'), path.join(target, 'backend', 'server.d.ts')); // hints for require('kreftjs/server')
|
|
259
|
-
// the documentation pages, in their own folder: docs/Docs.
|
|
259
|
+
// the documentation pages, in their own folder: docs/Docs.kreft, its Markdown reader and the README.md manual
|
|
260
260
|
copyFolder(path.join(__dirname, '..', 'docs'), path.join(front, 'docs'));
|
|
261
261
|
|
|
262
262
|
const projectName = path.basename(target).toLowerCase().replace(/[^a-z0-9-]+/g, '-');
|
|
@@ -307,7 +307,7 @@ ${auth ? ` Then log in as test@example.com with the password "password", or reg
|
|
|
307
307
|
` : ' Created without authentication (--no-auth): there is no login, registration or users table.\n'}`);
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
// kreft new my-app --ts → TypeScript across backend (.ts) and frontend (.
|
|
310
|
+
// kreft new my-app --ts → TypeScript across backend (.ts) and frontend (.kreft, .ts), tsconfig.json, and npm run typecheck
|
|
311
311
|
function toTypeScript(target, projectPkg) {
|
|
312
312
|
const { toTs, TSCONFIG } = require('./to-ts.js');
|
|
313
313
|
const walk = (dir) => {
|
|
@@ -323,8 +323,8 @@ function toTypeScript(target, projectPkg) {
|
|
|
323
323
|
projectPkg.devDependencies = { esbuild: '^0.25.0', typescript: '^5.8.0', '@types/node': '^22.13.0' };
|
|
324
324
|
projectPkg.allowScripts = { esbuild: true };
|
|
325
325
|
// Shared TypeScript interfaces for the frontend
|
|
326
|
-
fs.writeFileSync(path.join(target, 'frontend', 'types.ts'), `// Shared TypeScript interfaces for your .
|
|
327
|
-
// Import in any .
|
|
326
|
+
fs.writeFileSync(path.join(target, 'frontend', 'types.ts'), `// Shared TypeScript interfaces for your .kreft pages and frontend modules.
|
|
327
|
+
// Import in any .kreft file or .ts module: import type { User } from '../types.ts'
|
|
328
328
|
|
|
329
329
|
export interface User {
|
|
330
330
|
id: number
|
|
@@ -355,10 +355,10 @@ export function ago(date: string | Date): string {
|
|
|
355
355
|
return 'just now'
|
|
356
356
|
}
|
|
357
357
|
`);
|
|
358
|
-
// Upgrade Counter.
|
|
359
|
-
const counterKreft = path.join(target, 'frontend', 'tests', 'components', 'Counter.
|
|
358
|
+
// Upgrade Counter.kreft to showcase TypeScript in .kreft components
|
|
359
|
+
const counterKreft = path.join(target, 'frontend', 'tests', 'components', 'Counter.kreft');
|
|
360
360
|
if (fs.existsSync(counterKreft)) {
|
|
361
|
-
fs.writeFileSync(counterKreft, `<!-- A TypeScript .
|
|
361
|
+
fs.writeFileSync(counterKreft, `<!-- A TypeScript .kreft component: a button that counts. Used by tests/pages/Test.kreft as <counter>. -->
|
|
362
362
|
prop start: number = 0
|
|
363
363
|
|
|
364
364
|
state count: number = start
|
|
@@ -377,8 +377,8 @@ button { font-variant-numeric: tabular-nums; justify-self: start; }
|
|
|
377
377
|
</style>
|
|
378
378
|
`);
|
|
379
379
|
}
|
|
380
|
-
// If --db created Todos.
|
|
381
|
-
const todosKreft = path.join(target, 'frontend', 'pages', 'Todos.
|
|
380
|
+
// If --db created Todos.kreft, upgrade it with TypeScript interfaces & typed actions
|
|
381
|
+
const todosKreft = path.join(target, 'frontend', 'pages', 'Todos.kreft');
|
|
382
382
|
if (fs.existsSync(todosKreft)) {
|
|
383
383
|
const src = fs.readFileSync(todosKreft, 'utf8');
|
|
384
384
|
fs.writeFileSync(todosKreft, src
|
|
@@ -391,9 +391,9 @@ button { font-variant-numeric: tabular-nums; justify-self: start; }
|
|
|
391
391
|
|
|
392
392
|
// kreft new my-app --ui bootstrap → the library's CDN links in index.html
|
|
393
393
|
function uiKits() { return {
|
|
394
|
-
tailwind: ['<!-- Tailwind CSS: use its classes in any .
|
|
395
|
-
bootstrap: ['<!-- Bootstrap: its classes and components (data-bs-toggle…) work in any .
|
|
396
|
-
bulma: ['<!-- Bulma: its classes work in any .
|
|
394
|
+
tailwind: ['<!-- Tailwind CSS: use its classes in any .kreft file -->', '<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>'],
|
|
395
|
+
bootstrap: ['<!-- Bootstrap: its classes and components (data-bs-toggle…) work in any .kreft file -->', '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">', '<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" defer></script>'],
|
|
396
|
+
bulma: ['<!-- Bulma: its classes work in any .kreft file -->', '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css">'],
|
|
397
397
|
pico: ['<!-- Pico CSS: plain HTML gets styled by itself -->', '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">'],
|
|
398
398
|
}; }
|
|
399
399
|
function addUiKit(target, ui) {
|
|
@@ -508,7 +508,7 @@ module.exports = TodoSeeder
|
|
|
508
508
|
const setUrl = (f, u) => fs.writeFileSync(f, fs.readFileSync(f, 'utf8').replace(/^DATABASE_URL=.*$/m, () => 'DATABASE_URL=' + u));
|
|
509
509
|
setUrl(path.join(target, '.env'), url);
|
|
510
510
|
setUrl(path.join(target, '.env.example'), url.replace(/\/\/[^:@\s]+:[^@\s]+@/, '//user:password@'));
|
|
511
|
-
fs.writeFileSync(path.join(front, 'pages', 'Todos.
|
|
511
|
+
fs.writeFileSync(path.join(front, 'pages', 'Todos.kreft'), `<!-- the todos table, through routes/api.js and app/Http/Controllers/TodoController.js -->
|
|
512
512
|
load todos = api.get('/api/todos')
|
|
513
513
|
// rules are checked here first, so a todo with mistakes is never sent; the server's 422 errors land here too
|
|
514
514
|
state draft = form({ title: '' }, { title: 'required|max:200' })
|
|
@@ -565,9 +565,9 @@ label { display: flex; align-items: center; gap: 10px; }
|
|
|
565
565
|
.done { color: var(--muted); text-decoration: line-through; }
|
|
566
566
|
</style>
|
|
567
567
|
`);
|
|
568
|
-
const webFile = path.join(front, 'routes', 'web.
|
|
569
|
-
fs.writeFileSync(webFile, fs.readFileSync(webFile, 'utf8').replace(/( Route\.view\('\/profile'[^\n]*\n)/, "$1 Route.view('/todos', 'pages/Todos.
|
|
570
|
-
const layout = path.join(front, 'layouts', 'app.
|
|
568
|
+
const webFile = path.join(front, 'routes', 'web.kreft');
|
|
569
|
+
fs.writeFileSync(webFile, fs.readFileSync(webFile, 'utf8').replace(/( Route\.view\('\/profile'[^\n]*\n)/, "$1 Route.view('/todos', 'pages/Todos.kreft').name('todos').title('Todos')\n"));
|
|
570
|
+
const layout = path.join(front, 'layouts', 'app.kreft');
|
|
571
571
|
fs.writeFileSync(layout, fs.readFileSync(layout, 'utf8').replace(/(<kreft-link k-if="\$store\.auth\.token" to="\/dashboard"[^>]*>[^<]*<\/kreft-link>)/, '$1\n <kreft-link k-if="$store.auth.token" to="/todos" exact-class="here">Todos</kreft-link>'));
|
|
572
572
|
}
|
|
573
573
|
function copyFolder(from, to) {
|
|
@@ -591,11 +591,11 @@ function installVscode() {
|
|
|
591
591
|
for (const home of homes) {
|
|
592
592
|
const dir = path.join(home, 'extensions');
|
|
593
593
|
fs.mkdirSync(dir, { recursive: true });
|
|
594
|
-
fs.readdirSync(dir).filter((n) => n.startsWith('kreft.kreftjs-') || n.startsWith('kreft.kreft-')
|
|
594
|
+
fs.readdirSync(dir).filter((n) => n.startsWith('kreft.kreftjs-') || n.startsWith('kreft.kreft-')) /* the older extensions go too */.forEach((n) => fs.rmSync(path.join(dir, n), { recursive: true, force: true }));
|
|
595
595
|
copyFolder(from, path.join(dir, `kreft.kreftjs-${ext.version}`));
|
|
596
596
|
console.log(` Installed in ${path.join(dir, `kreft.kreftjs-${ext.version}`)}`);
|
|
597
597
|
}
|
|
598
|
-
// Ensure current project .vscode/settings.json maps .
|
|
598
|
+
// Ensure current project .vscode/settings.json maps .kreft files to Kreft and configures formatter
|
|
599
599
|
const settingsDir = path.join(process.cwd(), '.vscode');
|
|
600
600
|
const settings = path.join(settingsDir, 'settings.json');
|
|
601
601
|
fs.mkdirSync(settingsDir, { recursive: true });
|
|
@@ -603,11 +603,11 @@ function installVscode() {
|
|
|
603
603
|
if (fs.existsSync(settings)) {
|
|
604
604
|
try { cfg = JSON.parse(fs.readFileSync(settings, 'utf8')); } catch {}
|
|
605
605
|
}
|
|
606
|
-
cfg['files.associations'] = { ...(cfg['files.associations'] || {}), '*.kreftjs': 'kreftjs' };
|
|
606
|
+
cfg['files.associations'] = { ...(cfg['files.associations'] || {}), '*.kreft': 'kreftjs', '*.kreftjs': 'kreftjs' };
|
|
607
607
|
cfg['[kreftjs]'] = { ...(cfg['[kreftjs]'] || {}), 'editor.defaultFormatter': 'kreft.kreftjs' };
|
|
608
608
|
fs.writeFileSync(settings, JSON.stringify(cfg, null, 2) + '\n');
|
|
609
|
-
console.log(' Updated .vscode/settings.json: .
|
|
610
|
-
console.log('\n Restart VS Code (or run "Reload Window") to see updated colors and formatting in .
|
|
609
|
+
console.log(' Updated .vscode/settings.json: .kreft files use Kreft language and kreft.kreftjs formatter');
|
|
610
|
+
console.log('\n Restart VS Code (or run "Reload Window") to see updated colors and formatting in .kreft files.\n');
|
|
611
611
|
}
|
|
612
612
|
|
|
613
613
|
/* ------------------------------------------------------------------ kreft dev */
|
|
@@ -670,7 +670,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
670
670
|
installAlias(); // require('kreftjs/server') and .ts files work in migrations and seeders
|
|
671
671
|
require('./backend/model.js').setBaseDir(backendDir);
|
|
672
672
|
const dir = migrations || migrationsDir(backendDir, config.dir, root);
|
|
673
|
-
const isNew = !(await db.tables()).some((t) => t.name === '
|
|
673
|
+
const isNew = !(await db.tables()).some((t) => t.name === 'kreft_migrations'); // nothing migrated yet
|
|
674
674
|
const ran = dir ? await migrator.migrate(db, dir, (f) => console.log(` migrated ${f}`)) : [];
|
|
675
675
|
// --seed: a brand-new database gets database/seeders/DatabaseSeeder.js
|
|
676
676
|
if (seedNew && isNew && ran.length) await migrator.seed(db, backendDir, { log: (n) => console.log(` seeded ${n}`) });
|
|
@@ -731,7 +731,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
731
731
|
const npmMap = () => (production ? null : deps.importMap(project));
|
|
732
732
|
|
|
733
733
|
const types = {
|
|
734
|
-
'.html': 'text/html', '.js': 'text/javascript', '.mjs': 'text/javascript', '.
|
|
734
|
+
'.html': 'text/html', '.js': 'text/javascript', '.mjs': 'text/javascript', '.kreft': 'text/plain', '.kreftjs': 'text/plain',
|
|
735
735
|
'.css': 'text/css', '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png',
|
|
736
736
|
'.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp',
|
|
737
737
|
'.ico': 'image/x-icon', '.woff2': 'font/woff2', '.woff': 'font/woff', '.txt': 'text/plain', '.md': 'text/markdown',
|
|
@@ -830,8 +830,8 @@ async function devServer(args, { production = false } = {}) {
|
|
|
830
830
|
if (!isFile(full)) return;
|
|
831
831
|
const urlPath = '/' + rel.split(path.sep).join('/');
|
|
832
832
|
const ext = path.extname(full).toLowerCase();
|
|
833
|
-
const isComp = ext === '.
|
|
834
|
-
const routeFile = isComp && /^routes[\/\\]/.test(rel); // routes/web.
|
|
833
|
+
const isComp = ext === '.kreft' || ext === '.kreftjs'; // .kreftjs: files of a project from the 0.0.x betas
|
|
834
|
+
const routeFile = isComp && /^routes[\/\\]/.test(rel); // routes/web.kreft: the whole page loads again
|
|
835
835
|
const msg = { type: isComp && !routeFile ? 'kreft' : ext === '.css' ? 'css' : 'reload', path: urlPath };
|
|
836
836
|
clients.forEach((c) => c.write(`data: ${JSON.stringify(msg)}\n\n`));
|
|
837
837
|
if (clients.size) console.log(` ${msg.type === 'reload' ? 'reload' : 'update'} ${urlPath}`);
|
|
@@ -862,7 +862,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
862
862
|
if (!isFile(full)) return;
|
|
863
863
|
const urlPath = '/admin/' + rel.split(path.sep).join('/');
|
|
864
864
|
const ext = path.extname(full).toLowerCase();
|
|
865
|
-
const isComp = ext === '.
|
|
865
|
+
const isComp = ext === '.kreft' || ext === '.kreftjs';
|
|
866
866
|
const routeFile = /^routes[\/\\]/.test(rel);
|
|
867
867
|
const msg = { type: isComp && !routeFile ? 'kreft' : ext === '.css' ? 'css' : 'reload', path: urlPath };
|
|
868
868
|
clients.forEach((c) => c.write(`data: ${JSON.stringify(msg)}\n\n`));
|
|
@@ -904,7 +904,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
904
904
|
clients.forEach((c) => c.write(`data: ${JSON.stringify({ type: 'reload', path: '/.env' })}\n\n`));
|
|
905
905
|
});
|
|
906
906
|
// files a browser must never get: the database itself, its settings and password, hidden files, locks, dumps
|
|
907
|
-
const secret = (p) => /(^|\/)\.|\.(db|sqlite3?|db-wal|db-shm|sqlite-wal|sqlite-shm|sql|bak|dump|key|pem|crt)$|(^|\/)((?:kreftjs|kreft
|
|
907
|
+
const secret = (p) => /(^|\/)\.|\.(db|sqlite3?|db-wal|db-shm|sqlite-wal|sqlite-shm|sql|bak|dump|key|pem|crt)$|(^|\/)((?:kreftjs|kreft)\.config\.json|package(-lock)?\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb|composer\.(json|lock)|docker-compose.*\.ya?ml|Dockerfile|node_modules|vendor)(\/|$)/i.test(p)
|
|
908
908
|
|| (production && /(^|\/)(database|app|routes)\//i.test(p));
|
|
909
909
|
setInterval(() => clients.forEach((c) => c.write(': ping\n\n')), 25000).unref();
|
|
910
910
|
// a page of the app: index.html, with the SEO tags of backend/routes/seo.js for this address (and its status)
|
|
@@ -989,7 +989,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
989
989
|
const down = downInfo();
|
|
990
990
|
if (!down) return false;
|
|
991
991
|
const { same, cookieSecure } = require('./backend/http.js');
|
|
992
|
-
const cookie = (req.headers.cookie || '').match(/(?:^|;\s*)
|
|
992
|
+
const cookie = (req.headers.cookie || '').match(/(?:^|;\s*)kreft_bypass=([^;]+)/);
|
|
993
993
|
let cookieVal = null;
|
|
994
994
|
if (cookie) { try { cookieVal = decodeURIComponent(cookie[1]); } catch (_) { cookieVal = cookie[1]; } }
|
|
995
995
|
if (down.secret && cookieVal && same(cookieVal, String(down.secret))) return false;
|
|
@@ -1018,7 +1018,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
1018
1018
|
// therefore only answers the names of this computer (add more with KREFT_ALLOWED_HOSTS=my-tunnel.example).
|
|
1019
1019
|
const hostOnly = (h) => String(h || '').toLowerCase().replace(/:\d+$/, '').replace(/^\[|\]$/g, '');
|
|
1020
1020
|
const isLocalName = (n) => n === 'localhost' || n === '127.0.0.1' || n === '::1' || n.endsWith('.localhost');
|
|
1021
|
-
const allowedHosts = new Set(String(
|
|
1021
|
+
const allowedHosts = new Set(String(process.env.KREFT_ALLOWED_HOSTS || '').split(',').map((x) => x.trim().toLowerCase()).filter(Boolean));
|
|
1022
1022
|
const loopbackBind = !production && (host === 'localhost' || host === '::1' || /^127\./.test(host));
|
|
1023
1023
|
// a request from another web site (Origin) that changes data in the unauthenticated API (--api, --db)
|
|
1024
1024
|
const crossSiteWrite = (req) => {
|
|
@@ -1068,7 +1068,6 @@ async function devServer(args, { production = false } = {}) {
|
|
|
1068
1068
|
let urlPath;
|
|
1069
1069
|
try { urlPath = decodeURIComponent(new URL(req.url, 'http://x').pathname); }
|
|
1070
1070
|
catch (_) { res.writeHead(400); return res.end('Bad request'); }
|
|
1071
|
-
if (urlPath.startsWith('/__som/')) urlPath = '/__kreft/' + urlPath.slice('/__som/'.length); // the Som.js address of the dev files
|
|
1072
1071
|
// a backslash or a NUL in the path is never a real page, and on Windows a backslash would act as a folder separator
|
|
1073
1072
|
if (/[\\\0]/.test(urlPath)) { res.writeHead(400); return res.end('Bad request'); }
|
|
1074
1073
|
if (maintenance(req, res, urlPath)) return;
|
package/bin/layout.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Where a project's parts are.
|
|
3
3
|
*
|
|
4
4
|
* New projects (kreft new) keep frontend and backend apart:
|
|
5
|
-
* frontend/ the only folder a browser can reach: index.html, pages, components, routes/web.
|
|
5
|
+
* frontend/ the only folder a browser can reach: index.html, pages, components, routes/web.kreft …
|
|
6
6
|
* backend/ never served: routes/api.js, app/, database/ (and server/)
|
|
7
7
|
* .env, package.json, node_modules/ at the top, next to both
|
|
8
8
|
*
|
package/bin/no-auth.js
CHANGED
|
@@ -11,8 +11,8 @@ const path = require('path');
|
|
|
11
11
|
|
|
12
12
|
const TO_REMOVE = [
|
|
13
13
|
// frontend: the sign-in pages, the profile, the admin pages and what only they use
|
|
14
|
-
'frontend/pages/auth', 'frontend/pages/Profile.
|
|
15
|
-
'frontend/components/profile', 'frontend/components/StatCard.
|
|
14
|
+
'frontend/pages/auth', 'frontend/pages/Profile.kreft', 'frontend/pages/Users.kreft', 'frontend/pages/Dashboard.kreft',
|
|
15
|
+
'frontend/components/profile', 'frontend/components/StatCard.kreft', 'frontend/layouts/guest.kreft',
|
|
16
16
|
// backend: the auth routes, controllers, middleware, models and the users table
|
|
17
17
|
'backend/routes/auth.js', 'backend/app/Auth',
|
|
18
18
|
'backend/app/Http/Controllers/Auth', 'backend/app/Http/Controllers/Admin', 'backend/app/Http/Controllers/ProfileController.js',
|
|
@@ -62,19 +62,21 @@ module.exports = DatabaseSeeder
|
|
|
62
62
|
const WEB = (todos) => `// Application Web Routes (this app was created without sign-in: --no-auth).
|
|
63
63
|
|
|
64
64
|
// Welcome page
|
|
65
|
-
Route.view('/', 'pages/Home.
|
|
66
|
-
Route.view('/welcome', 'pages/Home.
|
|
67
|
-
${todos ? "\nRoute.view('/todos', 'pages/Todos.
|
|
65
|
+
Route.view('/', 'pages/Home.kreft').layout('none').name('home').title('Welcome')
|
|
66
|
+
Route.view('/welcome', 'pages/Home.kreft').layout('none').name('welcome').title('Welcome')
|
|
67
|
+
${todos ? "\nRoute.view('/todos', 'pages/Todos.kreft').name('todos').title('Todos')\n" : ''}
|
|
68
68
|
// Documentation (docs/) and test suite (tests/)
|
|
69
|
-
Route.view('/docs', 'docs/Docs.
|
|
70
|
-
Route.view('/error-monitor', 'pages/ErrorMonitor.
|
|
69
|
+
Route.view('/docs', 'docs/Docs.kreft').name('docs').title('Documentation').layout('docs') // its own slim bar
|
|
70
|
+
Route.view('/error-monitor', 'pages/ErrorMonitor.kreft').name('error-monitor').title('Error Monitor')
|
|
71
71
|
Route.prefix('/tests').name('tests.').group(() => {
|
|
72
|
-
Route.view('/', 'tests/pages/Test.
|
|
73
|
-
Route.view('/about', 'tests/pages/About.
|
|
72
|
+
Route.view('/', 'tests/pages/Test.kreft').name('index').title('Test Suite')
|
|
73
|
+
Route.view('/about', 'tests/pages/About.kreft').name('about').title('About · Tests')
|
|
74
74
|
})
|
|
75
75
|
`;
|
|
76
76
|
|
|
77
77
|
const LAYOUT = (todos) => `<!-- The default layout: a slim bar and the page. Every route without .layout(…) uses it (see index.html). -->
|
|
78
|
+
state docs = (Kreftjs.env.PUBLIC_DOCS_URL || 'http://kreftjs.com').replace(/\\/+$/, '') // where "Documentation" links go: PUBLIC_DOCS_URL in .env
|
|
79
|
+
|
|
78
80
|
<view>
|
|
79
81
|
<header>
|
|
80
82
|
<kreft-link to="/" class="brand"><span class="mark" aria-hidden="true"></span>{env.PUBLIC_APP_NAME || 'My app'}</kreft-link>
|
|
@@ -83,7 +85,7 @@ const LAYOUT = (todos) => `<!-- The default layout: a slim bar and the page. Eve
|
|
|
83
85
|
<kreft-link to="/todos" exact-class="here">Todos</kreft-link>` : ''}
|
|
84
86
|
<kreft-link to="/error-monitor" exact-class="here">Error monitor</kreft-link>
|
|
85
87
|
<kreft-link to="/tests" exact-class="here">Tests</kreft-link>
|
|
86
|
-
<
|
|
88
|
+
<a href={docs + '/docs'} target="_blank" rel="noopener">Docs</a>
|
|
87
89
|
<button type="button" class="theme" @click="theme.toggle()" aria-label="Toggle theme" title="Toggle theme">
|
|
88
90
|
<span k-if="theme.dark">☀</span><span k-else>🌙</span>
|
|
89
91
|
</button>
|
|
@@ -125,18 +127,18 @@ function removeAuth(target) {
|
|
|
125
127
|
write('backend/routes/api.js', API(todos));
|
|
126
128
|
write('backend/app/Providers/AppServiceProvider.js', PROVIDER);
|
|
127
129
|
write('backend/database/seeders/DatabaseSeeder.js', SEEDER(todos));
|
|
128
|
-
write('frontend/routes/web.
|
|
129
|
-
write('frontend/layouts/app.
|
|
130
|
+
write('frontend/routes/web.kreft', WEB(todos));
|
|
131
|
+
write('frontend/layouts/app.kreft', LAYOUT(todos));
|
|
130
132
|
|
|
131
133
|
// index.html: no auth store, no guards, no "signed out" handling; the router keeps its pages and layout
|
|
132
134
|
let html = read('frontend/index.html');
|
|
133
135
|
const from = html.indexOf(' // Who is signed in.');
|
|
134
|
-
const to = html.indexOf(" Kreftjs.app('App.
|
|
136
|
+
const to = html.indexOf(" Kreftjs.app('App.kreft')");
|
|
135
137
|
if (from >= 0 && to > from) {
|
|
136
138
|
html = html.slice(0, from) + ` const router = Kreftjs.router({
|
|
137
|
-
routes: ['routes/web.
|
|
138
|
-
layout: 'app', // every page goes inside layouts/app.
|
|
139
|
-
errors: 'errors', // error pages: errors/404.
|
|
139
|
+
routes: ['routes/web.kreft'], // the pages are in routes/web.kreft; add more files: kreft make:routes admin
|
|
140
|
+
layout: 'app', // every page goes inside layouts/app.kreft…
|
|
141
|
+
errors: 'errors', // error pages: errors/404.kreft for unknown addresses, abort(403) → errors/403.kreft
|
|
140
142
|
})
|
|
141
143
|
|
|
142
144
|
` + html.slice(to);
|
|
@@ -145,12 +147,12 @@ function removeAuth(target) {
|
|
|
145
147
|
write('frontend/index.html', html);
|
|
146
148
|
|
|
147
149
|
// the welcome page loses its Log in / Sign up / Dashboard links
|
|
148
|
-
const home = read('frontend/pages/Home.
|
|
150
|
+
const home = read('frontend/pages/Home.kreft')
|
|
149
151
|
.replace(/[ \t]*<kreft-link k-if="!?\$store\.auth\.user"[^>]*>[\s\S]*?<\/kreft-link>[ \t]*\r?\n/g, '');
|
|
150
|
-
write('frontend/pages/Home.
|
|
152
|
+
write('frontend/pages/Home.kreft', home);
|
|
151
153
|
|
|
152
154
|
// the docs bar went back to "the app" when signed in
|
|
153
|
-
write('frontend/layouts/docs.
|
|
155
|
+
write('frontend/layouts/docs.kreft', read('frontend/layouts/docs.kreft')
|
|
154
156
|
.replace(/<kreft-link to=\{\$store\.auth\.user \? '\/dashboard' : '\/'\} class="back">← \{\$store\.auth\.user \? 'Back to the app' : 'Home'\}<\/kreft-link>/,
|
|
155
157
|
'<kreft-link to="/" class="back">← Home</kreft-link>'));
|
|
156
158
|
|
package/bin/prerender.js
CHANGED
|
@@ -131,7 +131,7 @@ function createRenderer({ jsdom, origin, basePath, maxWait = 10000, userAgent, e
|
|
|
131
131
|
return {
|
|
132
132
|
problems,
|
|
133
133
|
path: router ? router.route.path : route,
|
|
134
|
-
errorStatus: router && router.route.error ? Number(router.route.error.status) || 500 : 0, // the app showed errors/404.
|
|
134
|
+
errorStatus: router && router.route.error ? Number(router.route.error.status) || 500 : 0, // the app showed errors/404.kreft (or another)
|
|
135
135
|
title: w.document.title,
|
|
136
136
|
tags: seoTags(w.document, origin),
|
|
137
137
|
htmlAttrs: ['lang', 'dir', 'data-kreft-orig-lang', 'data-kreft-orig-dir'].filter((a) => w.document.documentElement.hasAttribute(a)).map((a) => [a, w.document.documentElement.getAttribute(a)]),
|