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.
Files changed (92) hide show
  1. package/README.md +5 -31
  2. package/bin/artisan-more.js +2 -0
  3. package/bin/artisan.js +51 -49
  4. package/bin/backend/config.js +2 -2
  5. package/bin/backend/http.js +5 -5
  6. package/bin/backend/index.js +2 -2
  7. package/bin/backend/mcp.js +3 -5
  8. package/bin/backend/migrator.js +1 -3
  9. package/bin/backend/query.js +1 -1
  10. package/bin/backend/queue.js +8 -9
  11. package/bin/backend/redis.js +1 -1
  12. package/bin/backend/typescript.js +1 -1
  13. package/bin/build.js +23 -19
  14. package/bin/db-api.js +2 -2
  15. package/bin/db.js +4 -4
  16. package/bin/deps.js +1 -1
  17. package/bin/electron.js +3 -5
  18. package/bin/kreftjs-config.js +1 -6
  19. package/bin/kreftjs.js +34 -35
  20. package/bin/layout.js +1 -1
  21. package/bin/no-auth.js +21 -19
  22. package/bin/prerender.js +1 -1
  23. package/bin/ssr.js +3 -3
  24. package/bin/update.js +10 -10
  25. package/docs/{Docs.kreftjs → Docs.kreft} +1 -1
  26. package/docs/README.md +90 -102
  27. package/editors/vscode/README.md +3 -3
  28. package/editors/vscode/package.json +3 -4
  29. package/editors/vscode/snippets/kreftjs.json +2 -2
  30. package/kreftjs.d.ts +15 -27
  31. package/kreftjs.js +69 -79
  32. package/package.json +4 -4
  33. package/templates/app/README.md +11 -11
  34. package/templates/app/_env.example +4 -2
  35. package/templates/app/_vscode/settings.json +3 -2
  36. package/templates/app/backend/app/Http/Controllers/Admin/DashboardController.js +2 -2
  37. package/templates/app/backend/app/Http/Controllers/Admin/UserController.js +1 -1
  38. package/templates/app/backend/app/Http/Controllers/Auth/ApiTokenController.js +1 -1
  39. package/templates/app/backend/app/Http/Controllers/Auth/AuthenticatedSessionController.js +1 -1
  40. package/templates/app/backend/app/Http/Controllers/Auth/ConfirmablePasswordController.js +1 -1
  41. package/templates/app/backend/app/Http/Controllers/Auth/EmailVerificationController.js +1 -1
  42. package/templates/app/backend/app/Http/Controllers/Auth/NewPasswordController.js +1 -1
  43. package/templates/app/backend/app/Http/Controllers/Auth/PasswordResetLinkController.js +1 -1
  44. package/templates/app/backend/app/Http/Controllers/Auth/RegisteredUserController.js +1 -1
  45. package/templates/app/backend/app/Http/Controllers/Auth/SessionController.js +1 -1
  46. package/templates/app/backend/app/Http/Controllers/Auth/TwoFactorController.js +1 -1
  47. package/templates/app/backend/app/Http/Controllers/ProfileController.js +1 -1
  48. package/templates/app/backend/app/Http/Middleware/RequirePassword.js +1 -1
  49. package/templates/app/frontend/{App.kreftjs → App.kreft} +1 -1
  50. package/templates/app/frontend/components/profile/{ApiTokens.kreftjs → ApiTokens.kreft} +1 -1
  51. package/templates/app/frontend/components/profile/{Sessions.kreftjs → Sessions.kreft} +1 -1
  52. package/templates/app/frontend/components/profile/{TwoFactor.kreftjs → TwoFactor.kreft} +1 -1
  53. package/templates/app/frontend/errors/{ErrorMonitor.kreftjs → ErrorMonitor.kreft} +6 -4
  54. package/templates/app/frontend/index.html +8 -8
  55. package/templates/app/frontend/kreftjs.d.ts +15 -27
  56. package/templates/app/frontend/kreftjs.js +69 -79
  57. package/templates/app/frontend/layouts/{app.kreftjs → app.kreft} +7 -1
  58. package/templates/app/frontend/layouts/{docs.kreftjs → docs.kreft} +1 -1
  59. package/templates/app/frontend/pages/{Dashboard.kreftjs → Dashboard.kreft} +4 -2
  60. package/templates/app/frontend/pages/{ErrorMonitor.kreftjs → ErrorMonitor.kreft} +2 -2
  61. package/templates/app/frontend/pages/{Home.kreftjs → Home.kreft} +10 -9
  62. package/templates/app/frontend/pages/{Profile.kreftjs → Profile.kreft} +3 -3
  63. package/templates/app/frontend/routes/web.kreft +34 -0
  64. package/templates/app/frontend/tests/components/{Counter.kreftjs → Counter.kreft} +1 -1
  65. package/templates/app/frontend/tests/pages/About.kreft +12 -0
  66. package/templates/app/frontend/tests/pages/{Test.kreftjs → Test.kreft} +1 -1
  67. package/templates/errors/{401.kreftjs → 401.kreft} +2 -2
  68. package/templates/errors/{402.kreftjs → 402.kreft} +2 -2
  69. package/templates/errors/{403.kreftjs → 403.kreft} +2 -2
  70. package/templates/errors/{404.kreftjs → 404.kreft} +2 -2
  71. package/templates/errors/{419.kreftjs → 419.kreft} +2 -2
  72. package/templates/errors/{429.kreftjs → 429.kreft} +2 -2
  73. package/templates/errors/{500.kreftjs → 500.kreft} +2 -2
  74. package/templates/errors/{503.kreftjs → 503.kreft} +2 -2
  75. package/templates/errors/{ErrorMonitor.kreftjs → ErrorMonitor.kreft} +17 -14
  76. package/templates/errors/{error.kreftjs → error.kreft} +2 -2
  77. package/templates/errors/{minimal.kreftjs → minimal.kreft} +1 -1
  78. package/tools/dev-client.js +2 -2
  79. package/tools/vite-plugin-kreftjs.d.ts +3 -5
  80. package/tools/vite-plugin-kreftjs.js +12 -14
  81. package/templates/app/frontend/routes/web.kreftjs +0 -34
  82. package/templates/app/frontend/tests/pages/About.kreftjs +0 -12
  83. /package/templates/app/frontend/components/{Field.kreftjs → Field.kreft} +0 -0
  84. /package/templates/app/frontend/components/{StatCard.kreftjs → StatCard.kreft} +0 -0
  85. /package/templates/app/frontend/layouts/{guest.kreftjs → guest.kreft} +0 -0
  86. /package/templates/app/frontend/pages/{Users.kreftjs → Users.kreft} +0 -0
  87. /package/templates/app/frontend/pages/auth/{ConfirmPassword.kreftjs → ConfirmPassword.kreft} +0 -0
  88. /package/templates/app/frontend/pages/auth/{ForgotPassword.kreftjs → ForgotPassword.kreft} +0 -0
  89. /package/templates/app/frontend/pages/auth/{Login.kreftjs → Login.kreft} +0 -0
  90. /package/templates/app/frontend/pages/auth/{Register.kreftjs → Register.kreft} +0 -0
  91. /package/templates/app/frontend/pages/auth/{ResetPassword.kreftjs → ResetPassword.kreft} +0 -0
  92. /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 `.kreftjs` 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.
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.kreftjs -->
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 `.kreftjs` language** | `state`, `derive`, `action`, `watch`, `prop`, `event`, slots, stores, `saved` data, `load` for APIs, forms with validation, animations. |
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 .kreftjs files
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.kreftjs)
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.
@@ -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.kreftjs', 'routes/${name}.kreftjs'] })
218
- // Route.view(address, page) · .name() · .title() · .layout() · .middleware() — see routes/web.kreftjs
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.kreftjs').name('index').title('${name[0].toUpperCase() + name.slice(1)}')
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('/')}.kreftjs -->
530
- layout 'layouts/app.kreftjs'
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|somjs|som)\/server'\)|import \{[^}]+\} from '(?:kreftjs|kreft|somjs|som)\/server')[^\n]*\n)/, `$1${requireLine}`);
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.kreftjs');
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|som|js)$/, '').replace(/^[\\/]+/, '') : 'web';
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 + '.kreftjs');
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('/')}.kreftjs`;
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|som|js)$/, '').replace(/^[\\/]+/, '') : 'web';
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 + '.kreftjs'),
1015
- path.join(ctx.project, 'frontend', 'routes', fileTargetName + '.kreftjs'),
1016
- path.join(ctx.frontend, 'routes', 'web.kreftjs'),
1017
- path.join(ctx.project, 'frontend', 'routes', 'web.kreftjs'),
1018
- path.join(ctx.project, 'routes', fileTargetName + '.kreftjs'),
1019
- path.join(ctx.project, 'routes', 'web.kreftjs')
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('/')}.kreftjs`;
1028
- const pageRel2 = `pages/${name}.kreftjs`;
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.kreftjs'),
1068
- path.join(ctx.project, 'frontend', 'layouts', 'app.kreftjs'),
1069
- path.join(ctx.project, 'layouts', 'app.kreftjs')
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 + '.kreftjs');
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.kreftjs: Route.view('/${endpoint}', 'pages/${[...dirs, name].join('/')}.kreftjs')${auth ? ".middleware('auth')" : ''}`));
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.kreftjs, 403, 500…)
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|Som)\.router\(\{/.test(html)) {
1508
- fs.writeFileSync(index, html.replace(/(?:Kreftjs|Kreft|Som)\.router\(\{/, (m) => `${m}\n errors: '${path.relative(path.dirname(index), to).split(path.sep).join('/')}', // error pages: errors/404.kreftjs, abort(403) → errors/403.kreftjs`));
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.kreftjs (pages), listed in Kreftjs.router({ routes: […] }) in index.html
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|som|js)$/, '').replace(/[^\w-]/g, '');
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|somjs|som)\/server['"][^\n]*\n)/, `$1\n// ${name} routes (routes/${name}.ts)\nimport './${name}'\n`)
1530
- : text.replace(/(const \{ Route \} = require\('(?:kreftjs|kreft|somjs|som)\/server'\)\n)/, `$1\n// ${name} routes (routes/${name}.js)\nrequire('./${name}')\n`);
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 + '.kreftjs');
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}.kreftjs'`;
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
- if (p && fs.existsSync(p)) filesToDelete.add(p);
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}.kreftjs`));
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}.kreftjs`));
1732
+ checkFile(path.join(ctx.frontend, 'pages', ...dirs, `${name}.kreft`));
1728
1733
  }
1729
- checkFile(path.join(ctx.frontend, 'pages', `${name}.kreftjs`));
1730
- checkFile(path.join(ctx.project, 'frontend', 'pages', ...otherDirs, `${name}.kreftjs`));
1731
- checkFile(path.join(ctx.project, 'frontend', 'pages', ...dirs, `${name}.kreftjs`));
1732
- checkFile(path.join(ctx.project, 'frontend', 'pages', `${name}.kreftjs`));
1733
- checkFile(path.join(ctx.project, 'pages', ...otherDirs, `${name}.kreftjs`));
1734
- checkFile(path.join(ctx.project, 'pages', ...dirs, `${name}.kreftjs`));
1735
- checkFile(path.join(ctx.project, 'pages', `${name}.kreftjs`));
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
@@ -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 and som.config.json are the earlier names, read when there is no kreftjs.config.json)
149
- const kreftConfigPath = ['kreftjs.config.json', 'kreft.config.json', 'som.config.json'].map((n) => path.join(root, n)).find((p) => fs.existsSync(p)) || path.join(root, 'kreftjs.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)];
@@ -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') || this.cookie('som_locale'); // som_*: the Som.js cookie names, still read
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') || this.cookie('som_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.cookie('som_xsrf') || this._csrfToken;
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.cookie('som_xsrf') || req._csrfToken;
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('som_session') || req.cookie('XSRF-TOKEN') || req.cookie('kreft_xsrf') || req.cookie('som_xsrf')); // som_*: Som.js cookies still count as cookie auth
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) {
@@ -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; somjs/server, som/server and @somjs/server the Som.js ones: older projects still require them
35
- if (['kreftjs/server', '@kreftjs/server', 'kreft/server', '@kreft/server', 'somjs/server', 'som/server', '@somjs/server'].includes(request)) return SERVER_ENTRY;
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
  }
@@ -159,16 +159,15 @@ class McpServer {
159
159
  };
160
160
  });
161
161
 
162
- // 4. Resource: kreft://routes (som://routes is the Som.js address, still answered)
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 (som://schema likewise)
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 || (process.env.KREFT_MCP_TOKEN || process.env.SOM_MCP_TOKEN) || null;
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 } }));
@@ -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
- // TODO(3.1): the table keeps its Som.js name so deployed databases keep their history. Rename it to kreft_migrations
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)';
@@ -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 = (process.env.KREFT_CLI || process.env.SOM_CLI) === '1' || process.argv.some((a) => /artisan|migrate|seed/i.test(a));
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.`);
@@ -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('som:delayed:' + qName, now() + Math.max(0, Number(delay) || 0), payload);
225
+ await redis.zadd('kreft:delayed:' + qName, now() + Math.max(0, Number(delay) || 0), payload);
227
226
  } else {
228
- await redis.lpush('som:queue:' + qName, payload);
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('som:delayed:' + qName, '-inf', now());
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('som:delayed:' + qName, item);
298
- if (removed) await redis.lpush('som:queue:' + qName, item);
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('som:queue:' + qName); } catch (_) {}
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('som:delayed:' + qName, now() + waitSec, retryPayload);
321
- else await redis.lpush('som:queue:' + qName, retryPayload);
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
  }
@@ -26,7 +26,7 @@
26
26
  * REDIS_PORT=6379
27
27
  * REDIS_PASSWORD=
28
28
  * REDIS_DB=0
29
- * REDIS_PREFIX=som:
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 .kreftjs components with TypeScript syntax work natively in
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