kreftjs 0.0.3-beta → 0.0.4-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 +12 -12
- package/bin/add.js +11 -11
- package/bin/artisan-more.js +41 -41
- package/bin/artisan.js +41 -41
- package/bin/backend/command.js +1 -1
- package/bin/backend/factory.js +1 -1
- package/bin/backend/index.js +3 -3
- package/bin/backend/model.js +2 -2
- package/bin/backend/notifications.js +1 -1
- package/bin/backend/queue.js +2 -2
- package/bin/backend/schedule.js +2 -2
- package/bin/backend/seo-pages.js +1 -1
- package/bin/backend/storage.js +1 -1
- package/bin/backend/support.js +4 -4
- package/bin/backend/typescript.js +1 -1
- package/bin/build.js +11 -11
- package/bin/db-api.js +3 -3
- package/bin/db.js +1 -1
- package/bin/deps.js +6 -6
- package/bin/electron.js +11 -11
- package/bin/env.js +2 -2
- package/bin/guard.js +2 -2
- package/bin/kreftjs-config.js +1 -1
- package/bin/kreftjs.js +105 -105
- package/bin/layout.js +1 -1
- package/bin/no-auth.js +4 -4
- package/bin/prerender.js +4 -4
- package/bin/server-routes.js +2 -2
- package/bin/ssr.js +2 -2
- package/bin/terminal.js +1 -1
- package/bin/to-ts.js +1 -1
- package/bin/update.js +14 -14
- package/bin/urls-file.js +1 -1
- package/bin/vite.js +11 -11
- package/docs/Docs.kreftjs +1 -1
- package/docs/README.md +140 -123
- package/kreftjs.d.ts +1 -1
- package/kreftjs.js +18 -18
- package/package.json +1 -1
- package/templates/app/README.md +20 -20
- package/templates/app/backend/database/migrations/0001_01_01_000000_create_users_table.js +1 -1
- package/templates/app/backend/database/migrations/0001_01_01_000002_create_jobs_table.js +1 -1
- package/templates/app/backend/database/seeders/DatabaseSeeder.js +1 -1
- package/templates/app/backend/routes/api.js +1 -1
- package/templates/app/backend/routes/console.js +2 -2
- package/templates/app/frontend/errors/ErrorMonitor.kreftjs +2 -2
- package/templates/app/frontend/index.html +1 -1
- package/templates/app/frontend/kreftjs.d.ts +1 -1
- package/templates/app/frontend/kreftjs.js +18 -18
- package/templates/app/frontend/pages/Dashboard.kreftjs +1 -1
- package/templates/app/frontend/pages/ErrorMonitor.kreftjs +1 -1
- package/templates/app/frontend/pages/Home.kreftjs +2 -2
- package/templates/errors/ErrorMonitor.kreftjs +1 -1
package/bin/kreftjs.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
/*
|
|
3
3
|
* The kreftjs command
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
5
|
+
* kreft new my-app create a new Kreft project
|
|
6
|
+
* kreft dev run the dev server in this folder (http://localhost:8000)
|
|
7
|
+
* kreft dev --port 3000 another port
|
|
8
|
+
* kreft dev ./site another folder
|
|
9
|
+
* kreft build build this folder for production into ./dist
|
|
10
|
+
* kreft vscode install colors and snippets for .kreftjs files in VS Code
|
|
11
|
+
* kreft serve dist run the built app in production (with --db: its database API)
|
|
12
|
+
* kreft db migrate run new database/migrations/*.sql (also: seed, tables, sql "…")
|
|
13
|
+
* kreft help show this help
|
|
14
14
|
*
|
|
15
15
|
* No dependencies — only Node.js.
|
|
16
16
|
*/
|
|
@@ -71,7 +71,7 @@ guard.checkStart(command);
|
|
|
71
71
|
if (['queue:work', 'queue:start', 'schedule:work'].includes(command)) guard.watch([process.cwd()]);
|
|
72
72
|
if (commands[command]) commands[command](rest);
|
|
73
73
|
else if (command && !command.startsWith('-')) {
|
|
74
|
-
// your own commands: app/Console/Commands (
|
|
74
|
+
// your own commands: app/Console/Commands (kreft make:command SendReport)
|
|
75
75
|
require('./artisan-more.js').runCustom(command, rest).then((found) => { if (!found) unknown(); }, (e) => { console.error(' ' + (e.stack || e)); process.exitCode = 1; });
|
|
76
76
|
} else unknown();
|
|
77
77
|
|
|
@@ -81,7 +81,7 @@ function help() {
|
|
|
81
81
|
Kreft ${pkg.version}
|
|
82
82
|
|
|
83
83
|
Usage:
|
|
84
|
-
|
|
84
|
+
kreft new <name> Create a new project in a folder called <name>, with login, register,
|
|
85
85
|
password reset, email verification and profile pages (on SQLite)
|
|
86
86
|
--db <kind> …on another database (sqlite, mysql or postgres), plus a Todos example
|
|
87
87
|
--no-auth …without login, register, profile and users (--auth: with them, the default)
|
|
@@ -91,7 +91,7 @@ function help() {
|
|
|
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)
|
|
94
|
-
|
|
94
|
+
kreft dev [folder] Start the dev server (default: this folder)
|
|
95
95
|
--port <number> Port to use (default: 8000, or the next free one)
|
|
96
96
|
--host <address> Who may open it (default: 127.0.0.1, only this computer; 0.0.0.0: your network)
|
|
97
97
|
--api <file.json> Serve a REST API from a JSON file at /api (GET, POST, PUT, PATCH, DELETE)
|
|
@@ -104,83 +104,83 @@ function help() {
|
|
|
104
104
|
--server <dir> Your own Node.js code for /api (default: server/): server/hello.js → /api/hello
|
|
105
105
|
--db <database> Serve the tables of a database at /api (new migrations run first):
|
|
106
106
|
sqlite:database/app.db (built in, nothing to install)
|
|
107
|
-
mysql://user:pass@localhost:3306/shop (
|
|
108
|
-
postgres://user:pass@localhost:5432/shop (
|
|
109
|
-
|
|
107
|
+
mysql://user:pass@localhost:3306/shop (kreft add mysql2)
|
|
108
|
+
postgres://user:pass@localhost:5432/shop (kreft add pg)
|
|
109
|
+
kreft serve [folder] Run a built app for real users (folder: dist). Same --port and --db;
|
|
110
110
|
only the tables listed in kreftjs.config.json are served
|
|
111
111
|
|
|
112
112
|
Bundler & Dev Server (Vite.js):
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
kreft vite Start Vite dev server and proxy backend API (alias: kreft vite:dev)
|
|
114
|
+
kreft vite:dev Start Vite dev server with instant HMR and backend proxy
|
|
115
|
+
kreft vite:build Build production bundle with Vite and Rollup
|
|
116
|
+
kreft vite:preview Preview production build locally with Vite
|
|
117
|
+
kreft vite:init Add Vite configuration to an existing project (alias: kreft make:vite)
|
|
118
118
|
|
|
119
119
|
Desktop (Electron.js):
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
The backend (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
120
|
+
kreft electron Start dev server and launch desktop window (alias: kreft electron:dev)
|
|
121
|
+
kreft electron:dev Start dev server and launch desktop window
|
|
122
|
+
kreft electron:build Build static bundle and package desktop app with electron-builder
|
|
123
|
+
kreft electron:init Add Electron support to an existing project (alias: kreft make:electron)
|
|
124
|
+
|
|
125
|
+
The backend (kreft list shows every command, yours too):
|
|
126
|
+
kreft key:generate Set the application encryption key in .env (--show: print only)
|
|
127
|
+
kreft migrate Run new migrations in database/migrations (.js or .sql)
|
|
128
|
+
kreft migrate:rollback Undo the last batch of migrations (--step 2: the last two)
|
|
129
|
+
kreft migrate:fresh Drop every table and migrate again (--seed: then seed)
|
|
130
|
+
kreft migrate:status Which migrations ran
|
|
131
|
+
kreft db:seed Run database/seeders/DatabaseSeeder.js (--class PostSeeder: another one)
|
|
132
|
+
kreft route:list The routes in routes/api.js
|
|
133
|
+
kreft storage:link Connect public/storage to storage/app/public (--force) (alias: kreft link:storage)
|
|
134
|
+
kreft storage:unlink Remove the storage link (alias: kreft unlink:storage)
|
|
135
|
+
kreft sitemap Generate public/sitemap.xml and robots.txt
|
|
136
|
+
kreft log Tail recent log entries from storage/logs/kreft.log (--lines 50, --level error)
|
|
137
|
+
kreft log:clear Clear the log file
|
|
138
|
+
kreft make:model Post app/Models/Post.js (-m -f -s -c -p -r, --route=file, --auth, --all)
|
|
139
|
+
kreft make:controller PostController (--api, --model Post, -r, --route=file, --auth)
|
|
140
|
+
kreft make:request StorePostRequest app/Http/Requests/StorePostRequest.js (--model Post)
|
|
141
|
+
kreft make:resource PostResource app/Http/Resources/PostResource.js
|
|
142
|
+
kreft make:rule Uppercase app/Rules/Uppercase.js
|
|
143
|
+
kreft make:page Post frontend/pages/Post.kreftjs (--model Post, -r, --route=file, --auth)
|
|
144
|
+
kreft make:factory PostFactory database/factories/PostFactory.js (--model Post)
|
|
145
|
+
kreft make:migration create_posts_table
|
|
146
|
+
kreft make:seeder PostSeeder
|
|
147
|
+
kreft make:middleware EnsureIsAdmin
|
|
148
|
+
kreft make:job SendWelcomeEmail app/Jobs/SendWelcomeEmail.js
|
|
149
|
+
kreft make:event OrderShipped app/Events/OrderShipped.js
|
|
150
|
+
kreft make:listener SendShipmentNotice app/Listeners/SendShipmentNotice.js (--event OrderShipped)
|
|
151
|
+
kreft make:mail WelcomeMail app/Mail/WelcomeMail.js
|
|
152
|
+
kreft make:notification InvoicePaid app/Notifications/InvoicePaid.js
|
|
153
|
+
kreft make:policy PostPolicy app/Policies/PostPolicy.js (--model Post)
|
|
154
|
+
kreft make:observer PostObserver app/Observers/PostObserver.js (--model Post)
|
|
155
|
+
kreft make:command SendReport app/Console/Commands/SendReport.js (--command report:send)
|
|
156
|
+
kreft make:errors The error pages in errors/ (401, 403, 404, 419, 429, 500, 503…)
|
|
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.kreftjs (--api: backend/routes/admin.js)
|
|
159
|
+
kreft db tables List tables and columns
|
|
160
|
+
kreft db sql "<SQL>" Run one SQL statement and print the result
|
|
161
|
+
kreft build [folder] Build for production (default: this folder)
|
|
162
162
|
--out <folder> Where to write it (default: dist)
|
|
163
163
|
--base <path> Change the <base href> (for example /)
|
|
164
164
|
--prerender [paths] Also save every page as ready HTML (needs: npm i -D jsdom)
|
|
165
165
|
--include <folder> Also copy this folder into the output (for example docs)
|
|
166
|
-
|
|
166
|
+
kreft add <packages...> Install npm packages with strict security gatekeeper (alias: kreft install, kreft i)
|
|
167
167
|
-D, --dev Install as devDependency
|
|
168
168
|
-y, --yes Auto-confirm unverified community packages in CI/CD
|
|
169
169
|
--dry-run, --check Inspect package security report without installing
|
|
170
170
|
--allow-scripts Permit postinstall lifecycle scripts (default: sandboxed with --ignore-scripts)
|
|
171
171
|
--force Bypass redundancy blocks, typosquat traps and security warnings
|
|
172
|
-
|
|
172
|
+
kreft update [packages] Update project packages (npm update) and Kreft runtime files
|
|
173
173
|
--latest Upgrade packages to their latest versions
|
|
174
174
|
--check Check for outdated packages (npm outdated)
|
|
175
175
|
--core Update only Kreft runtime files (kreftjs.js, kreftjs.d.ts, server.d.ts)
|
|
176
176
|
--npm Update only npm dependencies
|
|
177
|
-
|
|
177
|
+
kreft vscode Install .kreftjs colors and snippets in VS Code (also Cursor, VSCodium)
|
|
178
178
|
|
|
179
179
|
Examples:
|
|
180
|
-
|
|
180
|
+
kreft new shop
|
|
181
181
|
cd shop
|
|
182
182
|
npm run dev
|
|
183
|
-
|
|
183
|
+
kreft update
|
|
184
184
|
npm run build
|
|
185
185
|
`);
|
|
186
186
|
}
|
|
@@ -191,7 +191,7 @@ function unknown() {
|
|
|
191
191
|
process.exit(1);
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
/* ------------------------------------------------------------------
|
|
194
|
+
/* ------------------------------------------------------------------ kreft new */
|
|
195
195
|
async function createProject(args) {
|
|
196
196
|
let name = null, dbKind = null, ui = null, ts = false, langChosen = false, electron = false, vite = false, redis = false, auth = true, authChosen = false;
|
|
197
197
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -217,7 +217,7 @@ async function createProject(args) {
|
|
|
217
217
|
process.exit(1);
|
|
218
218
|
}
|
|
219
219
|
if (!name) {
|
|
220
|
-
console.error(' Give the project a name:
|
|
220
|
+
console.error(' Give the project a name: kreft new my-app (or: kreft new my-app --ts)');
|
|
221
221
|
process.exit(1);
|
|
222
222
|
}
|
|
223
223
|
const target = path.resolve(name);
|
|
@@ -264,8 +264,8 @@ async function createProject(args) {
|
|
|
264
264
|
version: '0.1.0',
|
|
265
265
|
private: true,
|
|
266
266
|
scripts: {
|
|
267
|
-
dev: '
|
|
268
|
-
migrate: '
|
|
267
|
+
dev: 'kreft dev --seed', start: 'kreft dev --seed', build: 'kreft build', preview: 'kreft dev dist', serve: 'kreft serve dist',
|
|
268
|
+
migrate: 'kreft migrate', 'db:seed': 'kreft db:seed', 'migrate:fresh': 'kreft migrate:fresh --seed',
|
|
269
269
|
},
|
|
270
270
|
};
|
|
271
271
|
// .env: the project's own settings (from .env.example, with its name filled in)
|
|
@@ -298,7 +298,7 @@ async function createProject(args) {
|
|
|
298
298
|
Created ${name}/
|
|
299
299
|
|
|
300
300
|
Next:
|
|
301
|
-
cd ${name}${ts ? '\n npm install (TypeScript, its Node.js types and esbuild)' : ''}${vite ? '\n npm install (install vite)\n
|
|
301
|
+
cd ${name}${ts ? '\n npm install (TypeScript, its Node.js types and esbuild)' : ''}${vite ? '\n npm install (install vite)\n kreft vite:dev (launch Vite dev server with instant HMR)' : ''}${electron ? '\n npm install (install electron & electron-builder)\n kreft electron:dev (launch desktop window)' : ''}${dbKind === 'mysql' ? '\n kreft add mysql2 (the MySQL driver, verified & sandboxed)' : dbKind === 'postgres' ? '\n kreft add pg (the PostgreSQL driver, verified & sandboxed)' : ''}${dbKind && dbKind !== 'sqlite' ? '\n (set DATABASE_URL in .env to your database)' : ''}
|
|
302
302
|
npm run dev
|
|
303
303
|
|
|
304
304
|
${auth ? ` Then log in as test@example.com with the password "password", or register.
|
|
@@ -306,7 +306,7 @@ ${auth ? ` Then log in as test@example.com with the password "password", or reg
|
|
|
306
306
|
` : ' Created without authentication (--no-auth): there is no login, registration or users table.\n'}`);
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
//
|
|
309
|
+
// kreft new my-app --ts → TypeScript across backend (.ts) and frontend (.kreftjs, .ts), tsconfig.json, and npm run typecheck
|
|
310
310
|
function toTypeScript(target, projectPkg) {
|
|
311
311
|
const { toTs, TSCONFIG } = require('./to-ts.js');
|
|
312
312
|
const walk = (dir) => {
|
|
@@ -341,7 +341,7 @@ export interface ApiList<T> {
|
|
|
341
341
|
// a typed helper for the frontend, used like any module: import { money } from '../logic/format.ts'
|
|
342
342
|
fs.mkdirSync(path.join(target, 'frontend', 'logic'), { recursive: true });
|
|
343
343
|
fs.writeFileSync(path.join(target, 'frontend', 'logic', 'format.ts'), `// A TypeScript module for the pages: import { money, ago } from '../logic/format.ts'
|
|
344
|
-
//
|
|
344
|
+
// kreft dev turns it into JavaScript as it is sent; kreft build writes it as format.js
|
|
345
345
|
|
|
346
346
|
export function money(amount: number, currency: string = 'USD'): string {
|
|
347
347
|
return new Intl.NumberFormat('en', { style: 'currency', currency }).format(amount)
|
|
@@ -388,7 +388,7 @@ button { font-variant-numeric: tabular-nums; justify-self: start; }
|
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
-
//
|
|
391
|
+
// kreft new my-app --ui bootstrap → the library's CDN links in index.html
|
|
392
392
|
function uiKits() { return {
|
|
393
393
|
tailwind: ['<!-- Tailwind CSS: use its classes in any .kreftjs file -->', '<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>'],
|
|
394
394
|
bootstrap: ['<!-- Bootstrap: its classes and components (data-bs-toggle…) work in any .kreftjs 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>'],
|
|
@@ -411,14 +411,14 @@ function addUiKit(target, ui) {
|
|
|
411
411
|
fs.writeFileSync(file, updated);
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
-
//
|
|
414
|
+
// kreft new my-app --db sqlite → database/migrations, a seed, kreftjs.config.json and a Todos page
|
|
415
415
|
function addDatabase(target, kind, projectName) {
|
|
416
416
|
const back = path.join(target, 'backend'), front = path.join(target, 'frontend');
|
|
417
417
|
const file = (rel, text) => { fs.mkdirSync(path.dirname(path.join(back, rel)), { recursive: true }); fs.writeFileSync(path.join(back, rel), text); };
|
|
418
418
|
const d = new Date(), p2 = (n) => String(n).padStart(2, '0');
|
|
419
419
|
const stamp = `${d.getFullYear()}_${p2(d.getMonth() + 1)}_${p2(d.getDate())}_000001`;
|
|
420
420
|
// A migration, a model, a controller, a seeder and a route
|
|
421
|
-
file(`database/migrations/${stamp}_create_todos_table.js`, `//
|
|
421
|
+
file(`database/migrations/${stamp}_create_todos_table.js`, `// kreft dev runs new migrations by itself (or: kreft migrate). kreft migrate:rollback runs down().
|
|
422
422
|
module.exports = {
|
|
423
423
|
up(schema) {
|
|
424
424
|
schema.create('todos', (table) => {
|
|
@@ -484,7 +484,7 @@ module.exports = TodoController
|
|
|
484
484
|
file('database/seeders/TodoSeeder.js', `const { Seeder } = require('kreftjs/server')
|
|
485
485
|
const Todo = require('../../app/Models/Todo')
|
|
486
486
|
|
|
487
|
-
// DatabaseSeeder calls this one (or on its own:
|
|
487
|
+
// DatabaseSeeder calls this one (or on its own: kreft db:seed --class TodoSeeder)
|
|
488
488
|
class TodoSeeder extends Seeder {
|
|
489
489
|
async run() {
|
|
490
490
|
await Todo.create({ title: 'Learn Kreft', done: true })
|
|
@@ -579,7 +579,7 @@ function copyFolder(from, to) {
|
|
|
579
579
|
}
|
|
580
580
|
}
|
|
581
581
|
|
|
582
|
-
/* ------------------------------------------------------------------
|
|
582
|
+
/* ------------------------------------------------------------------ kreft vscode */
|
|
583
583
|
function installVscode() {
|
|
584
584
|
const os = require('os');
|
|
585
585
|
const from = path.join(__dirname, '..', 'editors', 'vscode');
|
|
@@ -609,12 +609,12 @@ function installVscode() {
|
|
|
609
609
|
console.log('\n Restart VS Code (or run "Reload Window") to see updated colors and formatting in .kreftjs files.\n');
|
|
610
610
|
}
|
|
611
611
|
|
|
612
|
-
/* ------------------------------------------------------------------
|
|
612
|
+
/* ------------------------------------------------------------------ kreft dev */
|
|
613
613
|
async function devServer(args, { production = false } = {}) {
|
|
614
614
|
const isProd = Boolean(production || process.env.APP_ENV === 'production' || process.env.NODE_ENV === 'production');
|
|
615
615
|
let port = production ? Number(process.env.PORT) || 8000 : 8000, folder = production ? 'dist' : '.', apiFile = null, delay = 0, saveApi = true, hot = !production, dbUrl = null, migrations = null, serverFolder = null, backendFolder = null, seedNew = false;
|
|
616
|
-
//
|
|
617
|
-
//
|
|
616
|
+
// kreft dev only answers this computer; --host 0.0.0.0 shares it on your network.
|
|
617
|
+
// kreft serve answers everyone (HOST in the environment can narrow it).
|
|
618
618
|
let host = production ? process.env.HOST || '0.0.0.0' : '127.0.0.1';
|
|
619
619
|
for (let i = 0; i < args.length; i++) {
|
|
620
620
|
if (args[i] === '--port' || args[i] === '-p') port = Number(args[++i]) || port;
|
|
@@ -635,7 +635,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
635
635
|
const lay = require('./layout.js').layout(folder === 'dist' || folder === '.' ? '.' : folder);
|
|
636
636
|
const root = !production && folder === '.' ? lay.frontend : path.resolve(folder);
|
|
637
637
|
if (production && !fs.existsSync(root)) {
|
|
638
|
-
console.error(` Nothing to serve in ${root}. Build first:
|
|
638
|
+
console.error(` Nothing to serve in ${root}. Build first: kreft build`);
|
|
639
639
|
process.exit(1);
|
|
640
640
|
}
|
|
641
641
|
const mockApi = apiFile && !production ? createMockApi(path.resolve(apiFile), delay, saveApi) : null;
|
|
@@ -654,7 +654,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
654
654
|
const hasBackend = Boolean(apiRoutesFile);
|
|
655
655
|
// kreftjs serve: the backend must not sit inside the folder that is served, or its files could be downloaded
|
|
656
656
|
if (production && hasBackend && (backendDir === root || backendDir.startsWith(root + path.sep))) {
|
|
657
|
-
console.error(` The backend (${backendDir}) is inside the folder being served (${root}).\n Serve the built site instead:
|
|
657
|
+
console.error(` The backend (${backendDir}) is inside the folder being served (${root}).\n Serve the built site instead: kreft build, then kreft serve dist`);
|
|
658
658
|
process.exit(1);
|
|
659
659
|
}
|
|
660
660
|
if (production && hasBackend && !process.env.APP_KEY) console.warn(' Warning: no APP_KEY in .env. Signed links (email verification) stop working on every restart.');
|
|
@@ -664,7 +664,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
664
664
|
const { createDbApi } = require('./db-api.js');
|
|
665
665
|
try {
|
|
666
666
|
db = await connect(dbUrl, { root: config.dir });
|
|
667
|
-
if (!production) { //
|
|
667
|
+
if (!production) { // kreft dev runs new migrations by itself; for kreft serve, run `kreft migrate`
|
|
668
668
|
const { migrator, installAlias } = require('./backend/index.js');
|
|
669
669
|
installAlias(); // require('kreftjs/server') and .ts files work in migrations and seeders
|
|
670
670
|
require('./backend/model.js').setBaseDir(backendDir);
|
|
@@ -693,7 +693,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
693
693
|
const n = backend.routes().length;
|
|
694
694
|
backendRoutesCount = n;
|
|
695
695
|
// queued jobs, mails and notifications run in the background here, and routes/console.js runs on schedule
|
|
696
|
-
// (QUEUE_WORKER=off or SCHEDULER=off in .env when a separate `
|
|
696
|
+
// (QUEUE_WORKER=off or SCHEDULER=off in .env when a separate `kreft queue:work` / cron does it)
|
|
697
697
|
if (db) {
|
|
698
698
|
const queue = require('./backend/queue.js');
|
|
699
699
|
const queues = String(process.env.QUEUE_NAMES || 'high,default,low').split(',').map((x) => x.trim());
|
|
@@ -709,7 +709,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
709
709
|
}
|
|
710
710
|
if (process.env.SCHEDULER !== 'off') require('./backend/schedule.js').startScheduler(backendDir);
|
|
711
711
|
}
|
|
712
|
-
backendNote = `\n Routes: ${path.relative(process.cwd(), apiRoutesFile) || 'routes/api.js'} → ${n} route${n === 1 ? '' : 's'} at /api (
|
|
712
|
+
backendNote = `\n Routes: ${path.relative(process.cwd(), apiRoutesFile) || 'routes/api.js'} → ${n} route${n === 1 ? '' : 's'} at /api (kreft route:list)`;
|
|
713
713
|
}
|
|
714
714
|
|
|
715
715
|
// ----- your own server code: server/hello.js → /api/hello (any Node.js package can be used there)
|
|
@@ -726,7 +726,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
726
726
|
console.error(` --server: ${serverFolder} does not exist.`);
|
|
727
727
|
process.exit(1);
|
|
728
728
|
}
|
|
729
|
-
// npm packages in the browser (
|
|
729
|
+
// npm packages in the browser (kreft dev only; kreft build puts them in dist/deps)
|
|
730
730
|
const npmMap = () => (production ? null : deps.importMap(project));
|
|
731
731
|
|
|
732
732
|
const types = {
|
|
@@ -739,7 +739,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
739
739
|
|
|
740
740
|
const prodHeaders = isProd ? { 'Referrer-Policy': 'same-origin', 'X-Frame-Options': 'SAMEORIGIN', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload' } : {};
|
|
741
741
|
function send(res, file, opts = {}) { // opts.seo: tags for <head> (pages from seo.js), opts.status: 404 for a page that does not exist
|
|
742
|
-
// a TypeScript module for the browser: its JavaScript (
|
|
742
|
+
// a TypeScript module for the browser: its JavaScript (kreft build writes it as .js)
|
|
743
743
|
if (!production && /\.(m|c)?ts$/i.test(file) && !file.endsWith('.d.ts')) {
|
|
744
744
|
const { transpileTs } = require('./backend/typescript.js');
|
|
745
745
|
res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8', 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff' });
|
|
@@ -759,7 +759,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
759
759
|
});
|
|
760
760
|
if (!production && type === 'text/html') {
|
|
761
761
|
let html = fs.readFileSync(file, 'utf8');
|
|
762
|
-
// PUBLIC_ values from .env, as Kreftjs.env (
|
|
762
|
+
// PUBLIC_ values from .env, as Kreftjs.env (kreft build puts them in app.js)
|
|
763
763
|
const envTag = `<script>${env.envScript(env.publicEnv())}</script>`;
|
|
764
764
|
const langHelper = require('./lang.js');
|
|
765
765
|
const langDirs = langHelper.findLangDirs(root);
|
|
@@ -899,7 +899,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
899
899
|
if (cur.mtimeMs === prev.mtimeMs) return; // not a real change (the file may not exist at all)
|
|
900
900
|
const before = process.env.DATABASE_URL;
|
|
901
901
|
env.loadEnv(envDir);
|
|
902
|
-
console.log(' .env changed' + (process.env.DATABASE_URL !== before ? ' (restart
|
|
902
|
+
console.log(' .env changed' + (process.env.DATABASE_URL !== before ? ' (restart kreft dev to use the new DATABASE_URL)' : ''));
|
|
903
903
|
clients.forEach((c) => c.write(`data: ${JSON.stringify({ type: 'reload', path: '/.env' })}\n\n`));
|
|
904
904
|
});
|
|
905
905
|
// files a browser must never get: the database itself, its settings and password, hidden files, locks, dumps
|
|
@@ -914,7 +914,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
914
914
|
try { seo = await require('./backend/seo-pages.js').resolve(backendDir, urlPath, req); } catch (e) { console.log(' routes/seo.js: ' + (e && e.message || e)); }
|
|
915
915
|
}
|
|
916
916
|
// a crawler or link preview gets the page already drawn (and a 404 when the app shows its 404 page)
|
|
917
|
-
if (ssr.wants(req) && (!seo || seo.status === 200) && !fs.readFileSync(file, 'utf8').includes('data-kreft-ssg')) { // a page
|
|
917
|
+
if (ssr.wants(req) && (!seo || seo.status === 200) && !fs.readFileSync(file, 'utf8').includes('data-kreft-ssg')) { // a page kreft build --prerender already drew is sent as it is
|
|
918
918
|
const page = await ssr.render(urlPath, search);
|
|
919
919
|
if (page) {
|
|
920
920
|
res.writeHead(page.status, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': isProd ? 'no-cache' : 'no-store', 'Vary': 'User-Agent', 'X-Content-Type-Options': 'nosniff', ...prodHeaders });
|
|
@@ -960,7 +960,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
960
960
|
}
|
|
961
961
|
if (urlPath.startsWith('/__kreft/npm/')) {
|
|
962
962
|
const file = deps.packageFile(project, urlPath.slice('/__kreft/npm/'.length));
|
|
963
|
-
if (!file) { res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); return res.end('Not in node_modules: ' + urlPath.slice(11) + ' (
|
|
963
|
+
if (!file) { res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); return res.end('Not in node_modules: ' + urlPath.slice(11) + ' (kreft add ' + (urlPath.slice(11).match(deps.NAME) || [''])[0] + ')'); }
|
|
964
964
|
return send(res, file);
|
|
965
965
|
}
|
|
966
966
|
const spec = urlPath.slice('/__kreft/deps/'.length).replace(/\/$/, '');
|
|
@@ -973,7 +973,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
973
973
|
const privateDirs = [serverDir, ...(hasBackend ? ['app', 'routes', 'database', 'config', 'storage'].map((d) => path.join(backendDir, d)) : []), path.join(root, 'config')];
|
|
974
974
|
const insideServerDir = (file) => privateDirs.some((d) => file === d || file.startsWith(d + path.sep));
|
|
975
975
|
|
|
976
|
-
// kreftjs down: storage/framework/down.json → a 503 for everyone, except with the secret (
|
|
976
|
+
// kreftjs down: storage/framework/down.json → a 503 for everyone, except with the secret (kreft down --secret=…)
|
|
977
977
|
const downPath = path.join(backendDir, 'storage', 'framework', 'down.json');
|
|
978
978
|
let downCache = { at: 0, data: null };
|
|
979
979
|
const downInfo = () => {
|
|
@@ -1013,7 +1013,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
1013
1013
|
}
|
|
1014
1014
|
|
|
1015
1015
|
// DNS rebinding: a web page can make its own name (evil.example) point at 127.0.0.1 and then read everything
|
|
1016
|
-
//
|
|
1016
|
+
// kreft dev serves, with the browser believing it is the same site. A server that only answers this computer
|
|
1017
1017
|
// therefore only answers the names of this computer (add more with KREFT_ALLOWED_HOSTS=my-tunnel.example).
|
|
1018
1018
|
const hostOnly = (h) => String(h || '').toLowerCase().replace(/:\d+$/, '').replace(/^\[|\]$/g, '');
|
|
1019
1019
|
const isLocalName = (n) => n === 'localhost' || n === '127.0.0.1' || n === '::1' || n.endsWith('.localhost');
|
|
@@ -1097,7 +1097,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
1097
1097
|
}
|
|
1098
1098
|
if (hot && urlPath.startsWith('/__kreft/')) return devFiles(req, res, urlPath);
|
|
1099
1099
|
if (adminDir && (urlPath === '/admin' || urlPath.startsWith('/admin/'))) return adminFiles(req, res, urlPath);
|
|
1100
|
-
// /storage/…: files on the public disk (backend/storage/app/public), connected by
|
|
1100
|
+
// /storage/…: files on the public disk (backend/storage/app/public), connected by kreft storage:link.
|
|
1101
1101
|
// They are sent as downloads of plain data: an uploaded .html or .svg can't run scripts on your site.
|
|
1102
1102
|
if (urlPath.startsWith('/storage/')) {
|
|
1103
1103
|
const pub = path.join(backendDir, 'storage', 'app', 'public');
|
|
@@ -1185,7 +1185,7 @@ async function devServer(args, { production = false } = {}) {
|
|
|
1185
1185
|
|
|
1186
1186
|
/* ------------------------------------------------------------------ settings: kreftjs.config.json
|
|
1187
1187
|
* { "db": "sqlite:database/app.db", "tables": { "posts": true, "users": { "write": false, "hidden": ["password"] } } }
|
|
1188
|
-
* Looked for in the folder being served, then its parents (so `
|
|
1188
|
+
* Looked for in the folder being served, then its parents (so `kreft serve dist` finds the project's file).
|
|
1189
1189
|
* DATABASE_URL in the environment wins over "db" — put real passwords there, not in the file.
|
|
1190
1190
|
*/
|
|
1191
1191
|
function readConfig(start) { return require('./kreftjs-config.js').readConfig(start); }
|
|
@@ -1195,7 +1195,7 @@ function migrationsDir(...dirs) {
|
|
|
1195
1195
|
}
|
|
1196
1196
|
const describeDb = (url) => url.replace(/\/\/([^:@/]+):[^@/]*@/, '//$1:•••@');
|
|
1197
1197
|
|
|
1198
|
-
/* ------------------------------------------------------------------
|
|
1198
|
+
/* ------------------------------------------------------------------ kreft db */
|
|
1199
1199
|
async function dbCommand(args) {
|
|
1200
1200
|
const [sub = 'help', ...rest] = args;
|
|
1201
1201
|
let dbUrl = null, folder = '.', migrations = null;
|
|
@@ -1211,10 +1211,10 @@ async function dbCommand(args) {
|
|
|
1211
1211
|
dbUrl = dbUrl || process.env.DATABASE_URL || config.db;
|
|
1212
1212
|
if (sub === 'help' || !['migrate', 'seed', 'tables', 'sql'].includes(sub)) {
|
|
1213
1213
|
console.log(`
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1214
|
+
kreft db migrate The same as kreft migrate
|
|
1215
|
+
kreft db seed The same as kreft db:seed
|
|
1216
|
+
kreft db tables List tables and columns
|
|
1217
|
+
kreft db sql "<SQL>" Run one statement and print the result
|
|
1218
1218
|
|
|
1219
1219
|
The database comes from --db, DATABASE_URL or "db" in kreftjs.config.json.
|
|
1220
1220
|
`);
|
|
@@ -1225,7 +1225,7 @@ async function dbCommand(args) {
|
|
|
1225
1225
|
let db;
|
|
1226
1226
|
try {
|
|
1227
1227
|
db = await connect(dbUrl, { root: config.dir });
|
|
1228
|
-
if (sub === 'migrate' || sub === 'seed') { // the same as
|
|
1228
|
+
if (sub === 'migrate' || sub === 'seed') { // the same as kreft migrate and kreft db:seed
|
|
1229
1229
|
await db.close(); db = null;
|
|
1230
1230
|
return require('./artisan.js').commands[sub === 'migrate' ? 'migrate' : 'db:seed'](rest);
|
|
1231
1231
|
} else if (sub === 'tables') {
|
|
@@ -1236,7 +1236,7 @@ async function dbCommand(args) {
|
|
|
1236
1236
|
console.log('');
|
|
1237
1237
|
} else if (sub === 'sql') {
|
|
1238
1238
|
const sql = extra.join(' ');
|
|
1239
|
-
if (!sql.trim()) { console.error('
|
|
1239
|
+
if (!sql.trim()) { console.error(' kreft db sql "SELECT * FROM posts"'); process.exit(1); }
|
|
1240
1240
|
if (/^\s*(select|with|pragma|show|describe|explain)\b/i.test(sql)) console.table(await db.query(sql));
|
|
1241
1241
|
else { const r = await db.run(sql); console.log(` Done: ${r.changes} row${r.changes === 1 ? '' : 's'} changed.`); }
|
|
1242
1242
|
}
|
|
@@ -1248,7 +1248,7 @@ async function dbCommand(args) {
|
|
|
1248
1248
|
}
|
|
1249
1249
|
}
|
|
1250
1250
|
|
|
1251
|
-
/* ------------------------------------------------------------------
|
|
1251
|
+
/* ------------------------------------------------------------------ kreft dev --api db.json
|
|
1252
1252
|
* A small REST API for trying things out, made from a JSON file:
|
|
1253
1253
|
* { "posts": [ { "id": 1, "title": "Hello" } ], "$required": { "posts": ["title"] } }
|
|
1254
1254
|
*
|
package/bin/layout.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Where a project's parts are.
|
|
3
3
|
*
|
|
4
|
-
* New projects (
|
|
4
|
+
* New projects (kreft new) keep frontend and backend apart:
|
|
5
5
|
* frontend/ the only folder a browser can reach: index.html, pages, components, routes/web.kreftjs …
|
|
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
|
package/bin/no-auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
*
|
|
2
|
+
* kreft new my-app --no-auth
|
|
3
3
|
*
|
|
4
4
|
* A new app comes with login, registration, password reset, email verification, a profile and an admin area. This takes
|
|
5
5
|
* all of that out again, so the app starts as a plain site: the welcome page, the docs, the error monitor and the tests
|
|
@@ -24,7 +24,7 @@ const TO_REMOVE = [
|
|
|
24
24
|
|
|
25
25
|
const API = (todos) => `// Your API: every address here starts with /api.
|
|
26
26
|
// All API routes live directly in this file. Add your new routes here!
|
|
27
|
-
// See them all anytime with:
|
|
27
|
+
// See them all anytime with: kreft route:list
|
|
28
28
|
const { Route } = require('kreftjs/server')
|
|
29
29
|
${todos ? `const TodoController = require('../app/Http/Controllers/TodoController')
|
|
30
30
|
|
|
@@ -50,7 +50,7 @@ module.exports = AppServiceProvider
|
|
|
50
50
|
|
|
51
51
|
const SEEDER = (todos) => `const { Seeder } = require('kreftjs/server')
|
|
52
52
|
|
|
53
|
-
//
|
|
53
|
+
// kreft db:seed (kreft dev --seed fills a new database)
|
|
54
54
|
class DatabaseSeeder extends Seeder {
|
|
55
55
|
async run() {
|
|
56
56
|
${todos ? " await this.call(require('./TodoSeeder'))\n" : ''} }
|
|
@@ -134,7 +134,7 @@ function removeAuth(target) {
|
|
|
134
134
|
const to = html.indexOf(" Kreftjs.app('App.kreftjs')");
|
|
135
135
|
if (from >= 0 && to > from) {
|
|
136
136
|
html = html.slice(0, from) + ` const router = Kreftjs.router({
|
|
137
|
-
routes: ['routes/web.kreftjs'], // the pages are in routes/web.kreftjs; add more files:
|
|
137
|
+
routes: ['routes/web.kreftjs'], // the pages are in routes/web.kreftjs; add more files: kreft make:routes admin
|
|
138
138
|
layout: 'app', // every page goes inside layouts/app.kreftjs…
|
|
139
139
|
errors: 'errors', // error pages: errors/404.kreftjs for unknown addresses, abort(403) → errors/403.kreftjs
|
|
140
140
|
})
|
package/bin/prerender.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
|
-
*
|
|
2
|
+
* kreft build --prerender — saves every page of the router as ready HTML (static site generation).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* kreft build --prerender every route without :params
|
|
5
|
+
* kreft build --prerender /users/1,/users/2 also these addresses
|
|
6
6
|
*
|
|
7
7
|
* Each page opens in a simulated browser (jsdom), waits until its data has loaded, and the result
|
|
8
8
|
* is written to dist/<path>/index.html. Visitors and search engines get the content at once;
|
|
@@ -176,7 +176,7 @@ module.exports = async function prerender(dist, root, extra) {
|
|
|
176
176
|
try {
|
|
177
177
|
jsdom = loadJsdom(root);
|
|
178
178
|
} catch (_) {
|
|
179
|
-
throw new Error('it needs jsdom. Install it once in your project:\n\n
|
|
179
|
+
throw new Error('it needs jsdom. Install it once in your project:\n\n kreft add -D jsdom\n');
|
|
180
180
|
}
|
|
181
181
|
const { JSDOM } = jsdom;
|
|
182
182
|
if (typeof fetch !== 'function') throw new Error('it needs Node.js 18 or newer.');
|
package/bin/server-routes.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* Export get, post, put, patch and del (delete is a reserved word), or one `handle` for every method.
|
|
18
18
|
* What you return goes back as JSON (nothing → 204). ctx.fail(status, message or { field: [...] })
|
|
19
19
|
* stops with an error. ctx.res is Node's response, if you need it (stream, a file, a redirect).
|
|
20
|
-
*
|
|
20
|
+
* kreft dev picks up changes to these files by itself.
|
|
21
21
|
*/
|
|
22
22
|
const fs = require('fs');
|
|
23
23
|
const path = require('path');
|
|
@@ -62,7 +62,7 @@ function createServerRoutes(dir, { db = null, production = false, prefix = '/api
|
|
|
62
62
|
return out.sort((a, b) => a.dynamic - b.dynamic); // /users/me before /users/[id]
|
|
63
63
|
};
|
|
64
64
|
const list = () => {
|
|
65
|
-
//
|
|
65
|
+
// kreft dev looks for new files twice a second at most; kreft serve reads the folder once
|
|
66
66
|
if (!scannedAt || (!production && Date.now() - scannedAt > 500)) { routes = scan(); scannedAt = Date.now(); }
|
|
67
67
|
return routes;
|
|
68
68
|
};
|
package/bin/ssr.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Whole pages for crawlers and link previews: the server answers them with the page already drawn.
|
|
3
3
|
*
|
|
4
4
|
* A search engine or a chat app that does not run JavaScript would see an empty <div id="app">. For those
|
|
5
|
-
* visitors the server opens the page in a simulated browser (the same one `
|
|
5
|
+
* visitors the server opens the page in a simulated browser (the same one `kreft build --prerender` uses),
|
|
6
6
|
* waits until its data has loaded, and answers with the result. Everyone else gets the normal page.
|
|
7
7
|
*
|
|
8
8
|
* KREFT_SSR=bots (default) crawlers and link-preview bots only
|
|
@@ -45,7 +45,7 @@ function create({ root, origin, log = () => { }, cached = true }) {
|
|
|
45
45
|
if (jsdom || unavailable) return jsdom;
|
|
46
46
|
try { jsdom = require('./prerender.js').loadJsdom(root); } catch (_) {
|
|
47
47
|
unavailable = true;
|
|
48
|
-
if (mode !== 'off') log(' SSR for crawlers needs jsdom:
|
|
48
|
+
if (mode !== 'off') log(' SSR for crawlers needs jsdom: kreft add -D jsdom (crawlers get the normal page until then)');
|
|
49
49
|
}
|
|
50
50
|
return jsdom;
|
|
51
51
|
}
|
package/bin/terminal.js
CHANGED
|
@@ -167,7 +167,7 @@ function renderDevBanner({
|
|
|
167
167
|
lines.push(` ${c.dim('•')} ${c.bold('Database:')} ${c.green(database)}`);
|
|
168
168
|
}
|
|
169
169
|
if (routesCount > 0) {
|
|
170
|
-
lines.push(` ${c.dim('•')} ${c.bold('API:')} ${c.cyan(routesCount + ' routes')} ${c.dim('(
|
|
170
|
+
lines.push(` ${c.dim('•')} ${c.bold('API:')} ${c.cyan(routesCount + ' routes')} ${c.dim('(kreft route:list)')}`);
|
|
171
171
|
}
|
|
172
172
|
if (serverRoutesCount > 0) {
|
|
173
173
|
lines.push(` ${c.dim('•')} ${c.bold('Server:')} ${c.cyan(serverRoutesCount + ' custom endpoints')}`);
|
package/bin/to-ts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// JavaScript backend files → TypeScript (
|
|
1
|
+
// JavaScript backend files → TypeScript (kreft new --ts, and kreftjs make:* in a TypeScript project):
|
|
2
2
|
// require → import, module.exports → export default. Inline require('./X') stays (it works at run time).
|
|
3
3
|
function toTs(code) {
|
|
4
4
|
return code
|