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
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * Kreft v0.0.6-beta — reactive by nature
3
- * The .kreftjs component language (state · derive · action · when · watch · on),
2
+ * Kreft v0.0.9-beta — reactive by nature
3
+ * The .kreft component language (state · derive · action · when · watch · on),
4
4
  * { } views with k-if / k-each / k-model, components, slots, router, stores,
5
5
  * share / receive, event bus and a template engine.
6
6
  * One file, no build step, no virtual DOM. MIT License.
@@ -29,7 +29,7 @@
29
29
  };
30
30
  const text = () => document.createTextNode('');
31
31
  // `kreft dev` turns this on: live components are tracked for hot reload and the DevTools panel
32
- const devMode = () => !!(global.__KREFT_DEV__ || global.__SOM_DEV__);
32
+ const devMode = () => !!global.__KREFT_DEV__;
33
33
  const liveInstances = new Set();
34
34
  const BAD_PATH_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
35
35
  const getPath = (obj, path) => String(path).split('.').reduce((o, k) => (o == null || BAD_PATH_KEYS.has(k) ? undefined : o[k]), obj);
@@ -651,8 +651,8 @@
651
651
  return replacePlaceholders(chosen, fullReplace);
652
652
  }
653
653
 
654
- if (typeof window !== 'undefined' && (window.__KREFT_LANG__ || window.__SOM_LANG__)) {
655
- addTranslations(window.__KREFT_LANG__ || window.__SOM_LANG__);
654
+ if (typeof window !== 'undefined' && (window.__KREFT_LANG__)) {
655
+ addTranslations(window.__KREFT_LANG__);
656
656
  }
657
657
 
658
658
  function formatNumber(n, options, locale = currentLocaleRef.value) {
@@ -1898,9 +1898,9 @@
1898
1898
 
1899
1899
  /* ---------- <kreft-view>: the page for the current route (nested views show child routes) ----------
1900
1900
  * Layouts: the page is shown inside a layout component, in its <slot>.
1901
- * Kreftjs.router({ layout: 'app', routes }) every page uses layouts/app.kreftjs
1902
- * { path: '/admin', component: …, layout: 'admin' } this route (and its children) use layouts/admin.kreftjs
1903
- * layout admin in a page's .kreftjs file: this page uses it
1901
+ * Kreftjs.router({ layout: 'app', routes }) every page uses layouts/app.kreft
1902
+ * { path: '/admin', component: …, layout: 'admin' } this route (and its children) use layouts/admin.kreft
1903
+ * layout admin in a page's .kreft file: this page uses it
1904
1904
  * layout none / layout: false no layout
1905
1905
  * Moving between pages with the same layout keeps the layout on the page (its state stays).
1906
1906
  * ------------------------------------------------------------------------------------------- */
@@ -1953,7 +1953,7 @@
1953
1953
  const p = loadDef(def).then(() => { loaded.value++; }, (e) => {
1954
1954
  def.__failed = true;
1955
1955
  def.__failedError = e;
1956
- if (router.__errors) { console.error('[Kreft error] the page could not be loaded:', e); return router.abort(500, e && e.message); } // errors/500.kreftjs
1956
+ if (router.__errors) { console.error('[Kreft error] the page could not be loaded:', e); return router.abort(500, e && e.message); } // errors/500.kreft
1957
1957
  loaded.value++; // the page reports its own error
1958
1958
  });
1959
1959
  if (first) waitFor(p);
@@ -2028,7 +2028,7 @@
2028
2028
 
2029
2029
  /* ---------- <kreft-wait>: show something else until everything inside has loaded ----------
2030
2030
  * <kreft-wait>
2031
- * <UserList /> (its .kreftjs file, `load` data, async `on start`)
2031
+ * <UserList /> (its .kreft file, `load` data, async `on start`)
2032
2032
  * <p k-waiting>Loading…</p>
2033
2033
  * <p k-failed="error">Could not load: {error.message}</p>
2034
2034
  * </kreft-wait>
@@ -2234,20 +2234,7 @@
2234
2234
  if (open.length) throw new SyntaxError('A {for} block is missing its {/for}');
2235
2235
  return out;
2236
2236
  }
2237
- // Som.js names still work in a template: `s-if` is read as `k-if` and `<som-view>` as `<kreft-view>`.
2238
- // It runs on the finished markup, so the browser, `kreft build` and the Vite plugin all see the same names.
2239
- const LEGACY_TAGS = /^som-(?=(?:wait|view|component|yield|section|portal|link)$)/i;
2240
- const TAG_SCAN = /<(\/?)([A-Za-z][\w:.-]*)((?:\s+[^\s"'<>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'>]+))?)*)(\s*\/?>)/g;
2241
- const ATTR_SCAN = /(\s+)([^\s"'<>\/=]+)(\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'>]+))?/g;
2242
- function legacyMarkup(html) {
2243
- if (!/s-[a-z]|<\/?som-/i.test(html)) return html;
2244
- return html.replace(TAG_SCAN, (m, slash, name, attrs, end) => {
2245
- const tag = LEGACY_TAGS.test(name) ? 'kreft-' + name.slice(4) : name;
2246
- const out = attrs ? attrs.replace(ATTR_SCAN, (a, sp, an, val) => (/^s-[a-z]/i.test(an) ? sp + 'k-' + an.slice(2) + (val || '') : a)) : attrs;
2247
- return tag === name && out === attrs ? m : '<' + slash + tag + out + end;
2248
- });
2249
- }
2250
- const prepareTemplate = (src) => legacyMarkup(quoteBraceAttrs(expandBlocks(expandSelfClosing(src))));
2237
+ const prepareTemplate = (src) => quoteBraceAttrs(expandBlocks(expandSelfClosing(src)));
2251
2238
  function getTemplate(def) {
2252
2239
  if (def.__tpl) return def.__tpl;
2253
2240
  let src = def.template;
@@ -2380,7 +2367,7 @@
2380
2367
  }
2381
2368
 
2382
2369
  function mountComponent(el, def, parentScope, parentInst, options = {}) {
2383
- if (typeof def === 'string') def = kreftComponent(def); // 'components/Card.kreftjs'
2370
+ if (typeof def === 'string') def = kreftComponent(def); // 'components/Card.kreft'
2384
2371
  if (def.__loader && def.__resolved) def = def.__resolved;
2385
2372
  if (isFunction(def)) def = { init: def };
2386
2373
  if (def.__loader || (def.templateUrl && !def.__tpl)) {
@@ -2574,11 +2561,16 @@
2574
2561
  if (refName && parentInst) setRef(parentInst, refName, inst.exposed ? reactive(inst.exposed) : inst.ctx);
2575
2562
  const mountedJob = () => {
2576
2563
  inst.isMounted = true;
2577
- // run inside the component's scope, so watchers and bus listeners made here are removed with it
2578
- inst.scope.run(() => {
2579
- inst.hooks.ready.forEach((h) => safeCall(h, inst.ctx));
2580
- if (inst.keep) inst.hooks.show.forEach((h) => safeCall(h, inst.ctx)); // a kept one is shown the first time too
2581
- });
2564
+ // run inside the component's scope, so watchers and bus listeners made here are removed with it.
2565
+ // The instance is current too, so `on ready { onDestroy(() => …) }` registers its cleanup on this component
2566
+ const prevInstance = currentInstance;
2567
+ currentInstance = inst;
2568
+ try {
2569
+ inst.scope.run(() => {
2570
+ inst.hooks.ready.forEach((h) => safeCall(h, inst.ctx));
2571
+ if (inst.keep) inst.hooks.show.forEach((h) => safeCall(h, inst.ctx)); // a kept one is shown the first time too
2572
+ });
2573
+ } finally { currentInstance = prevInstance; }
2582
2574
  };
2583
2575
  if (setupDone.length) {
2584
2576
  const waitCtx = currentWait, keepCtx = currentKeep;
@@ -2743,7 +2735,7 @@
2743
2735
  if (!container) return warn(`Failed to mount app: "${target}" not found.`);
2744
2736
  if (rootInst) return warn('App has already been mounted.');
2745
2737
  // copy with descriptors so getters stay getters (computed)
2746
- let def = rootComponent; // a .kreftjs path like 'App.kreftjs', or an object
2738
+ let def = rootComponent; // a .kreft path like 'App.kreft', or an object
2747
2739
  let source = container.innerHTML;
2748
2740
  if (container.hasAttribute('data-kreft-ssg')) { // a page made by `kreft build --prerender`: its first view came ready
2749
2741
  const t = document.querySelector('template[data-kreft-ssg-source]');
@@ -2846,14 +2838,13 @@
2846
2838
 
2847
2839
  /* ================================================================
2848
2840
  * Saved data — kept in localStorage, so it survives a page reload
2849
- * .kreftjs: saved todos = [] (key: "<Component>.todos")
2841
+ * .kreft: saved todos = [] (key: "<Component>.todos")
2850
2842
  * saved theme = 'dark' as 'settings.theme'
2851
2843
  * JS: const theme = Kreftjs.saved('theme', 'dark') → a signal
2852
2844
  * Kreftjs.storage.get / set / remove / clear
2853
- * Values are stored as JSON under "som:<key>". Changes in another tab show up here too.
2845
+ * Values are stored as JSON under "kreft:<key>". Changes in another tab show up here too.
2854
2846
  * ================================================================ */
2855
- // TODO(3.1): the prefix keeps its Som.js spelling so saved data survives the rename; move it to 'kreft:' with a fallback read
2856
- const STORAGE_PREFIX = 'som:';
2847
+ const STORAGE_PREFIX = 'kreft:';
2857
2848
  const ls = () => { try { return typeof localStorage !== 'undefined' ? localStorage : null; } catch (_) { return null; } };
2858
2849
  const storage = {
2859
2850
  has(key) { const s = ls(); try { return !!s && s.getItem(STORAGE_PREFIX + key) !== null; } catch (_) { return false; } },
@@ -3048,7 +3039,7 @@
3048
3039
 
3049
3040
  /* ================================================================
3050
3041
  * load — data from an async function, with loading and error built in
3051
- * .kreftjs: load users = api.get('/api/users')
3042
+ * .kreft: load users = api.get('/api/users')
3052
3043
  * JS: const users = Kreftjs.resource(() => Kreftjs.api.get('/api/users'))
3053
3044
  * → users.data · users.loading · users.error · users.loaded · users.reload()
3054
3045
  * It loads again by itself when a value it reads changes: load user = api.get('/api/users/' + id)
@@ -3158,7 +3149,7 @@
3158
3149
  }
3159
3150
 
3160
3151
  /* ================================================================
3161
- * The .kreftjs language
3152
+ * The .kreft language
3162
3153
  *
3163
3154
  * prop start = 0
3164
3155
  * state count = start
@@ -3174,20 +3165,20 @@
3174
3165
  *
3175
3166
  * <style scoped> button { color: red } </style>
3176
3167
  *
3177
- * Load with app.component('Counter', 'components/Counter.kreftjs'),
3178
- * a route { path: '/', component: 'pages/Home.kreftjs' } or Kreftjs.load(url).
3168
+ * Load with app.component('Counter', 'components/Counter.kreft'),
3169
+ * a route { path: '/', component: 'pages/Home.kreft' } or Kreftjs.load(url).
3179
3170
  * ================================================================ */
3180
- // Component files are `.kreftjs`. Older projects have `.kreft` (an early Kreft name) or `.som` (Som.js) files:
3181
- // all three are accepted, and a name that is not found with one extension is tried with the others.
3182
- const EXT = '.kreftjs';
3183
- const LEGACY_EXTS = ['.kreft', '.som'];
3184
- const EXT_RE = /\.(?:kreftjs|kreft|som)$/i;
3185
- const COMPONENT_RE = /\.(?:kreftjs|kreft|som)(?:[?#].*)?$/i;
3171
+ // Component files are `.kreft`. Projects from the 0.0.x betas have `.kreftjs` files:
3172
+ // both are accepted, and a name that is not found with one extension is tried with the other.
3173
+ const EXT = '.kreft';
3174
+ const LEGACY_EXTS = ['.kreftjs'];
3175
+ const EXT_RE = /\.(?:kreftjs|kreft)$/i;
3176
+ const COMPONENT_RE = /\.(?:kreftjs|kreft)(?:[?#].*)?$/i;
3186
3177
  const isComponent = (s) => COMPONENT_RE.test(s);
3187
3178
  const isKreftUrl = isComponent;
3188
3179
  // the same address under the other extensions
3189
3180
  const altExts = (url) => {
3190
- const m = /^(.*?)(\.(?:kreftjs|kreft|som))([?#].*)?$/i.exec(url);
3181
+ const m = /^(.*?)(\.(?:kreftjs|kreft))([?#].*)?$/i.exec(url);
3191
3182
  return m ? [EXT, ...LEGACY_EXTS].filter((e) => e !== m[2].toLowerCase()).map((e) => m[1] + e + (m[3] || '')) : [];
3192
3183
  };
3193
3184
  // fetch() the file, or the first of its other-extension names that exists; also tells which address answered
@@ -3228,7 +3219,7 @@
3228
3219
  const hashString = (s) => { let h = 0; for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0; return (h >>> 0).toString(36); };
3229
3220
 
3230
3221
  /* ---------- a small scanner that understands strings, comments and brackets ---------- */
3231
- // the .kreftjs script part may hold regular expressions like /["']/g; views never do ({/for} must stay a block)
3222
+ // the .kreft script part may hold regular expressions like /["']/g; views never do ({/for} must stay a block)
3232
3223
  let scanScript = false;
3233
3224
  const REGEX_BEFORE = /(?:^|[(,=:[!&|?{};+\-*%<>~^]|\b(?:return|typeof|instanceof|in|of|new|delete|void|throw|case|do|else|yield|await))\s*$/;
3234
3225
  function skipLiteral(src, i) {
@@ -3634,7 +3625,7 @@
3634
3625
  }
3635
3626
  }
3636
3627
 
3637
- /* ---------- the compiler: .kreftjs source → component definition ---------- */
3628
+ /* ---------- the compiler: .kreft source → component definition ---------- */
3638
3629
  function parseKreftScript(src, url) {
3639
3630
  scanScript = true;
3640
3631
  try { return parseKreftScriptInner(src, url); } finally { scanScript = false; }
@@ -3677,7 +3668,7 @@
3677
3668
  if (m && (m[1] === 'watch' || m[1] === 'when') && /^\w+\s*\(/.test(src.slice(i))) m = null;
3678
3669
  if (m && m[1] === 'interface' && !/^interface\s+[A-Za-z_$][\w$]*/.test(src.slice(i, i + 120))) m = null;
3679
3670
  if (m && m[1] === 'type' && !/^type\s+[A-Za-z_$][\w$]*(?:\s*<[^>]*>)?\s*=(?!=)/.test(src.slice(i, i + 160))) m = null;
3680
- // `layout admin` / `layout 'layouts/admin.kreftjs'` / `layout none`: only like that, so `layout = …` stays code
3671
+ // `layout admin` / `layout 'layouts/admin.kreft'` / `layout none`: only like that, so `layout = …` stays code
3681
3672
  const lm = m && m[1] === 'layout' && src.slice(i).match(/^layout[ \t]+(?:(['"])([^'"\n]+)\1|([\w./-]+))[ \t]*;?[ \t]*(?=\n|$|\/\/)/);
3682
3673
  if (m && m[1] === 'layout' && !lm) m = null;
3683
3674
  if (lm) { layout = lm[2] || lm[3]; if (layout === 'none' || layout === 'false') layout = false; i += lm[0].length; continue; }
@@ -3844,7 +3835,7 @@
3844
3835
  return { code: out.join('\n'), imports, props, events, layout };
3845
3836
  }
3846
3837
 
3847
- // a CSS file imported by a .kreftjs file: one <link> for the whole page, before the components' own styles
3838
+ // a CSS file imported by a .kreft file: one <link> for the whole page, before the components' own styles
3848
3839
  function addStylesheet(href) {
3849
3840
  if (typeof document === 'undefined') return;
3850
3841
  const full = new URL(href, document.baseURI).href;
@@ -3857,14 +3848,14 @@
3857
3848
  document.head.insertBefore(link, first);
3858
3849
  }
3859
3850
 
3860
- // load `import` lines once per file; .kreftjs imports become local components
3851
+ // load `import` lines once per file; .kreft imports become local components
3861
3852
  async function resolveImports(list, url) {
3862
3853
  const values = {}, components = {};
3863
3854
  for (const stmt of list) {
3864
3855
  const m = stmt.match(/^import\s+(?:([\s\S]+?)\s+from\s+)?(['"])(.+?)\2/);
3865
3856
  if (!m) { warn(`Can't read import: ${stmt}`); continue; }
3866
3857
  const [, what, , spec] = m;
3867
- if (/^(?:kreftjs|kreft|som)(\.js)?$/.test(spec)) continue; // Kreft's own functions are always available
3858
+ if (/^(?:kreftjs|kreft)(\.js)?$/.test(spec)) continue; // Kreft's own functions are always available
3868
3859
  const abs = /^\.{0,2}\//.test(spec) ? new URL(spec, url).href : spec;
3869
3860
  if (isKreftUrl(spec)) {
3870
3861
  if (what && IDENT.test(what.trim())) components[what.trim()] = kreftComponent(abs);
@@ -4072,15 +4063,15 @@
4072
4063
  return st;
4073
4064
  }
4074
4065
 
4075
- // Pure text work, shared by the browser and `kreft build`: .kreftjs source → its parts
4076
- function splitKreft(source, url = 'inline.kreftjs') {
4066
+ // Pure text work, shared by the browser and `kreft build`: .kreft source → its parts
4067
+ function splitKreft(source, url = 'inline.kreft') {
4077
4068
  const name = (url.split('/').pop() || '').replace(/[?#].*$/, '').replace(EXT_RE, '');
4078
4069
  source = source.replace(/\r\n?/g, '\n');
4079
4070
  // Safe linear extraction of <view> ... </view> to prevent ReDoS catastrophic backtracking
4080
4071
  const viewOpenIdx = source.search(/<view(\s[^>]*)?>/i);
4081
4072
  const viewCloseIdx = source.toLowerCase().lastIndexOf('</view>');
4082
4073
  if (viewOpenIdx === -1 || viewCloseIdx === -1 || viewCloseIdx <= viewOpenIdx) {
4083
- throw new SyntaxError(`${name}.kreftjs has no <view> … </view>`);
4074
+ throw new SyntaxError(`${name}.kreft has no <view> … </view>`);
4084
4075
  }
4085
4076
  const viewOpenTagMatch = source.slice(viewOpenIdx).match(/^<view(\s[^>]*)?>/i);
4086
4077
  const viewContentStart = viewOpenIdx + viewOpenTagMatch[0].length;
@@ -4120,12 +4111,12 @@
4120
4111
  return def;
4121
4112
  }
4122
4113
 
4123
- async function compileKreft(source, url = 'inline.kreftjs') {
4114
+ async function compileKreft(source, url = 'inline.kreft') {
4124
4115
  const sp = splitKreft(source, url);
4125
4116
  const { values, components } = await resolveImports(sp.imports, url);
4126
4117
  let run;
4127
4118
  try { run = new Function('$scope', '$h', `with ($scope) {\n${sp.code}\n}`); }
4128
- catch (e) { throw new SyntaxError(`${sp.name}.kreftjs: ${e.message}`); }
4119
+ catch (e) { throw new SyntaxError(`${sp.name}.kreft: ${e.message}`); }
4129
4120
 
4130
4121
  const props = {};
4131
4122
  for (const p in sp.props) {
@@ -4270,20 +4261,20 @@
4270
4261
 
4271
4262
  // mode 'history' (default): clean URLs like /users/5 — the server must send index.html for every path.
4272
4263
  // mode 'hash': URLs like #/users/5 — works with any server, even file://.
4273
- // Error pages: errors/404.kreftjs, errors/403.kreftjs, errors/500.kreftjs …
4264
+ // Error pages: errors/404.kreft, errors/403.kreft, errors/500.kreft …
4274
4265
  // (Kreftjs.router({ errors: 'errors' }) turns them on; `kreft make:errors` writes them into a project)
4275
4266
  const ERROR_PAGES = [401, 402, 403, 404, 419, 429, 500, 503];
4276
4267
  const ERROR_TITLES = { 400: 'Bad Request', 401: 'Unauthorized', 402: 'Payment Required', 403: 'Forbidden', 404: 'Not Found', 405: 'Method Not Allowed', 409: 'Conflict', 410: 'Gone', 419: 'Page Expired', 422: 'Unprocessable Content', 429: 'Too Many Requests', 500: 'Server Error', 502: 'Bad Gateway', 503: 'Service Unavailable', 504: 'Gateway Timeout' };
4277
4268
  let activeRouter = null;
4278
4269
 
4279
- /* ---------- route files: routes/web.kreftjs ----------
4280
- * Route.view('/', 'pages/Home.kreftjs').name('home').title('Home')
4281
- * Route.view('/users/:id', 'pages/User.kreftjs') // :id reaches the page as a prop
4282
- * Route.middleware('auth').group(() => { Route.view('/dashboard', 'pages/Dashboard.kreftjs') })
4283
- * Route.layout('guest').middleware('guest').group(() => { Route.view('/login', 'pages/auth/Login.kreftjs') })
4270
+ /* ---------- route files: routes/web.kreft ----------
4271
+ * Route.view('/', 'pages/Home.kreft').name('home').title('Home')
4272
+ * Route.view('/users/:id', 'pages/User.kreft') // :id reaches the page as a prop
4273
+ * Route.middleware('auth').group(() => { Route.view('/dashboard', 'pages/Dashboard.kreft') })
4274
+ * Route.layout('guest').middleware('guest').group(() => { Route.view('/login', 'pages/auth/Login.kreft') })
4284
4275
  * Route.prefix('/admin').name('admin.').group(() => { … })
4285
4276
  * Route.redirect('/home', '/')
4286
- * Route.view('/settings', 'pages/Settings.kreftjs').children(() => { Route.view('profile', 'pages/settings/Profile.kreftjs') })
4277
+ * Route.view('/settings', 'pages/Settings.kreft').children(() => { Route.view('profile', 'pages/settings/Profile.kreft') })
4287
4278
  */
4288
4279
  const routeFiles = new Map(); // absolute URL → function (Route) { … } (kreft build puts them in app.js)
4289
4280
  function defineRoutes(path, fn) { routeFiles.set(new URL(path, document.baseURI).href, fn); }
@@ -4397,7 +4388,7 @@
4397
4388
  }
4398
4389
 
4399
4390
  function createRouter({ routes = [], mode = 'history', base, scrollToTop = true, layout = null, layouts = null, errors = null, middleware = {} } = {}) {
4400
- // routes: 'routes/web.kreftjs', or ['routes/web.kreftjs', 'routes/admin.kreftjs'], or the route objects themselves
4391
+ // routes: 'routes/web.kreft', or ['routes/web.kreft', 'routes/admin.kreft'], or the route objects themselves
4401
4392
  const files = [].concat(routes).filter((r) => typeof r === 'string');
4402
4393
  const inline = [].concat(routes).filter((r) => r && typeof r === 'object');
4403
4394
  const middlewares = { ...middleware };
@@ -4539,7 +4530,7 @@
4539
4530
  if (!fromBrowser) setURL(target.fullPath, replace);
4540
4531
  Object.assign(route, target, { error: null });
4541
4532
  const rec = target.matched;
4542
- if (!rec && errorsDir) { // no route for this address: errors/404.kreftjs
4533
+ if (!rec && errorsDir) { // no route for this address: errors/404.kreft
4543
4534
  showError(404, '');
4544
4535
  afterHooks.forEach((h) => { try { h(route, from); } catch (e) { console.error(e); } });
4545
4536
  return readyResolve();
@@ -4563,10 +4554,10 @@
4563
4554
  readyResolve();
4564
4555
  }
4565
4556
 
4566
- // shows errors/<status>.kreftjs in place of the page; the address stays as it is
4557
+ // shows errors/<status>.kreft in place of the page; the address stays as it is
4567
4558
  function showError(status, message) {
4568
4559
  status = Number(status) || 500;
4569
- const file = ERROR_PAGES.includes(status) ? status : 'error'; // errors/error.kreftjs for any other status
4560
+ const file = ERROR_PAGES.includes(status) ? status : 'error'; // errors/error.kreft for any other status
4570
4561
  const component = kreftComponent(`${errorsDir}/${file}${EXT}`);
4571
4562
  route.error = { status, message: message || '' };
4572
4563
  levels.value = [{ component, isError: true, props: { status, message: message || '', title: ERROR_TITLES[status] || 'Error' } }];
@@ -4635,7 +4626,7 @@
4635
4626
  };
4636
4627
  return router;
4637
4628
  }
4638
- // abort(404) in any .kreftjs file or in JavaScript
4629
+ // abort(404) in any .kreft file or in JavaScript
4639
4630
  const abortPage = (status, message) => { if (activeRouter) activeRouter.abort(status, message); else warn('abort() needs a router.'); };
4640
4631
  const currentRouter = () => receive(ROUTER_KEY, null);
4641
4632
  const currentRoute = () => { const r = receive(ROUTER_KEY, null); return r && r.route; };
@@ -4959,7 +4950,7 @@
4959
4950
  Object.keys(kept).forEach((k) => { if (hasOwn(raw, k) && keepable(raw[k])) s[k] = kept[k]; });
4960
4951
  });
4961
4952
  }
4962
- // a .kreftjs file changed: compile it again and swap every live copy (false = reload the page instead)
4953
+ // a .kreft file changed: compile it again and swap every live copy (false = reload the page instead)
4963
4954
  async function hotUpdate(url, source) {
4964
4955
  const abs = new URL(url, document.baseURI).href;
4965
4956
  const entry = kreftCache.get(abs);
@@ -5032,29 +5023,29 @@
5032
5023
  stores, storeLog, busLog,
5033
5024
  };
5034
5025
 
5035
- const VERSION = '0.0.6-beta';
5026
+ const VERSION = '0.0.9-beta';
5036
5027
  const Kreftjs = {
5037
5028
  version: VERSION,
5038
5029
  // PUBLIC_ values from .env (kreft dev and kreft build put them here); never secrets
5039
- get env() { return Object.freeze({ ...((typeof window !== 'undefined' && (window.__KREFT_ENV__ || window.__SOM_ENV__)) || {}) }); },
5030
+ get env() { return Object.freeze({ ...((typeof window !== 'undefined' && window.__KREFT_ENV__) || {}) }); },
5040
5031
  get isVite() {
5041
- return Boolean(typeof window !== 'undefined' && (window.__KREFT_VITE__ || window.__SOM_VITE__));
5032
+ return Boolean(typeof window !== 'undefined' && window.__KREFT_VITE__);
5042
5033
  },
5043
5034
  get isElectron() {
5044
5035
  return Boolean(
5045
5036
  typeof window !== 'undefined' && (
5046
- window.kreftDesktop || window.somDesktop ||
5037
+ window.kreftDesktop ||
5047
5038
  (window.process && window.process.type === 'renderer') ||
5048
5039
  (typeof navigator !== 'undefined' && /electron/i.test(navigator.userAgent))
5049
5040
  )
5050
5041
  );
5051
5042
  },
5052
5043
  get desktop() {
5053
- return (typeof window !== 'undefined' && (window.kreftDesktop || window.somDesktop)) || null;
5044
+ return (typeof window !== 'undefined' && window.kreftDesktop) || null;
5054
5045
  },
5055
5046
  abort: abortPage,
5056
5047
  // apps & components
5057
- app: createApp, lazy, load, compile: compileKreft, compileSom: compileKreft, render, // compileSom: the Som.js name, kept as an alias
5048
+ app: createApp, lazy, load, compile: compileKreft, render,
5058
5049
  // used by `kreft build`, Vite plugin and the app.js it writes
5059
5050
  __makeDef: makeKreftDef,
5060
5051
  __registerDef: registerDef,
@@ -5063,7 +5054,7 @@
5063
5054
  __routes: defineRoutes,
5064
5055
  __fileHeaders: (url) => fileHeaders(new URL(url, document.baseURI).href),
5065
5056
  __npmBase: '/__kreft/npm/', // where kreft dev serves files from node_modules (CSS imports)
5066
- __import: (url) => import(url), // how built apps load the .js modules a .kreftjs file imports
5057
+ __import: (url) => import(url), // how built apps load the .js modules a .kreft file imports
5067
5058
  __precompiled: precompiled,
5068
5059
  __hot: hotUpdate,
5069
5060
  __devtools: devtools,
@@ -5098,11 +5089,10 @@
5098
5089
 
5099
5090
  // the text-only helpers the build tool reuses, so built code matches the browser exactly
5100
5091
  Object.defineProperty(Kreftjs, '__internal', {
5101
- value: { expandBlocks, prepareTemplate, legacyMarkup, splitKreft, splitInterp, exactBrace, stripBrace, interpToExpr, quoteBraceAttrs, expandSelfClosing, scopeCss, hashString, propType, simplePathRE, fnExpRE, forAliasRE, makeKreftDef, compileFn, evaluate, stripTypeScript, stripTsParams },
5092
+ value: { expandBlocks, prepareTemplate, splitKreft, splitInterp, exactBrace, stripBrace, interpToExpr, quoteBraceAttrs, expandSelfClosing, scopeCss, hashString, propType, simplePathRE, fnExpRE, forAliasRE, makeKreftDef, compileFn, evaluate, stripTypeScript, stripTsParams },
5102
5093
  });
5103
5094
 
5104
5095
  if (typeof module !== 'undefined' && module.exports) module.exports = Kreftjs;
5105
5096
  global.Kreftjs = Kreftjs;
5106
5097
  global.Kreft = Kreftjs; // the shorter name, kept as an alias
5107
- global.Som = Kreftjs; // Som.js name, kept so older code keeps working (deprecated: use Kreftjs)
5108
5098
  })(typeof window !== 'undefined' ? window : globalThis);
@@ -11,6 +11,7 @@ state remainingMinutes = 5
11
11
  state inactivityTimer = null
12
12
 
13
13
  // SVG vector icon paths (24x24)
14
+ state docs = (Kreftjs.env.PUBLIC_DOCS_URL || 'http://kreftjs.com').replace(/\/+$/, '') // where "Documentation" links go: PUBLIC_DOCS_URL in .env
14
15
  state icons = {
15
16
  dashboard: 'M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z',
16
17
  users: 'M16 19v-1a4 4 0 0 0-4-4H7a4 4 0 0 0-4 4v1M9.5 10a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7M21 19v-1a4 4 0 0 0-3-3.87M15.5 3.13a3.5 3.5 0 0 1 0 6.75',
@@ -33,7 +34,7 @@ state navGroups = [
33
34
  { label: 'Platform', links: [
34
35
  { to: 'welcome', label: 'Showcase', icon: 'showcase' },
35
36
  { to: 'error-monitor', label: 'Error Monitor', icon: 'panel' },
36
- { to: 'docs', label: 'Documentation', icon: 'docs' },
37
+ { docs: '/docs', label: 'Documentation', icon: 'docs' },
37
38
  { to: 'tests.index', label: 'Test Suite', icon: 'tests' },
38
39
  ] },
39
40
  ]
@@ -130,7 +131,12 @@ action extendSession() {
130
131
  <ul class="nav-list">
131
132
  {for link in group.links}
132
133
  <li>
134
+ <a k-if="link.docs" href={docs + link.docs} target="_blank" rel="noopener" title={collapsed ? link.label : null} class="nav-item">
135
+ <svg viewBox="0 0 24 24" aria-hidden="true" class="nav-icon"><path d={icons[link.icon] || icons.panel}></path></svg>
136
+ <span class="nav-label">{link.label}</span>
137
+ </a>
133
138
  <kreft-link
139
+ k-else
134
140
  to={link.to}
135
141
  exact-class={link.exact ? 'active-link' : 'kreft-link-exact'}
136
142
  active-class={link.exact ? 'kreft-link-active' : 'active-link'}
@@ -1,5 +1,5 @@
1
1
  <!-- The layout of the documentation page (/docs): a slim top bar and the page, without the dashboard sidebar.
2
- The route asks for it with .layout('docs') in routes/web.kreftjs. The documentation viewer (docs/Docs.kreftjs)
2
+ The route asks for it with .layout('docs') in routes/web.kreft. The documentation viewer (docs/Docs.kreft)
3
3
  assumes a bar this tall (60px) above it. -->
4
4
  <view>
5
5
  <header>
@@ -5,6 +5,8 @@
5
5
  load stats = api.get('/api/admin/stats')
6
6
  load board = api.get('/api/dashboard')
7
7
 
8
+ state docs = (Kreftjs.env.PUBLIC_DOCS_URL || 'http://kreftjs.com').replace(/\/+$/, '') // where "Documentation" links go: PUBLIC_DOCS_URL in .env
9
+
8
10
  <view>
9
11
  <div class="dashboard-page">
10
12
  <div class="dashboard-header">
@@ -110,7 +112,7 @@ load board = api.get('/api/dashboard')
110
112
  <div class="info-list">
111
113
  <div class="info-item">
112
114
  <span class="info-label">Framework Engine</span>
113
- <span class="info-value">Kreft {Kreftjs.version || '0.0.6-beta'}</span>
115
+ <span class="info-value">Kreft {Kreftjs.version || '0.0.9-beta'}</span>
114
116
  </div>
115
117
  <div class="info-item">
116
118
  <span class="info-label">Active Database</span>
@@ -128,7 +130,7 @@ load board = api.get('/api/dashboard')
128
130
 
129
131
  <div class="quick-nav-group">
130
132
  <kreft-link to="/profile" class="btn secondary sm w-full">Edit Profile Settings</kreft-link>
131
- <kreft-link to="/docs" class="btn ghost sm w-full">API Documentation</kreft-link>
133
+ <a href={docs + '/docs'} class="btn ghost sm w-full" target="_blank" rel="noopener">API Documentation</a>
132
134
  <kreft-link to="/welcome" class="btn ghost sm w-full">Landing Page Showcase</kreft-link>
133
135
  </div>
134
136
  </section>
@@ -1,8 +1,8 @@
1
1
  <!--
2
2
  ErrorMonitor Page — Interactive Kreft Diagnostic Inspector Showcase
3
- Uses Kreft's own minimalist geometric UI and authentic JavaScript/.kreftjs stack traces.
3
+ Uses Kreft's own minimalist geometric UI and authentic JavaScript/.kreft stack traces.
4
4
  -->
5
- import ErrorMonitor from '../errors/ErrorMonitor.kreftjs'
5
+ import ErrorMonitor from '../errors/ErrorMonitor.kreft'
6
6
 
7
7
  state scenario = 'method'
8
8
 
@@ -1,11 +1,12 @@
1
1
  <!-- The welcome page: what Kreft is, shown with a component that runs right here.
2
- routes/web.kreftjs gives it no layout, so the bar and the footer are part of this page. -->
2
+ routes/web.kreft gives it no layout, so the bar and the footer are part of this page. -->
3
3
 
4
4
  state name = 'Enterprise User'
5
5
  state count = 0
6
6
  state copied = false
7
7
  derive double = count * 2
8
8
 
9
+ state docs = (Kreftjs.env.PUBLIC_DOCS_URL || 'http://kreftjs.com').replace(/\/+$/, '') // where "Documentation" links go: PUBLIC_DOCS_URL in .env
9
10
  state words = [
10
11
  { word: 'state', says: 'a value that can change', to: '/docs#components-and-the-kreftjs-file' },
11
12
  { word: 'saved', says: 'a value kept after a reload', to: '/docs#saving-data-in-the-browser' },
@@ -49,7 +50,7 @@ action copy() {
49
50
  <header class="site-bar">
50
51
  <kreft-link to="/" class="site-brand"><span class="site-mark" aria-hidden="true"></span>{env.PUBLIC_APP_NAME || 'Kreft'}</kreft-link>
51
52
  <nav class="site-nav">
52
- <kreft-link to="/docs">Documentation</kreft-link>
53
+ <a href={docs + '/docs'} target="_blank" rel="noopener">Documentation</a>
53
54
  <kreft-link k-if="$store.auth.user" to="/dashboard">Dashboard</kreft-link>
54
55
  <kreft-link k-if="!$store.auth.user" to="/login">Log in</kreft-link>
55
56
  <kreft-link k-if="!$store.auth.user" to="/register" class="site-cta">Sign up</kreft-link>
@@ -77,10 +78,10 @@ action copy() {
77
78
 
78
79
  <!-- CTA Buttons -->
79
80
  <div class="cta-actions">
80
- <kreft-link to="/docs" class="pill-btn primary">Read the docs</kreft-link>
81
+ <a href={docs + '/docs'} class="pill-btn primary" target="_blank" rel="noopener">Read the docs</a>
81
82
  <kreft-link k-if="$store.auth.user" to="/dashboard" class="pill-btn ghost">Open dashboard</kreft-link>
82
83
  <kreft-link k-if="!$store.auth.user" to="/register" class="pill-btn ghost">Sign up</kreft-link>
83
- <kreft-link to="/docs#your-first-app" class="pill-btn ghost">Quick start</kreft-link>
84
+ <a href={docs + '/docs#your-first-app'} class="pill-btn ghost" target="_blank" rel="noopener">Quick start</a>
84
85
  </div>
85
86
 
86
87
  <!-- Badges Row -->
@@ -102,7 +103,7 @@ action copy() {
102
103
  <span class="mac-dot yellow"></span>
103
104
  <span class="mac-dot green"></span>
104
105
  </div>
105
- <span class="tab-title">Hello.ts.kreftjs</span>
106
+ <span class="tab-title">Hello.ts.kreft</span>
106
107
  <div class="terminal-bar-end"></div>
107
108
  </div>
108
109
 
@@ -177,7 +178,7 @@ action copy() {
177
178
  <h2>The whole language fits on one screen</h2>
178
179
  <dl>
179
180
  {for w in words key w.word}
180
- <div><dt><kreft-link to={w.to}>{w.word}</kreft-link></dt><dd>{w.says}</dd></div>
181
+ <div><dt><a href={docs + w.to} target="_blank" rel="noopener">{w.word}</a></dt><dd>{w.says}</dd></div>
181
182
  {/for}
182
183
  </dl>
183
184
  </section>
@@ -186,7 +187,7 @@ action copy() {
186
187
  <h2>Also in the box</h2>
187
188
  <ul>
188
189
  {for p in parts key p.title}
189
- <li><kreft-link to={p.to}><strong>{p.title}</strong><span>{p.text}</span></kreft-link></li>
190
+ <li><a href={docs + p.to} target="_blank" rel="noopener"><strong>{p.title}</strong><span>{p.text}</span></a></li>
190
191
  {/for}
191
192
  </ul>
192
193
  </section>
@@ -197,12 +198,12 @@ action copy() {
197
198
  <code>kreft new my-app --db sqlite</code>
198
199
  <button class="sm" @click={copy} aria-label="Copy the command">{copied ? 'Copied' : 'Copy'}</button>
199
200
  </div>
200
- <p>Then <code>cd my-app</code> and <code>npm run dev</code>. Coming from Vue? The <kreft-link to="/docs">docs</kreft-link> have a table with the Kreft name for every Vue feature.</p>
201
+ <p>Then <code>cd my-app</code> and <code>npm run dev</code>. Coming from Vue? The <a href={docs + '/docs'} target="_blank" rel="noopener">docs</a> have a table with the Kreft name for every Vue feature.</p>
201
202
  </section>
202
203
 
203
204
  <footer class="site-foot">
204
205
  <span>Kreft {Kreftjs.version}</span>
205
- <kreft-link to="/docs">Docs</kreft-link>
206
+ <a href={docs + '/docs'} target="_blank" rel="noopener">Docs</a>
206
207
  <kreft-link to="/error-monitor">Error monitor</kreft-link>
207
208
  </footer>
208
209
  </div>
@@ -1,8 +1,8 @@
1
1
  <!-- /profile: name, email, password, two-factor, devices, API tokens, or delete the account
2
2
  (app/Http/Controllers/ProfileController.js and Auth/…) -->
3
- import TwoFactor from '../components/profile/TwoFactor.kreftjs'
4
- import Sessions from '../components/profile/Sessions.kreftjs'
5
- import ApiTokens from '../components/profile/ApiTokens.kreftjs'
3
+ import TwoFactor from '../components/profile/TwoFactor.kreft'
4
+ import Sessions from '../components/profile/Sessions.kreft'
5
+ import ApiTokens from '../components/profile/ApiTokens.kreft'
6
6
  state info = form(
7
7
  { name: $store.auth.user ? $store.auth.user.name : '', email: $store.auth.user ? $store.auth.user.email : '' },
8
8
  { name: 'required|max:255', email: 'required|email|max:255' },
@@ -0,0 +1,34 @@
1
+ // Application Web Routes.
2
+ // Developers can attach custom auth guards directly: .middleware('auth', 'admin') or custom guards.
3
+
4
+ // Public Welcome & Showcase Page (shows first with links to Sign Up, Log In, Docs)
5
+ Route.view('/', 'pages/Home.kreft').layout('none').name('home').title('Welcome')
6
+ Route.view('/welcome', 'pages/Home.kreft').layout('none').name('welcome').title('Welcome')
7
+
8
+ // Protected Dashboard & management pages (requires signed-in user)
9
+ Route.middleware('auth').group(() => {
10
+ Route.view('/dashboard', 'pages/Dashboard.kreft').name('dashboard').title('Dashboard')
11
+ Route.view('/users', 'pages/Users.kreft').name('users').title('Users')
12
+ Route.view('/profile', 'pages/Profile.kreft').name('profile').title('Profile')
13
+ })
14
+
15
+ // Authentication routes (pages/auth/), wrapped in guest layout
16
+ Route.layout('guest').group(() => {
17
+ Route.middleware('guest').group(() => {
18
+ Route.view('/login', 'pages/auth/Login.kreft').name('login').title('Log In')
19
+ Route.view('/register', 'pages/auth/Register.kreft').name('register').title('Register')
20
+ Route.view('/forgot-password', 'pages/auth/ForgotPassword.kreft').name('password.request').title('Forgot Password')
21
+ })
22
+ Route.view('/reset-password/:token', 'pages/auth/ResetPassword.kreft').name('password.reset').title('Reset Password')
23
+ Route.view('/verify-email/:id?/:hash?', 'pages/auth/VerifyEmail.kreft').name('verification.notice').title('Verify Email')
24
+ Route.view('/confirm-password', 'pages/auth/ConfirmPassword.kreft').name('password.confirm').title('Confirm Password').middleware('auth')
25
+ })
26
+
27
+ // Documentation (docs/) and test suite (tests/)
28
+ Route.view('/docs', 'docs/Docs.kreft').name('docs').title('Documentation').layout('docs') // its own slim bar, not the dashboard sidebar
29
+ Route.view('/error-monitor', 'pages/ErrorMonitor.kreft').name('error-monitor').title('Error Monitor')
30
+ Route.prefix('/tests').name('tests.').group(() => {
31
+ Route.view('/', 'tests/pages/Test.kreft').name('index').title('Test Suite')
32
+ Route.view('/about', 'tests/pages/About.kreft').name('about').title('About · Tests')
33
+ })
34
+
@@ -1,4 +1,4 @@
1
- <!-- A test component: a button that counts. Used by tests/pages/Test.kreftjs as <counter>. -->
1
+ <!-- A test component: a button that counts. Used by tests/pages/Test.kreft as <counter>. -->
2
2
  prop start = 0
3
3
 
4
4
  state count = start