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/README.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
**A small reactive framework with its own component language, and a full Node.js backend to go with it.**
|
|
4
4
|
|
|
5
|
-
You write components in `.
|
|
5
|
+
You write components in `.kreft` files in plain words: `state`, `derive`, `action`. The same project also gives you routing, controllers, an Active Record ORM, migrations, queues, mail, sign-in pages with two-factor codes, and a development server with hot reload that keeps your state.
|
|
6
6
|
|
|
7
7
|
📖 **[Read the documentation](docs/README.md)** · run `npm run dev` and open `/demo/docs` for the searchable version.
|
|
8
8
|
|
|
9
9
|
```html
|
|
10
|
-
<!-- Counter.
|
|
10
|
+
<!-- Counter.kreft -->
|
|
11
11
|
state count = 0
|
|
12
12
|
derive double = count * 2
|
|
13
13
|
action add() { count++ }
|
|
@@ -34,7 +34,7 @@ Open the address in the terminal (normally `http://localhost:8000`). A new proje
|
|
|
34
34
|
|
|
35
35
|
| | |
|
|
36
36
|
|---|---|
|
|
37
|
-
| **The `.
|
|
37
|
+
| **The `.kreft` language** | `state`, `derive`, `action`, `watch`, `prop`, `event`, slots, stores, `saved` data, `load` for APIs, forms with validation, animations. |
|
|
38
38
|
| **Router** | Nested routes, layouts, guards, error pages, clean URLs. |
|
|
39
39
|
| **Backend** | `Route`, controllers, form requests, policies, models with relations, soft deletes and scopes, migrations, seeders, factories, jobs, mail, notifications, cache, Redis, file storage, scheduling. |
|
|
40
40
|
| **Authentication** | Cookie sessions with CSRF protection, API tokens with permissions, device list, two-factor codes, email verification, account lockout. |
|
|
@@ -63,9 +63,9 @@ server.js, server.d.ts the backend entry point: require('kreftjs/server')
|
|
|
63
63
|
bin/ the `kreft` command and the backend modules (bin/backend/)
|
|
64
64
|
tools/ hot reload client, DevTools, Vite plugin
|
|
65
65
|
templates/app/ the project that `kreft new` creates
|
|
66
|
-
editors/vscode/ syntax colours and snippets for .
|
|
66
|
+
editors/vscode/ syntax colours and snippets for .kreft files
|
|
67
67
|
demo/ the interactive demo
|
|
68
|
-
docs/ the manual (README.md) and its viewer (Docs.
|
|
68
|
+
docs/ the manual (README.md) and its viewer (Docs.kreft)
|
|
69
69
|
test/ security regression tests: npm test
|
|
70
70
|
```
|
|
71
71
|
|
|
@@ -73,32 +73,6 @@ test/ security regression tests: npm test
|
|
|
73
73
|
|
|
74
74
|
Views are escaped, logins use HttpOnly cookies with CSRF protection, passwords use scrypt, and `kreft dev` only answers your own computer. The manual has a [Security overview](docs/README.md#security-overview) and a [Production checklist](docs/README.md#production-checklist). Run `npm test` to run the security regression tests.
|
|
75
75
|
|
|
76
|
-
## Migrating from Som.js
|
|
77
|
-
|
|
78
|
-
Kreft is Som.js under a new name: the package is `kreftjs` (version 0.0.6-beta), the command is `kreft` (`kreftjs` works too), the global is `Kreftjs` and component files end in `.kreftjs`. The old names keep working, so an existing project runs without edits. New files and everything the tools write use the new names. Move over when it suits you. (`kreft` was an earlier spelling of the new name; it is accepted in the same places.)
|
|
79
|
-
|
|
80
|
-
| Som.js | Kreft | Still works? |
|
|
81
|
-
|---|---|---|
|
|
82
|
-
| `npm i -g somjs` | `npm i -g kreftjs` | `kreftjs` is another name for `kreft`. Kreft does not install a `som` command, so it does not clash with an old Som.js (remove that with `npm rm -g somjs`). |
|
|
83
|
-
| `som dev`, `som build`, `som make:page` … | `kreft dev`, `kreft build`, `kreft make:page` … | Replace `som` with `kreftjs`. |
|
|
84
|
-
| `Counter.som` (and `Counter.kreft`) | `Counter.kreftjs` | Yes. All three extensions compile; a name that is missing with one extension is tried with the others (`layouts/app.kreftjs` finds `layouts/app.som`). |
|
|
85
|
-
| `s-if`, `s-each`, `s-model` … (every `s-` directive) | `k-if`, `k-each`, `k-model` … | Yes. Both prefixes are read in templates. |
|
|
86
|
-
| `<som-view>`, `<som-link>`, `<som-wait>`, `<som-component>`, `<som-yield>`, `<som-section>`, `<som-portal>` | `<kreft-view>`, `<kreft-link>` … | Yes. Both tag names work. |
|
|
87
|
-
| `Som` (global object) | `Kreftjs` | `Som` and `Kreft` point to the same object. `Som` is deprecated. |
|
|
88
|
-
| `Som.compileSom(source)` | `Kreftjs.compile(source)` | `compileSom` is kept as an alias. |
|
|
89
|
-
| `window.somDesktop` | `window.kreftDesktop` | Same object under both names. |
|
|
90
|
-
| `som.js`, `som.d.ts` in your project | `kreftjs.js`, `kreftjs.d.ts` | An existing `som.js` (or `kreft.js`) keeps working; `kreft update` rewrites it in place with the new runtime. |
|
|
91
|
-
| `som.config.json`, `config/som.json` | `kreftjs.config.json`, `config/kreftjs.json` | Yes: `kreft.config.json` and `som.config.json` are read too (the new name wins). |
|
|
92
|
-
| `require('somjs/server')` | `require('kreftjs/server')` | Also accepted: `kreft/server`, `@kreft/server`, `somjs/server`, `som/server`, `@somjs/server`. |
|
|
93
|
-
| `som_session`, `som_2fa`, `som_bypass` cookies | `kreft_session`, `kreft_2fa`, `kreft_bypass` | `app/Auth/Session.js` belongs to your project, so an existing project keeps its `som_` names until you edit that file. The framework's CSRF check and the maintenance-mode bypass accept both. New projects use the new names. |
|
|
94
|
-
| `SOM_ALLOWED_HOSTS`, `SOM_SSR`, `SOM_SSR_TTL`, `SOM_MCP_TOKEN`, `SOM_CLI`, `SOM_ELECTRON_DEVTOOLS` | `KREFT_…` with the same ending | Yes: the `KREFT_` name is read first, then the `SOM_` one. |
|
|
95
|
-
| `window.__SOM_ENV__`, `__SOM_DEV__`, `__SOM_LANG__`, `__SOM_VITE__` | `__KREFT_ENV__` … | Read by the runtime under both names. |
|
|
96
|
-
| `/__som/…` dev-server addresses | `/__kreft/…` | The dev server answers both. |
|
|
97
|
-
| Vite: `import som from 'somjs/vite'` | `import kreftjs from 'kreftjs/vite'` | The plugin also exports `kreft`, `som` and `somPlugin`, and handles `.kreft` and `.som` files. |
|
|
98
|
-
| VS Code extension `.som` language | `.kreftjs` language | The new extension colours `.kreft` and `.som` files as well. Run `kreft vscode` to install it. |
|
|
99
|
-
|
|
100
|
-
Kept on purpose, with a TODO for 3.1: the `som_migrations` table (renaming it would lose the migration history of every deployed database), the `som:` prefix of `saved`/`Kreftjs.storage` keys in `localStorage`, and the `som:queue:` / `som:delayed:` Redis keys of the queue. Renaming them needs a migration with a fallback read, so it waits for a minor release.
|
|
101
|
-
|
|
102
76
|
## License
|
|
103
77
|
|
|
104
78
|
Kreft is released under the [Elastic License 2.0](LICENSE). You may use it, including to build and sell your own applications, copy it and change it. You may not offer Kreft itself to others as a hosted or managed service, and you may not remove or hide its license notices.
|
package/bin/artisan-more.js
CHANGED
|
@@ -364,7 +364,9 @@ commands['sitemap'] = async function (args) {
|
|
|
364
364
|
// a route with a :param or * is a family of pages, not one address: it cannot be listed as it is
|
|
365
365
|
const add = (p) => { if (p && p.startsWith('/') && !/[:*]/.test(p) && !routes.includes(p)) routes.push(p); };
|
|
366
366
|
const webKreft = [
|
|
367
|
+
path.join(ctx.frontend, 'routes', 'web.kreft'),
|
|
367
368
|
path.join(ctx.frontend, 'routes', 'web.kreftjs'),
|
|
369
|
+
path.join(ctx.project, 'routes', 'web.kreft'),
|
|
368
370
|
path.join(ctx.project, 'routes', 'web.kreftjs'),
|
|
369
371
|
].find((f) => fs.existsSync(f));
|
|
370
372
|
|
package/bin/artisan.js
CHANGED
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
const fs = require('fs');
|
|
21
21
|
const path = require('path');
|
|
22
22
|
|
|
23
|
+
// Component files are .kreft; projects from the 0.0.x betas have .kreftjs. A path asked for as .kreft
|
|
24
|
+
// also tries .kreftjs, so every command keeps working in an older project.
|
|
25
|
+
const altExts = (f) => (/\.kreft$/.test(f) ? [f, f.replace(/\.kreft$/, '.kreftjs')] : [f]);
|
|
26
|
+
const existingKreft = (f) => altExts(f).find((x) => fs.existsSync(x)) || f;
|
|
27
|
+
|
|
23
28
|
function options(args) {
|
|
24
29
|
const flatArgs = (Array.isArray(args) ? args.flat(Infinity) : []).map((x) => (typeof x === 'string' ? x : String(x ?? '')));
|
|
25
30
|
const o = { _: [], flags: new Set() };
|
|
@@ -214,11 +219,11 @@ function write(file, content, base) {
|
|
|
214
219
|
}
|
|
215
220
|
|
|
216
221
|
const stubs = {
|
|
217
|
-
webRoutes: (name) => `// The ${name} pages. index.html loads it: Kreftjs.router({ routes: ['routes/web.
|
|
218
|
-
// Route.view(address, page) · .name() · .title() · .layout() · .middleware() — see routes/web.
|
|
222
|
+
webRoutes: (name) => `// The ${name} pages. index.html loads it: Kreftjs.router({ routes: ['routes/web.kreft', 'routes/${name}.kreft'] })
|
|
223
|
+
// Route.view(address, page) · .name() · .title() · .layout() · .middleware() — see routes/web.kreft
|
|
219
224
|
|
|
220
225
|
Route.prefix('/${name}').name('${name}.').middleware('auth').group(() => {
|
|
221
|
-
// Route.view('/', 'pages/${name}/Index.
|
|
226
|
+
// Route.view('/', 'pages/${name}/Index.kreft').name('index').title('${name[0].toUpperCase() + name.slice(1)}')
|
|
222
227
|
})
|
|
223
228
|
`,
|
|
224
229
|
apiRoutes: (name) => `// The ${name} API, a route file of its own. routes/api.js loads it with require('./${name}'), so every address starts with /api.
|
|
@@ -526,8 +531,8 @@ ${fields}
|
|
|
526
531
|
}))
|
|
527
532
|
`;
|
|
528
533
|
},
|
|
529
|
-
page: (name, endpoint, dirs = [], isTs = false) => `<!-- frontend/pages/${[...dirs, name].join('/')}.
|
|
530
|
-
layout 'layouts/app.
|
|
534
|
+
page: (name, endpoint, dirs = [], isTs = false) => `<!-- frontend/pages/${[...dirs, name].join('/')}.kreft -->
|
|
535
|
+
layout 'layouts/app.kreft'
|
|
531
536
|
${isTs ? `
|
|
532
537
|
interface ${name}Item {
|
|
533
538
|
id: number
|
|
@@ -856,7 +861,7 @@ function addApiRoute(ctx, name, dirs, { route, auth }) {
|
|
|
856
861
|
|
|
857
862
|
if (!text.includes(controllerName)) {
|
|
858
863
|
if (text.includes("require('kreftjs/server')") || text.includes("from 'kreftjs/server'")) {
|
|
859
|
-
text = text.replace(/((?:const \{[^}]+\} = require\('(?:kreftjs|kreft
|
|
864
|
+
text = text.replace(/((?:const \{[^}]+\} = require\('(?:kreftjs|kreft)\/server'\)|import \{[^}]+\} from '(?:kreftjs|kreft)\/server')[^\n]*\n)/, `$1${requireLine}`);
|
|
860
865
|
} else {
|
|
861
866
|
text = requireLine + text;
|
|
862
867
|
}
|
|
@@ -869,7 +874,7 @@ function addApiRoute(ctx, name, dirs, { route, auth }) {
|
|
|
869
874
|
}
|
|
870
875
|
|
|
871
876
|
function addSidebarLink(ctx, routeNameTag, label) {
|
|
872
|
-
const layoutFile = path.join(ctx.frontend, 'layouts', 'app.
|
|
877
|
+
const layoutFile = existingKreft(path.join(ctx.frontend, 'layouts', 'app.kreft'));
|
|
873
878
|
if (!fs.existsSync(layoutFile)) return;
|
|
874
879
|
let text = fs.readFileSync(layoutFile, 'utf8');
|
|
875
880
|
if (text.includes(`'${routeNameTag}'`) || text.includes(`"${routeNameTag}"`)) return;
|
|
@@ -884,17 +889,17 @@ function addSidebarLink(ctx, routeNameTag, label) {
|
|
|
884
889
|
}
|
|
885
890
|
|
|
886
891
|
function addWebRoute(ctx, name, dirs, { route, auth }) {
|
|
887
|
-
const routeName = route ? String(route).replace(/\.(kreftjs|kreft|
|
|
892
|
+
const routeName = route ? String(route).replace(/\.(kreftjs|kreft|js)$/, '').replace(/^[\\/]+/, '') : 'web';
|
|
888
893
|
const routeBaseName = routeName.split(/[\\/]/).pop();
|
|
889
894
|
const fileTargetName = routeBaseName === 'api' ? 'web' : routeBaseName;
|
|
890
|
-
const routeFile = path.join(ctx.frontend, 'routes', fileTargetName + '.
|
|
895
|
+
const routeFile = existingKreft(path.join(ctx.frontend, 'routes', fileTargetName + '.kreft'));
|
|
891
896
|
|
|
892
897
|
if (!fs.existsSync(routeFile)) {
|
|
893
898
|
write(routeFile, stubs.webRoutes(fileTargetName));
|
|
894
899
|
}
|
|
895
900
|
|
|
896
901
|
const endpoint = '/' + [...(dirs.length ? dirs.map((d) => snake(d).replace(/_/g, '-')) : []), plural(snake(name)).replace(/_/g, '-')].join('/');
|
|
897
|
-
const pageRel = `pages/${[...dirs, name].join('/')}.
|
|
902
|
+
const pageRel = `pages/${[...dirs, name].join('/')}.kreft`;
|
|
898
903
|
let text = fs.readFileSync(routeFile, 'utf8');
|
|
899
904
|
|
|
900
905
|
if (text.includes(`'${endpoint}'`) || text.includes(`"${endpoint}"`)) {
|
|
@@ -1007,29 +1012,29 @@ function removeApiRoute(ctx, name, dirs, { route, dryRun = false }) {
|
|
|
1007
1012
|
}
|
|
1008
1013
|
|
|
1009
1014
|
function removeWebRoute(ctx, name, dirs, { route, dryRun = false }) {
|
|
1010
|
-
const routeName = route ? String(route).replace(/\.(kreftjs|kreft|
|
|
1015
|
+
const routeName = route ? String(route).replace(/\.(kreftjs|kreft|js)$/, '').replace(/^[\\/]+/, '') : 'web';
|
|
1011
1016
|
const routeBaseName = routeName.split(/[\\/]/).pop();
|
|
1012
1017
|
const fileTargetName = routeBaseName === 'api' ? 'web' : routeBaseName;
|
|
1013
1018
|
const routeFiles = [
|
|
1014
|
-
path.join(ctx.frontend, 'routes', fileTargetName + '.
|
|
1015
|
-
path.join(ctx.project, 'frontend', 'routes', fileTargetName + '.
|
|
1016
|
-
path.join(ctx.frontend, 'routes', 'web.
|
|
1017
|
-
path.join(ctx.project, 'frontend', 'routes', 'web.
|
|
1018
|
-
path.join(ctx.project, 'routes', fileTargetName + '.
|
|
1019
|
-
path.join(ctx.project, 'routes', 'web.
|
|
1020
|
-
];
|
|
1019
|
+
path.join(ctx.frontend, 'routes', fileTargetName + '.kreft'),
|
|
1020
|
+
path.join(ctx.project, 'frontend', 'routes', fileTargetName + '.kreft'),
|
|
1021
|
+
path.join(ctx.frontend, 'routes', 'web.kreft'),
|
|
1022
|
+
path.join(ctx.project, 'frontend', 'routes', 'web.kreft'),
|
|
1023
|
+
path.join(ctx.project, 'routes', fileTargetName + '.kreft'),
|
|
1024
|
+
path.join(ctx.project, 'routes', 'web.kreft')
|
|
1025
|
+
].flatMap(altExts);
|
|
1021
1026
|
|
|
1022
1027
|
const seenFiles = new Set();
|
|
1023
1028
|
const removals = [];
|
|
1024
1029
|
|
|
1025
1030
|
const ep1 = '/' + [...(dirs.length ? dirs.map((d) => snake(d).replace(/_/g, '-')) : []), plural(snake(name)).replace(/_/g, '-')].join('/');
|
|
1026
1031
|
const ep2 = '/' + plural(snake(name)).replace(/_/g, '-');
|
|
1027
|
-
const pageRel1 = `pages/${[...dirs, name].join('/')}.
|
|
1028
|
-
const pageRel2 = `pages/${name}.
|
|
1032
|
+
const pageRel1 = `pages/${[...dirs, name].join('/')}.kreft`;
|
|
1033
|
+
const pageRel2 = `pages/${name}.kreft`;
|
|
1029
1034
|
const routeNameTag1 = [...dirs.map(snake), plural(snake(name)).replace(/_/g, '-')].join('.');
|
|
1030
1035
|
const routeNameTag2 = plural(snake(name)).replace(/_/g, '-');
|
|
1031
1036
|
|
|
1032
|
-
const matchTargets = [ep1, ep2, ep1.replace(/^\//, ''), ep2.replace(/^\//, ''), pageRel1, pageRel2, routeNameTag1, routeNameTag2];
|
|
1037
|
+
const matchTargets = [ep1, ep2, ep1.replace(/^\//, ''), ep2.replace(/^\//, ''), ...altExts(pageRel1), ...altExts(pageRel2), routeNameTag1, routeNameTag2];
|
|
1033
1038
|
|
|
1034
1039
|
for (const f of routeFiles) {
|
|
1035
1040
|
if (!f || seenFiles.has(f) || !fs.existsSync(f)) continue;
|
|
@@ -1064,10 +1069,10 @@ function removeWebRoute(ctx, name, dirs, { route, dryRun = false }) {
|
|
|
1064
1069
|
|
|
1065
1070
|
function removeSidebarLink(ctx, name, dirs, { dryRun = false }) {
|
|
1066
1071
|
const layoutFiles = [
|
|
1067
|
-
path.join(ctx.frontend, 'layouts', 'app.
|
|
1068
|
-
path.join(ctx.project, 'frontend', 'layouts', 'app.
|
|
1069
|
-
path.join(ctx.project, 'layouts', 'app.
|
|
1070
|
-
];
|
|
1072
|
+
path.join(ctx.frontend, 'layouts', 'app.kreft'),
|
|
1073
|
+
path.join(ctx.project, 'frontend', 'layouts', 'app.kreft'),
|
|
1074
|
+
path.join(ctx.project, 'layouts', 'app.kreft')
|
|
1075
|
+
].flatMap(altExts);
|
|
1071
1076
|
|
|
1072
1077
|
const seenFiles = new Set();
|
|
1073
1078
|
const removals = [];
|
|
@@ -1257,13 +1262,13 @@ const make = {
|
|
|
1257
1262
|
const auth = o.flags.has('auth');
|
|
1258
1263
|
const hasRoute = o.flags.has('r') || o.flags.has('route') || Boolean(o.route);
|
|
1259
1264
|
|
|
1260
|
-
const pageFile = path.join(ctx.frontend, 'pages', ...dirs, name + '.
|
|
1265
|
+
const pageFile = path.join(ctx.frontend, 'pages', ...dirs, name + '.kreft');
|
|
1261
1266
|
const isTs = isTsProject(pageFile);
|
|
1262
1267
|
if (write(pageFile, stubs.page(name, endpoint, dirs, isTs))) {
|
|
1263
1268
|
if (hasRoute) {
|
|
1264
1269
|
addWebRoute(ctx, name, dirs, { route: o.route, auth });
|
|
1265
1270
|
} else {
|
|
1266
|
-
console.log(dim(` Add to frontend/routes/web.
|
|
1271
|
+
console.log(dim(` Add to frontend/routes/web.kreft: Route.view('/${endpoint}', 'pages/${[...dirs, name].join('/')}.kreft')${auth ? ".middleware('auth')" : ''}`));
|
|
1267
1272
|
}
|
|
1268
1273
|
}
|
|
1269
1274
|
},
|
|
@@ -1494,7 +1499,7 @@ const make = {
|
|
|
1494
1499
|
console.log(dim(` Use in frontend: { __('auth.failed') } or { t('Sign In') }`));
|
|
1495
1500
|
},
|
|
1496
1501
|
};
|
|
1497
|
-
// kreft make:errors — the error pages (errors/404.
|
|
1502
|
+
// kreft make:errors — the error pages (errors/404.kreft, 403, 500…)
|
|
1498
1503
|
make.errors = function (args) {
|
|
1499
1504
|
const o = options(args), ctx = context(o);
|
|
1500
1505
|
const from = path.join(__dirname, '..', 'templates', 'errors');
|
|
@@ -1504,18 +1509,18 @@ make.errors = function (args) {
|
|
|
1504
1509
|
const index = path.join(o.dir ? path.resolve(o.dir) : ctx.frontend, 'index.html');
|
|
1505
1510
|
if (fs.existsSync(index)) {
|
|
1506
1511
|
const html = fs.readFileSync(index, 'utf8');
|
|
1507
|
-
if (!/errors\s*:/.test(html) && /(?:Kreftjs|Kreft
|
|
1508
|
-
fs.writeFileSync(index, html.replace(/(?:Kreftjs|Kreft
|
|
1512
|
+
if (!/errors\s*:/.test(html) && /(?:Kreftjs|Kreft)\.router\(\{/.test(html)) {
|
|
1513
|
+
fs.writeFileSync(index, html.replace(/(?:Kreftjs|Kreft)\.router\(\{/, (m) => `${m}\n errors: '${path.relative(path.dirname(index), to).split(path.sep).join('/')}', // error pages: errors/404.kreft, abort(403) → errors/403.kreft`));
|
|
1509
1514
|
console.log(` ${green('UPDATED')} ${path.relative(process.cwd(), index) || 'index.html'} (errors: '${path.relative(path.dirname(index), to).split(path.sep).join('/')}' in Kreftjs.router)`);
|
|
1510
1515
|
}
|
|
1511
1516
|
}
|
|
1512
1517
|
};
|
|
1513
1518
|
|
|
1514
|
-
// kreft make:routes admin → frontend/routes/admin.
|
|
1519
|
+
// kreft make:routes admin → frontend/routes/admin.kreft (pages), listed in Kreftjs.router({ routes: […] }) in index.html
|
|
1515
1520
|
// kreft make:routes admin --api → backend/routes/admin.js (API), loaded by routes/api.js
|
|
1516
1521
|
make.routes = function (args) {
|
|
1517
1522
|
const o = options(args), ctx = context(o);
|
|
1518
|
-
const name = String(o._[0] || '').replace(/\.(kreftjs|kreft|
|
|
1523
|
+
const name = String(o._[0] || '').replace(/\.(kreftjs|kreft|js)$/, '').replace(/[^\w-]/g, '');
|
|
1519
1524
|
if (!name) return console.error(' kreft make:routes admin (pages)\n kreft make:routes admin --api (API)');
|
|
1520
1525
|
if (o.flags.has('api')) {
|
|
1521
1526
|
const file = path.join(ctx.base, 'routes', name + '.js');
|
|
@@ -1526,20 +1531,20 @@ make.routes = function (args) {
|
|
|
1526
1531
|
const isTs = api.endsWith('.ts');
|
|
1527
1532
|
if (!text.includes(`require('./${name}')`) && !text.includes(`import './${name}'`)) {
|
|
1528
1533
|
const updated = isTs
|
|
1529
|
-
? text.replace(/(import \{[^}]*Route[^}]*\} from ['"](?:kreftjs|kreft
|
|
1530
|
-
: text.replace(/(const \{ Route \} = require\('(?:kreftjs|kreft
|
|
1534
|
+
? text.replace(/(import \{[^}]*Route[^}]*\} from ['"](?:kreftjs|kreft)\/server['"][^\n]*\n)/, `$1\n// ${name} routes (routes/${name}.ts)\nimport './${name}'\n`)
|
|
1535
|
+
: text.replace(/(const \{ Route \} = require\('(?:kreftjs|kreft)\/server'\)\n)/, `$1\n// ${name} routes (routes/${name}.js)\nrequire('./${name}')\n`);
|
|
1531
1536
|
fs.writeFileSync(api, updated !== text ? updated : text + `\n${isTs ? `import './${name}'` : `require('./${name}')`}\n`);
|
|
1532
1537
|
console.log(` ${green('UPDATED')} ${path.relative(process.cwd(), api)} (${isTs ? `import './${name}'` : `require('./${name}')`})`);
|
|
1533
1538
|
}
|
|
1534
1539
|
}
|
|
1535
1540
|
return;
|
|
1536
1541
|
}
|
|
1537
|
-
const file = path.join(ctx.frontend, 'routes', name + '.
|
|
1542
|
+
const file = path.join(ctx.frontend, 'routes', name + '.kreft');
|
|
1538
1543
|
if (!write(file, stubs.webRoutes(name))) return;
|
|
1539
1544
|
const index = path.join(ctx.frontend, 'index.html');
|
|
1540
1545
|
if (!fs.existsSync(index)) return;
|
|
1541
1546
|
const html = fs.readFileSync(index, 'utf8');
|
|
1542
|
-
const entry = `'routes/${name}.
|
|
1547
|
+
const entry = `'routes/${name}.kreft'`;
|
|
1543
1548
|
if (html.includes(entry)) return;
|
|
1544
1549
|
let next = html;
|
|
1545
1550
|
if (/routes:\s*\[\s*'routes\//.test(html)) next = html.replace(/(routes:\s*\[[^\]]*?)(\s*\])/, (_, list, end) => `${list.replace(/,\s*$/, '')}, ${entry}${end}`);
|
|
@@ -1645,7 +1650,7 @@ async function removeModel(args) {
|
|
|
1645
1650
|
// Collect candidate files to delete
|
|
1646
1651
|
const filesToDelete = new Set();
|
|
1647
1652
|
const checkFile = (p) => {
|
|
1648
|
-
|
|
1653
|
+
for (const f of altExts(p || '')) if (f && fs.existsSync(f)) filesToDelete.add(f);
|
|
1649
1654
|
};
|
|
1650
1655
|
|
|
1651
1656
|
// 1. Model
|
|
@@ -1722,17 +1727,17 @@ async function removeModel(args) {
|
|
|
1722
1727
|
|
|
1723
1728
|
// 6. Page
|
|
1724
1729
|
if (wantsPage) {
|
|
1725
|
-
checkFile(path.join(ctx.frontend, 'pages', ...otherDirs, `${name}.
|
|
1730
|
+
checkFile(path.join(ctx.frontend, 'pages', ...otherDirs, `${name}.kreft`));
|
|
1726
1731
|
if (dirs.length && dirs.join('/') !== otherDirs.join('/')) {
|
|
1727
|
-
checkFile(path.join(ctx.frontend, 'pages', ...dirs, `${name}.
|
|
1732
|
+
checkFile(path.join(ctx.frontend, 'pages', ...dirs, `${name}.kreft`));
|
|
1728
1733
|
}
|
|
1729
|
-
checkFile(path.join(ctx.frontend, 'pages', `${name}.
|
|
1730
|
-
checkFile(path.join(ctx.project, 'frontend', 'pages', ...otherDirs, `${name}.
|
|
1731
|
-
checkFile(path.join(ctx.project, 'frontend', 'pages', ...dirs, `${name}.
|
|
1732
|
-
checkFile(path.join(ctx.project, 'frontend', 'pages', `${name}.
|
|
1733
|
-
checkFile(path.join(ctx.project, 'pages', ...otherDirs, `${name}.
|
|
1734
|
-
checkFile(path.join(ctx.project, 'pages', ...dirs, `${name}.
|
|
1735
|
-
checkFile(path.join(ctx.project, 'pages', `${name}.
|
|
1734
|
+
checkFile(path.join(ctx.frontend, 'pages', `${name}.kreft`));
|
|
1735
|
+
checkFile(path.join(ctx.project, 'frontend', 'pages', ...otherDirs, `${name}.kreft`));
|
|
1736
|
+
checkFile(path.join(ctx.project, 'frontend', 'pages', ...dirs, `${name}.kreft`));
|
|
1737
|
+
checkFile(path.join(ctx.project, 'frontend', 'pages', `${name}.kreft`));
|
|
1738
|
+
checkFile(path.join(ctx.project, 'pages', ...otherDirs, `${name}.kreft`));
|
|
1739
|
+
checkFile(path.join(ctx.project, 'pages', ...dirs, `${name}.kreft`));
|
|
1740
|
+
checkFile(path.join(ctx.project, 'pages', `${name}.kreft`));
|
|
1736
1741
|
}
|
|
1737
1742
|
|
|
1738
1743
|
// 7. Migration
|
|
@@ -1868,9 +1873,6 @@ commands['remove'] = async function (args) {
|
|
|
1868
1873
|
return await remove.model(args);
|
|
1869
1874
|
};
|
|
1870
1875
|
commands['kreft:remove'] = commands['remove'];
|
|
1871
|
-
// the Som.js names
|
|
1872
|
-
commands['som:remove:model'] = remove.model;
|
|
1873
|
-
commands['som:remove'] = commands['remove'];
|
|
1874
1876
|
|
|
1875
1877
|
module.exports = { commands, stubs, make, remove, helpers: { options, context, withDb, write, nested, green, red, yellow, dim, migrationsOf, nextStamp, up, pascal, runSeed } };
|
|
1876
1878
|
// the rest of the commands (queues, schedule, cache, tinker, make:job …) add themselves here
|
package/bin/backend/config.js
CHANGED
|
@@ -145,8 +145,8 @@ class ConfigRepository {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
// 2. Load and merge kreftjs.config.json if present
|
|
148
|
-
// (kreft.config.json
|
|
149
|
-
const kreftConfigPath = ['kreftjs.config.json', 'kreft.config.json'
|
|
148
|
+
// (kreft.config.json is the earlier name, read when there is no kreftjs.config.json)
|
|
149
|
+
const kreftConfigPath = ['kreftjs.config.json', 'kreft.config.json'].map((n) => path.join(root, n)).find((p) => fs.existsSync(p)) || path.join(root, 'kreftjs.config.json');
|
|
150
150
|
if (fs.existsSync(kreftConfigPath)) {
|
|
151
151
|
try {
|
|
152
152
|
delete require.cache[require.resolve(kreftConfigPath)];
|
package/bin/backend/http.js
CHANGED
|
@@ -198,7 +198,7 @@ class Request {
|
|
|
198
198
|
return this._locale;
|
|
199
199
|
}
|
|
200
200
|
// 2. Cookie locale / lang / kreft_locale
|
|
201
|
-
const c = this.cookie('locale') || this.cookie('lang') || this.cookie('kreft_locale')
|
|
201
|
+
const c = this.cookie('locale') || this.cookie('lang') || this.cookie('kreft_locale');
|
|
202
202
|
if (c && typeof c === 'string') {
|
|
203
203
|
this._locale = c.toLowerCase().trim();
|
|
204
204
|
return this._locale;
|
|
@@ -231,7 +231,7 @@ class Request {
|
|
|
231
231
|
// CSRF token helpers
|
|
232
232
|
csrfToken() {
|
|
233
233
|
if (this._csrfToken) return this._csrfToken;
|
|
234
|
-
const cookieToken = this.cookie('XSRF-TOKEN') || this.cookie('kreft_xsrf')
|
|
234
|
+
const cookieToken = this.cookie('XSRF-TOKEN') || this.cookie('kreft_xsrf');
|
|
235
235
|
if (cookieToken) {
|
|
236
236
|
this._csrfToken = cookieToken;
|
|
237
237
|
return this._csrfToken;
|
|
@@ -247,7 +247,7 @@ class Request {
|
|
|
247
247
|
|| this.header('csrf-token')
|
|
248
248
|
|| this.input('_token')
|
|
249
249
|
|| this.input('csrf_token');
|
|
250
|
-
const expected = this.cookie('XSRF-TOKEN') || this.cookie('kreft_xsrf') || this.
|
|
250
|
+
const expected = this.cookie('XSRF-TOKEN') || this.cookie('kreft_xsrf') || this._csrfToken;
|
|
251
251
|
return same(candidate, expected);
|
|
252
252
|
}
|
|
253
253
|
|
|
@@ -538,9 +538,9 @@ class VerifyCsrfToken {
|
|
|
538
538
|
|| req.header('csrf-token')
|
|
539
539
|
|| req.input('_token')
|
|
540
540
|
|| req.input('csrf_token');
|
|
541
|
-
const expected = req.cookie('XSRF-TOKEN') || req.cookie('kreft_xsrf') || req.
|
|
541
|
+
const expected = req.cookie('XSRF-TOKEN') || req.cookie('kreft_xsrf') || req._csrfToken;
|
|
542
542
|
const bearer = req.bearerToken();
|
|
543
|
-
const hasCookieAuth = Boolean(req.cookie('kreft_session') || req.cookie('
|
|
543
|
+
const hasCookieAuth = Boolean(req.cookie('kreft_session') || req.cookie('XSRF-TOKEN') || req.cookie('kreft_xsrf'))
|
|
544
544
|
|
|
545
545
|
// If accessing via session/cookies or standard browser form requests, enforce CSRF:
|
|
546
546
|
if (hasCookieAuth || !bearer) {
|
package/bin/backend/index.js
CHANGED
|
@@ -31,8 +31,8 @@ function installAlias() {
|
|
|
31
31
|
const original = Module._resolveFilename;
|
|
32
32
|
require('./typescript.js').registerTypeScript(process.cwd()); // .ts files in routes/, app/, database/
|
|
33
33
|
Module._resolveFilename = function (request, ...rest) {
|
|
34
|
-
// kreft/server and @kreft/server are the earlier Kreft names
|
|
35
|
-
if (['kreftjs/server', '@kreftjs/server', 'kreft/server', '@kreft/server'
|
|
34
|
+
// kreft/server and @kreft/server are the earlier Kreft names: older projects still require them
|
|
35
|
+
if (['kreftjs/server', '@kreftjs/server', 'kreft/server', '@kreft/server'].includes(request)) return SERVER_ENTRY;
|
|
36
36
|
return original.call(this, request, ...rest);
|
|
37
37
|
};
|
|
38
38
|
}
|
package/bin/backend/mcp.js
CHANGED
|
@@ -159,16 +159,15 @@ class McpServer {
|
|
|
159
159
|
};
|
|
160
160
|
});
|
|
161
161
|
|
|
162
|
-
// 4. Resource: kreft://routes
|
|
162
|
+
// 4. Resource: kreft://routes
|
|
163
163
|
const routesResource = async () => {
|
|
164
164
|
const { Route } = require('./index.js');
|
|
165
165
|
const routes = Route.list();
|
|
166
166
|
return JSON.stringify(routes, null, 2);
|
|
167
167
|
};
|
|
168
168
|
this.registerResource('kreft://routes', 'Active Kreft Routes', 'application/json', routesResource);
|
|
169
|
-
this.registerResource('som://routes', 'Active Kreft Routes (Som.js address)', 'application/json', routesResource);
|
|
170
169
|
|
|
171
|
-
// 5. Resource: kreft://schema
|
|
170
|
+
// 5. Resource: kreft://schema
|
|
172
171
|
const schemaResource = async () => {
|
|
173
172
|
try {
|
|
174
173
|
const migrator = require('./migrator.js');
|
|
@@ -179,7 +178,6 @@ class McpServer {
|
|
|
179
178
|
}
|
|
180
179
|
};
|
|
181
180
|
this.registerResource('kreft://schema', 'Database Schema', 'application/json', schemaResource);
|
|
182
|
-
this.registerResource('som://schema', 'Database Schema (Som.js address)', 'application/json', schemaResource);
|
|
183
181
|
}
|
|
184
182
|
|
|
185
183
|
async handleMessage(reqObj) {
|
|
@@ -324,7 +322,7 @@ class McpServer {
|
|
|
324
322
|
const hostName = String(req.headers.host || '').replace(/:\d+$/, '').replace(/^\[|\]$/g, '').toLowerCase();
|
|
325
323
|
const isLocalHost = hostName === 'localhost' || hostName === '127.0.0.1' || hostName === '::1';
|
|
326
324
|
const isProd = process.env.NODE_ENV === 'production' || process.env.APP_ENV === 'production';
|
|
327
|
-
const requiredToken = process.env.MCP_TOKEN ||
|
|
325
|
+
const requiredToken = process.env.MCP_TOKEN || process.env.KREFT_MCP_TOKEN || null;
|
|
328
326
|
const deny = (status, message) => {
|
|
329
327
|
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
330
328
|
return res.end(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32000, message } }));
|
package/bin/backend/migrator.js
CHANGED
|
@@ -17,9 +17,7 @@ const { createSchema } = require('./schema.js');
|
|
|
17
17
|
const { setConnection } = require('./query.js');
|
|
18
18
|
const { forgetTables } = require('./model.js');
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
// in a migration of its own, and read the old table as a fallback until every database has moved.
|
|
22
|
-
const TABLE = 'som_migrations';
|
|
20
|
+
const TABLE = 'kreft_migrations';
|
|
23
21
|
|
|
24
22
|
async function ensureTable(db) {
|
|
25
23
|
const text = db.kind === 'postgres' ? 'TEXT' : 'VARCHAR(255)';
|
package/bin/backend/query.js
CHANGED
|
@@ -216,7 +216,7 @@ class Builder {
|
|
|
216
216
|
const { sql, params } = this.toSql();
|
|
217
217
|
const rows = await this.db.query(sql, params);
|
|
218
218
|
const maxWarn = Number(process.env.MAX_QUERY_ROWS) || 10000;
|
|
219
|
-
const isCliOrMigrate =
|
|
219
|
+
const isCliOrMigrate = process.env.KREFT_CLI === '1' || process.argv.some((a) => /artisan|migrate|seed/i.test(a));
|
|
220
220
|
const hardLimit = this._unlimited || isCliOrMigrate ? 0 : (Number(process.env.HARD_QUERY_LIMIT) || (process.env.NODE_ENV === 'production' ? 50000 : 0));
|
|
221
221
|
if (rows && rows.length >= maxWarn && this._limit == null && !this._unlimited) {
|
|
222
222
|
console.warn(`[Kreft Query Warning] Query on "${this._table}" returned ${rows.length} rows without an explicit limit(). Consider using paginate(), chunk(), or limit() to prevent high memory usage.`);
|
package/bin/backend/queue.js
CHANGED
|
@@ -219,13 +219,12 @@ async function push(kind, className, data, { delay = 0, queue } = {}) {
|
|
|
219
219
|
const payload = payloadOf(kind, className, data);
|
|
220
220
|
|
|
221
221
|
if (conn === 'redis') {
|
|
222
|
-
// TODO(3.1): the 'som:queue:' / 'som:delayed:' key names keep their Som.js spelling so waiting jobs survive the rename
|
|
223
222
|
const { Redis } = require('./redis.js');
|
|
224
223
|
const redis = Redis.connection('queue');
|
|
225
224
|
if (delay > 0) {
|
|
226
|
-
await redis.zadd('
|
|
225
|
+
await redis.zadd('kreft:delayed:' + qName, now() + Math.max(0, Number(delay) || 0), payload);
|
|
227
226
|
} else {
|
|
228
|
-
await redis.lpush('
|
|
227
|
+
await redis.lpush('kreft:queue:' + qName, payload);
|
|
229
228
|
}
|
|
230
229
|
return true;
|
|
231
230
|
}
|
|
@@ -291,17 +290,17 @@ async function workOne({ queues = ['default'], log = () => {} } = {}) {
|
|
|
291
290
|
for (const qName of targetQueues) {
|
|
292
291
|
// Move delayed jobs that became ready into the main list
|
|
293
292
|
try {
|
|
294
|
-
const due = await redis.zrangebyscore('
|
|
293
|
+
const due = await redis.zrangebyscore('kreft:delayed:' + qName, '-inf', now());
|
|
295
294
|
if (Array.isArray(due) && due.length > 0) {
|
|
296
295
|
for (const item of due) {
|
|
297
|
-
const removed = await redis.zrem('
|
|
298
|
-
if (removed) await redis.lpush('
|
|
296
|
+
const removed = await redis.zrem('kreft:delayed:' + qName, item);
|
|
297
|
+
if (removed) await redis.lpush('kreft:queue:' + qName, item);
|
|
299
298
|
}
|
|
300
299
|
}
|
|
301
300
|
} catch (_) {}
|
|
302
301
|
|
|
303
302
|
let raw = null;
|
|
304
|
-
try { raw = await redis.rpop('
|
|
303
|
+
try { raw = await redis.rpop('kreft:queue:' + qName); } catch (_) {}
|
|
305
304
|
if (!raw) continue;
|
|
306
305
|
const p = parseJson(raw);
|
|
307
306
|
const C = require('./app.js').find(p.class);
|
|
@@ -317,8 +316,8 @@ async function workOne({ queues = ['default'], log = () => {} } = {}) {
|
|
|
317
316
|
const backoff = [].concat((C && C.backoff) || 0);
|
|
318
317
|
const waitSec = Number(backoff[Math.min(attempts - 1, backoff.length - 1)] || 0);
|
|
319
318
|
const retryPayload = JSON.stringify({ ...p, attempts });
|
|
320
|
-
if (waitSec > 0) await redis.zadd('
|
|
321
|
-
else await redis.lpush('
|
|
319
|
+
if (waitSec > 0) await redis.zadd('kreft:delayed:' + qName, now() + waitSec, retryPayload);
|
|
320
|
+
else await redis.lpush('kreft:queue:' + qName, retryPayload);
|
|
322
321
|
log(` ${new Date().toISOString().slice(11, 19)} ${p.class} ${'.'.repeat(Math.max(2, 40 - p.class.length))} RETRY (${attempts}/${tries}): ${e.message}`);
|
|
323
322
|
return 'released';
|
|
324
323
|
}
|
package/bin/backend/redis.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* REDIS_PORT=6379
|
|
27
27
|
* REDIS_PASSWORD=
|
|
28
28
|
* REDIS_DB=0
|
|
29
|
-
* REDIS_PREFIX=
|
|
29
|
+
* REDIS_PREFIX=kreft:
|
|
30
30
|
*
|
|
31
31
|
* Zero mandatory dependencies: works out of the box with Node's native net module (RESP protocol).
|
|
32
32
|
* Seamlessly wraps ioredis or redis npm packages when installed.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* TypeScript support across backend and frontend:
|
|
3
3
|
* - Backend: routes/api.ts, config/*.ts, server/*.ts, and every .ts file in app/ and database/ load like .js files.
|
|
4
|
-
* - Frontend: .ts modules (e.g. frontend/logic/*.ts) and .
|
|
4
|
+
* - Frontend: .ts modules (e.g. frontend/logic/*.ts) and .kreft components with TypeScript syntax work natively in
|
|
5
5
|
* both `kreft dev` and `kreft build`.
|
|
6
6
|
*
|
|
7
7
|
* Uses `esbuild` when installed in the project, or falls back to Kreft's built-in zero-dependency TypeScript
|