kreftjs 0.0.7-beta → 0.0.10-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 +6 -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 +24 -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 +18 -18
- 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 +102 -104
- 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 +82 -79
- package/package.json +4 -4
- package/templates/app/README.md +10 -10
- package/templates/app/_env.example +7 -6
- 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 +2 -2
- package/templates/app/backend/app/Http/Controllers/Auth/PasswordResetLinkController.js +1 -1
- package/templates/app/backend/app/Http/Controllers/Auth/RegisteredUserController.js +2 -2
- 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 +2 -2
- package/templates/app/backend/app/Http/Middleware/RequirePassword.js +1 -1
- package/templates/app/backend/lang/ar/validation.json +2 -2
- package/templates/app/backend/lang/en/validation.json +2 -2
- 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} +3 -3
- package/templates/app/frontend/index.html +8 -8
- package/templates/app/frontend/kreftjs.d.ts +15 -27
- package/templates/app/frontend/kreftjs.js +82 -79
- package/templates/app/frontend/layouts/{docs.kreftjs → docs.kreft} +1 -1
- package/templates/app/frontend/pages/{Dashboard.kreftjs → Dashboard.kreft} +1 -1
- package/templates/app/frontend/pages/{ErrorMonitor.kreftjs → ErrorMonitor.kreft} +2 -2
- package/templates/app/frontend/pages/{Home.kreftjs → Home.kreft} +2 -2
- package/templates/app/frontend/pages/{Profile.kreftjs → Profile.kreft} +4 -4
- package/templates/app/frontend/pages/auth/{Register.kreftjs → Register.kreft} +1 -1
- package/templates/app/frontend/pages/auth/{ResetPassword.kreftjs → ResetPassword.kreft} +1 -1
- 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/app/lang/ar/validation.json +1 -1
- package/templates/app/lang/en/validation.json +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} +13 -12
- 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/{app.kreftjs → app.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/{VerifyEmail.kreftjs → VerifyEmail.kreft} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Kreft v0.0.
|
|
3
|
-
* The .
|
|
2
|
+
* Kreft v0.0.10-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 = () => !!
|
|
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__
|
|
655
|
-
addTranslations(window.__KREFT_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.
|
|
1902
|
-
* { path: '/admin', component: …, layout: 'admin' } this route (and its children) use layouts/admin.
|
|
1903
|
-
* layout admin in a page's .
|
|
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.
|
|
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 .
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
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 .
|
|
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
|
-
* .
|
|
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 "
|
|
2845
|
+
* Values are stored as JSON under "kreft:<key>". Changes in another tab show up here too.
|
|
2854
2846
|
* ================================================================ */
|
|
2855
|
-
|
|
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
|
-
* .
|
|
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 .
|
|
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.
|
|
3178
|
-
* a route { path: '/', component: 'pages/Home.
|
|
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 `.
|
|
3181
|
-
//
|
|
3182
|
-
const EXT = '.
|
|
3183
|
-
const LEGACY_EXTS = ['.
|
|
3184
|
-
const EXT_RE = /\.(?:kreftjs|kreft
|
|
3185
|
-
const COMPONENT_RE = /\.(?:kreftjs|kreft
|
|
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
|
|
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 .
|
|
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: .
|
|
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.
|
|
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 .
|
|
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; .
|
|
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
|
|
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`: .
|
|
4076
|
-
function splitKreft(source, url = 'inline.
|
|
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}.
|
|
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.
|
|
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}.
|
|
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.
|
|
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.
|
|
4280
|
-
* Route.view('/', 'pages/Home.
|
|
4281
|
-
* Route.view('/users/:id', 'pages/User.
|
|
4282
|
-
* Route.middleware('auth').group(() => { Route.view('/dashboard', 'pages/Dashboard.
|
|
4283
|
-
* Route.layout('guest').middleware('guest').group(() => { Route.view('/login', 'pages/auth/Login.
|
|
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.
|
|
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.
|
|
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.
|
|
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>.
|
|
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.
|
|
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 .
|
|
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; };
|
|
@@ -4687,6 +4678,7 @@
|
|
|
4687
4678
|
same: 'The :attribute field must match :other.',
|
|
4688
4679
|
different: 'The :attribute field and :other must be different.',
|
|
4689
4680
|
confirmed: 'The :attribute field confirmation does not match.',
|
|
4681
|
+
not_common_password: 'The :attribute is too common or too easy to guess. Choose another one.',
|
|
4690
4682
|
regex: 'The :attribute field format is invalid.',
|
|
4691
4683
|
digits: 'The :attribute field must be :digits digits.',
|
|
4692
4684
|
min: { string: 'The :attribute field must be at least :min characters.', numeric: 'The :attribute field must be at least :min.', array: 'The :attribute field must have at least :min items.' },
|
|
@@ -4696,6 +4688,17 @@
|
|
|
4696
4688
|
};
|
|
4697
4689
|
const isEmptyValue = (v) => v === undefined || v === null || (typeof v === 'string' && v.trim() === '') || (isArray(v) && !v.length);
|
|
4698
4690
|
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4691
|
+
// not_common_password: the passwords people choose most (8+ characters, so they pass `min:8`), one character repeated, a run like
|
|
4692
|
+
// 12345678 or abcdefgh, or the same as the email address. Not a replacement for a long passphrase.
|
|
4693
|
+
const COMMON_PASSWORDS = new Set('00000000 11111111 111111111 123123123 12345678 123456789 1234567890 12345678910 123456789a 123456abc 1q2w3e4r 1q2w3e4r5t 1qaz2wsx 88888888 987654321 abc12345 abcd1234 abcdefg1 abcdefgh admin123 admin1234 administrator asdfghjk asdfghjkl baseball1 changeme changeme1 dragon123 football1 freedom1 google123 hello123 hello1234 iloveyou iloveyou1 kreft123 kreftjs123 letmein1 letmein123 login123 master123 monkey123 p@ssw0rd p@ssword pass1234 passpass passw0rd password password1 password12 password123 password1234 princess1 qazwsxedc qwerty12 qwerty123 qwertyui qwertyuiop secret123 shadow123 starwars1 sunshine1 superman1 test1234 testing123 trustno1 welcome1 welcome123 whatever1 zxcvbnm1'.split(' '));
|
|
4694
|
+
const isCommonPassword = (v, data) => {
|
|
4695
|
+
const p = String(v).toLowerCase();
|
|
4696
|
+
if (COMMON_PASSWORDS.has(p) || /^(.)\1+$/.test(p)) return true;
|
|
4697
|
+
const runs = (s) => { let up = true, down = true; for (let i = 1; i < s.length; i++) { const d = s.charCodeAt(i) - s.charCodeAt(i - 1); if (d !== 1) up = false; if (d !== -1) down = false; } return up || down; };
|
|
4698
|
+
if (p.length >= 6 && runs(p)) return true;
|
|
4699
|
+
const email = data && typeof data.email === 'string' ? data.email.toLowerCase() : '';
|
|
4700
|
+
return Boolean(email) && (p === email || p === email.split('@')[0]);
|
|
4701
|
+
};
|
|
4699
4702
|
const dateOf = (v, data) => { const d = new Date(hasOwn(data, v) ? data[v] : v === 'today' ? new Date().toDateString() : v); return isNaN(d) ? null : d; };
|
|
4700
4703
|
function parseRules(rules) {
|
|
4701
4704
|
return (isArray(rules) ? rules : String(rules || '').split('|')).filter(Boolean).map((r) => {
|
|
@@ -4755,6 +4758,7 @@
|
|
|
4755
4758
|
case 'same': failed = value !== getPath(data, a); extra = { other: a.replace(/_/g, ' ') }; break;
|
|
4756
4759
|
case 'different': failed = value === getPath(data, a); extra = { other: a.replace(/_/g, ' ') }; break;
|
|
4757
4760
|
case 'confirmed': failed = value !== getPath(data, field + '_confirmation'); break;
|
|
4761
|
+
case 'not_common_password': failed = isCommonPassword(value, data); break;
|
|
4758
4762
|
case 'regex': { const m = r.arg.match(/^\/(.*)\/([a-z]*)$/); failed = !new RegExp(m ? m[1] : r.arg, m ? m[2] : '').test(String(value)); break; }
|
|
4759
4763
|
case 'digits': failed = !new RegExp(`^\\d{${Number(a)}}$`).test(String(value)); extra = { digits: a }; break;
|
|
4760
4764
|
case 'min': failed = sizeOf(value) < Number(a); extra = { min: a }; break;
|
|
@@ -4959,7 +4963,7 @@
|
|
|
4959
4963
|
Object.keys(kept).forEach((k) => { if (hasOwn(raw, k) && keepable(raw[k])) s[k] = kept[k]; });
|
|
4960
4964
|
});
|
|
4961
4965
|
}
|
|
4962
|
-
// a .
|
|
4966
|
+
// a .kreft file changed: compile it again and swap every live copy (false = reload the page instead)
|
|
4963
4967
|
async function hotUpdate(url, source) {
|
|
4964
4968
|
const abs = new URL(url, document.baseURI).href;
|
|
4965
4969
|
const entry = kreftCache.get(abs);
|
|
@@ -5032,29 +5036,29 @@
|
|
|
5032
5036
|
stores, storeLog, busLog,
|
|
5033
5037
|
};
|
|
5034
5038
|
|
|
5035
|
-
const VERSION = '0.0.
|
|
5039
|
+
const VERSION = '0.0.10-beta';
|
|
5036
5040
|
const Kreftjs = {
|
|
5037
5041
|
version: VERSION,
|
|
5038
5042
|
// PUBLIC_ values from .env (kreft dev and kreft build put them here); never secrets
|
|
5039
|
-
get env() { return Object.freeze({ ...((typeof window !== 'undefined' &&
|
|
5043
|
+
get env() { return Object.freeze({ ...((typeof window !== 'undefined' && window.__KREFT_ENV__) || {}) }); },
|
|
5040
5044
|
get isVite() {
|
|
5041
|
-
return Boolean(typeof window !== 'undefined' &&
|
|
5045
|
+
return Boolean(typeof window !== 'undefined' && window.__KREFT_VITE__);
|
|
5042
5046
|
},
|
|
5043
5047
|
get isElectron() {
|
|
5044
5048
|
return Boolean(
|
|
5045
5049
|
typeof window !== 'undefined' && (
|
|
5046
|
-
window.kreftDesktop ||
|
|
5050
|
+
window.kreftDesktop ||
|
|
5047
5051
|
(window.process && window.process.type === 'renderer') ||
|
|
5048
5052
|
(typeof navigator !== 'undefined' && /electron/i.test(navigator.userAgent))
|
|
5049
5053
|
)
|
|
5050
5054
|
);
|
|
5051
5055
|
},
|
|
5052
5056
|
get desktop() {
|
|
5053
|
-
return (typeof window !== 'undefined' &&
|
|
5057
|
+
return (typeof window !== 'undefined' && window.kreftDesktop) || null;
|
|
5054
5058
|
},
|
|
5055
5059
|
abort: abortPage,
|
|
5056
5060
|
// apps & components
|
|
5057
|
-
app: createApp, lazy, load, compile: compileKreft,
|
|
5061
|
+
app: createApp, lazy, load, compile: compileKreft, render,
|
|
5058
5062
|
// used by `kreft build`, Vite plugin and the app.js it writes
|
|
5059
5063
|
__makeDef: makeKreftDef,
|
|
5060
5064
|
__registerDef: registerDef,
|
|
@@ -5063,7 +5067,7 @@
|
|
|
5063
5067
|
__routes: defineRoutes,
|
|
5064
5068
|
__fileHeaders: (url) => fileHeaders(new URL(url, document.baseURI).href),
|
|
5065
5069
|
__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 .
|
|
5070
|
+
__import: (url) => import(url), // how built apps load the .js modules a .kreft file imports
|
|
5067
5071
|
__precompiled: precompiled,
|
|
5068
5072
|
__hot: hotUpdate,
|
|
5069
5073
|
__devtools: devtools,
|
|
@@ -5098,11 +5102,10 @@
|
|
|
5098
5102
|
|
|
5099
5103
|
// the text-only helpers the build tool reuses, so built code matches the browser exactly
|
|
5100
5104
|
Object.defineProperty(Kreftjs, '__internal', {
|
|
5101
|
-
value: { expandBlocks, prepareTemplate,
|
|
5105
|
+
value: { expandBlocks, prepareTemplate, splitKreft, splitInterp, exactBrace, stripBrace, interpToExpr, quoteBraceAttrs, expandSelfClosing, scopeCss, hashString, propType, simplePathRE, fnExpRE, forAliasRE, makeKreftDef, compileFn, evaluate, stripTypeScript, stripTsParams },
|
|
5102
5106
|
});
|
|
5103
5107
|
|
|
5104
5108
|
if (typeof module !== 'undefined' && module.exports) module.exports = Kreftjs;
|
|
5105
5109
|
global.Kreftjs = Kreftjs;
|
|
5106
5110
|
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
5111
|
})(typeof window !== 'undefined' ? window : globalThis);
|
|
@@ -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.
|
|
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>
|
|
@@ -112,7 +112,7 @@ state docs = (Kreftjs.env.PUBLIC_DOCS_URL || 'http://kreftjs.com').replace(/\/+$
|
|
|
112
112
|
<div class="info-list">
|
|
113
113
|
<div class="info-item">
|
|
114
114
|
<span class="info-label">Framework Engine</span>
|
|
115
|
-
<span class="info-value">Kreft {Kreftjs.version || '0.0.
|
|
115
|
+
<span class="info-value">Kreft {Kreftjs.version || '0.0.10-beta'}</span>
|
|
116
116
|
</div>
|
|
117
117
|
<div class="info-item">
|
|
118
118
|
<span class="info-label">Active Database</span>
|
|
@@ -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/.
|
|
3
|
+
Uses Kreft's own minimalist geometric UI and authentic JavaScript/.kreft stack traces.
|
|
4
4
|
-->
|
|
5
|
-
import ErrorMonitor from '../errors/ErrorMonitor.
|
|
5
|
+
import ErrorMonitor from '../errors/ErrorMonitor.kreft'
|
|
6
6
|
|
|
7
7
|
state scenario = 'method'
|
|
8
8
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- The welcome page: what Kreft is, shown with a component that runs right here.
|
|
2
|
-
routes/web.
|
|
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
|
|
@@ -103,7 +103,7 @@ action copy() {
|
|
|
103
103
|
<span class="mac-dot yellow"></span>
|
|
104
104
|
<span class="mac-dot green"></span>
|
|
105
105
|
</div>
|
|
106
|
-
<span class="tab-title">Hello.ts.
|
|
106
|
+
<span class="tab-title">Hello.ts.kreft</span>
|
|
107
107
|
<div class="terminal-bar-end"></div>
|
|
108
108
|
</div>
|
|
109
109
|
|
|
@@ -1,15 +1,15 @@
|
|
|
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.
|
|
4
|
-
import Sessions from '../components/profile/Sessions.
|
|
5
|
-
import ApiTokens from '../components/profile/ApiTokens.
|
|
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' },
|
|
9
9
|
)
|
|
10
10
|
state pass = form(
|
|
11
11
|
{ current_password: '', password: '', password_confirmation: '' },
|
|
12
|
-
{ current_password: 'required', password: 'required|min:8|confirmed' },
|
|
12
|
+
{ current_password: 'required', password: 'required|min:8|confirmed|not_common_password' },
|
|
13
13
|
)
|
|
14
14
|
state saved = ''
|
|
15
15
|
state passSaved = ''
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- /register: make an account (POST /api/register, app/Http/Controllers/Auth/RegisteredUserController.js) -->
|
|
2
2
|
state f = form(
|
|
3
3
|
{ name: '', email: '', password: '', password_confirmation: '' },
|
|
4
|
-
{ name: 'required|max:255', email: 'required|email|max:255', password: 'required|min:8|confirmed' },
|
|
4
|
+
{ name: 'required|max:255', email: 'required|email|max:255', password: 'required|min:8|confirmed|not_common_password' },
|
|
5
5
|
)
|
|
6
6
|
state problem = ''
|
|
7
7
|
|
|
@@ -3,7 +3,7 @@ prop token = ''
|
|
|
3
3
|
|
|
4
4
|
state f = form(
|
|
5
5
|
{ email: $route.query.email || '', password: '', password_confirmation: '' },
|
|
6
|
-
{ email: 'required|email', password: 'required|min:8|confirmed' },
|
|
6
|
+
{ email: 'required|email', password: 'required|min:8|confirmed|not_common_password' },
|
|
7
7
|
)
|
|
8
8
|
state problem = ''
|
|
9
9
|
|
|
@@ -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
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!-- /tests/about: a second test page. Add more: a .kreft file here, then one line in the routes in index.html. -->
|
|
2
|
+
<view>
|
|
3
|
+
<section class="page">
|
|
4
|
+
<h1>About</h1>
|
|
5
|
+
<p class="lead">Edit <code>tests/pages/About.kreft</code> and save: the page updates by itself.</p>
|
|
6
|
+
<p><kreft-link to="/tests">Back to the test pages</kreft-link></p>
|
|
7
|
+
</section>
|
|
8
|
+
</view>
|
|
9
|
+
|
|
10
|
+
<style scoped>
|
|
11
|
+
.page { padding: 40px 0; max-width: 40em; }
|
|
12
|
+
</style>
|
|
@@ -10,7 +10,7 @@ load hello = api.get('/api/hello', { query: { name: name || undefined } })
|
|
|
10
10
|
|
|
11
11
|
<div class="card">
|
|
12
12
|
<h2>A component</h2>
|
|
13
|
-
<p class="muted"><code>tests/components/Counter.
|
|
13
|
+
<p class="muted"><code>tests/components/Counter.kreft</code></p>
|
|
14
14
|
<counter start={1}></counter>
|
|
15
15
|
</div>
|
|
16
16
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"array": "يجب ألا يحتوي :attribute على أكثر من :max عناصر."
|
|
13
13
|
},
|
|
14
14
|
"confirmed": "تأكيد :attribute غير متطابق.",
|
|
15
|
+
"not_common_password": "كلمة :attribute شائعة جداً أو يسهل تخمينها. اختر كلمة أخرى.",
|
|
15
16
|
"attributes": {
|
|
16
17
|
"email": "البريد الإلكتروني",
|
|
17
18
|
"password": "كلمة المرور",
|
|
@@ -19,4 +20,3 @@
|
|
|
19
20
|
"title": "العنوان"
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"array": "The :attribute field must not have more than :max items."
|
|
13
13
|
},
|
|
14
14
|
"confirmed": "The :attribute confirmation does not match.",
|
|
15
|
+
"not_common_password": "The :attribute is too common or too easy to guess. Choose another one.",
|
|
15
16
|
"attributes": {
|
|
16
17
|
"email": "email address",
|
|
17
18
|
"password": "password",
|
|
@@ -19,4 +20,3 @@
|
|
|
19
20
|
"title": "title"
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
|
-
|