kreftjs 0.0.20-beta → 0.0.22-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 (64) hide show
  1. package/README.md +10 -1
  2. package/bin/artisan.js +4 -2
  3. package/bin/backend/cache.js +31 -5
  4. package/bin/backend/gc.js +193 -43
  5. package/bin/backend/http.js +6 -2
  6. package/bin/backend/index.js +6 -0
  7. package/bin/backend/mcp-client.js +133 -0
  8. package/bin/backend/mcp.js +17 -0
  9. package/bin/backend/model.js +15 -1
  10. package/bin/backend/shield.js +266 -0
  11. package/bin/backend/stream.js +74 -0
  12. package/bin/build.js +12 -5
  13. package/bin/db-api.js +9 -1
  14. package/bin/kreftjs.js +50 -12
  15. package/book/ar/16-virtual-list.md +131 -0
  16. package/book/ar/17-combobox.md +169 -0
  17. package/book/ar/18-tabs-offline.md +168 -0
  18. package/book/ar/19-wizard.md +196 -0
  19. package/book/ar/20-pipes-match-props.md +75 -0
  20. package/book/ar/README.md +12 -1
  21. package/book/kreft-book-ar.docx +0 -0
  22. package/docs/README.md +209 -13
  23. package/editors/sublime/Comments.tmPreferences +26 -0
  24. package/editors/sublime/Component.sublime-snippet +20 -0
  25. package/editors/sublime/Kreft.sublime-syntax +39 -0
  26. package/editors/sublime/LICENSE +95 -0
  27. package/editors/sublime/README.md +7 -0
  28. package/editors/sublime/action.sublime-snippet +10 -0
  29. package/editors/sublime/derive.sublime-snippet +8 -0
  30. package/editors/sublime/for.sublime-snippet +12 -0
  31. package/editors/sublime/k-case.sublime-snippet +8 -0
  32. package/editors/sublime/k-if.sublime-snippet +8 -0
  33. package/editors/sublime/k-match.sublime-snippet +12 -0
  34. package/editors/sublime/kreft-wait.sublime-snippet +12 -0
  35. package/editors/sublime/layout.sublime-snippet +8 -0
  36. package/editors/sublime/load.sublime-snippet +8 -0
  37. package/editors/sublime/on.sublime-snippet +10 -0
  38. package/editors/sublime/pipe.sublime-snippet +8 -0
  39. package/editors/sublime/prop-typed.sublime-snippet +8 -0
  40. package/editors/sublime/saved.sublime-snippet +8 -0
  41. package/editors/sublime/section.sublime-snippet +10 -0
  42. package/editors/sublime/state.sublime-snippet +8 -0
  43. package/editors/sublime/watch.sublime-snippet +10 -0
  44. package/editors/sublime/when.sublime-snippet +10 -0
  45. package/editors/sublime/yield.sublime-snippet +8 -0
  46. package/editors/vscode/snippets/kreftjs.json +143 -15
  47. package/editors/zed/LICENSE +95 -0
  48. package/editors/zed/README.md +17 -0
  49. package/editors/zed/extension.toml +11 -0
  50. package/editors/zed/languages/kreft/brackets.scm +4 -0
  51. package/editors/zed/languages/kreft/config.toml +17 -0
  52. package/editors/zed/languages/kreft/highlights.scm +18 -0
  53. package/editors/zed/languages/kreft/indents.scm +5 -0
  54. package/editors/zed/languages/kreft/injections.scm +14 -0
  55. package/editors/zed/languages/kreft/outline.scm +3 -0
  56. package/kreftjs.d.ts +1 -1
  57. package/kreftjs.js +219 -14
  58. package/package.json +3 -2
  59. package/server.d.ts +44 -0
  60. package/templates/app/backend/app/Auth/Session.js +8 -4
  61. package/templates/app/backend/app/Http/Middleware/Authenticate.js +7 -4
  62. package/templates/app/backend/database/seeders/DatabaseSeeder.js +4 -0
  63. package/templates/app/frontend/kreftjs.d.ts +1 -1
  64. package/templates/app/frontend/kreftjs.js +219 -14
@@ -0,0 +1,75 @@
1
+ # الفصل العشرون: الأنابيب و`k-match` وأنواع الخصائص
2
+
3
+ ثلاث إضافات تجعل القوالب أقصر وأوضح. كلها تعمل مع `kreft dev` ومع `kreft build` ومع سياسة CSP الصارمة، والصيغة القديمة لا تتأثر.
4
+
5
+ ## 1) الأنابيب (Pipes)
6
+
7
+ بدل استدعاء دوال مساعدة داخل القالب، مرّر القيمة عبر أنبوب بعلامة `|`:
8
+
9
+ ```html
10
+ <p>{ price | currency:'EUR' }</p>
11
+ <p>{ createdAt | ago }</p>
12
+ <p>{ title | capitalize | truncate:30 }</p>
13
+ <a title={name | upper}>{ tags | join:' · ' }</a>
14
+ ```
15
+
16
+ - الوسائط بعد نقطتين: `truncate:30` و`currency:'EUR'`، أو بين قوسين: `number(2)`.
17
+ - تُسلسل الأنابيب من اليسار إلى اليمين.
18
+ - `||` و`|=` ليستا أنبوبًا، وكذلك `|` داخل نص أو تعبير نمطي أو قوسين.
19
+ - الأرقام والتواريخ تتبع لغة التطبيق الحالية (`Kreftjs.locale`)، فيتغير شكل العملة والتاريخ مع اللغة.
20
+
21
+ **الأنابيب الجاهزة:** `upper` و`lower` و`capitalize` و`trim` و`truncate:n` و`currency:'USD'` و`number:digits` و`percent:digits` و`date:'short'|'medium'|'long'` و`time` و`ago` و`bytes` و`json` و`fallback:'قيمة'` و`join:sep` و`first` و`last` و`slice:a:b` و`reverse` و`sortBy:'key':'desc'` و`count`.
22
+
23
+ **أنبوبك الخاص:** أي دالة في المكوّن تصلح أنبوبًا، وتتقدّم على الجاهز عند تطابق الاسم:
24
+
25
+ ```js
26
+ // في سكربت المكوّن
27
+ state name = 'ali'
28
+ const wrap = (v, left = '«', right = '»') => left + v + right
29
+ ```
30
+ ```html
31
+ <p>{ name | wrap }</p> <!-- «ali» -->
32
+ <p>{ name | wrap:'[':']' }</p> <!-- [ali] -->
33
+ ```
34
+
35
+ ولتعريف أنبوب لكل التطبيق: `Kreftjs.pipe('shout', (v) => String(v).toUpperCase() + '!')`.
36
+
37
+ > **تنبيه:** `a | b` كانت تعني عملية OR على مستوى البتات. إن كنت تستخدمها في قالب فاكتبها `(a | b)` داخل دالة، فالقالب الآن يفسّرها أنبوبًا.
38
+
39
+ ## 2) `k-match` و`k-case` و`k-default`
40
+
41
+ بدل سلسلة `k-if` و`k-elif`:
42
+
43
+ ```html
44
+ <div k-match="status">
45
+ <b k-case="'ok'">تم</b>
46
+ <i k-case="'wait', 'queued'">بالانتظار</i> <!-- أكثر من قيمة -->
47
+ <u k-default>غير معروف</u>
48
+ </div>
49
+ ```
50
+
51
+ - المقارنة صارمة (`===`).
52
+ - تُعرض أول حالة مطابقة فقط، و`k-default` للحالة التي لا يطابقها شيء (يوضع آخرًا).
53
+ - الأبناء المباشرون فقط هم الحالات، ويعملون كما يعمل `k-if`: مكوّنات و`<template>` و`k-animate`.
54
+
55
+ ## 3) خصائص بأنواع
56
+
57
+ اكتب النوع بصيغة TypeScript، وتحصل على تحذير أثناء التطوير عند قيمة خاطئة أو خاصية مطلوبة غائبة:
58
+
59
+ ```html
60
+ prop tone: 'info' | 'warn' = 'info' // قيم محددة، والافتراضي info
61
+ prop label: string // مطلوبة: غيابها يحذّر
62
+ prop count?: number // اختيارية
63
+ prop items: string[] = []
64
+ prop onSave: (id: number) => void
65
+ ```
66
+
67
+ يُتحقق من: `string` و`number` و`boolean` و`null` و`undefined` و`object` و`Date` و`T[]` والدوال والقيم الحرفية (`'sm'` و`3` و`true`) والاتحاد بينها. أما أسماء أنواعك (`User`) و`any` و`unknown` فلا تُفحص.
68
+
69
+ الخصائص التي لها قيمة افتراضية أو `?` أو تقبل `null`/`undefined` ليست مطلوبة. والنوع `number` أو `boolean` أو `string` يحوّل أيضًا القيمة المكتوبة نصًا في الوسم (`count="5"` تصبح `5`).
70
+
71
+ ## تمارين
72
+
73
+ 1. اعرض سعرًا بالدينار الكويتي وتاريخ إنشاء بصيغة `ago` في صفحة طلبات.
74
+ 2. حوّل سلسلة `k-if` من أربع حالات لحالة الطلب إلى `k-match`.
75
+ 3. أضف إلى مكوّن `Badge` الخاصية `prop tone: 'info' | 'warn' | 'danger' = 'info'` وجرّب `tone="loud"` لترى التحذير.
package/book/ar/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  كتاب عملي لتتعلم **Kreft (`kreftjs`)**، الإطار التفاعلي ذو لغة المكوّنات `.kreft` والواجهة الخلفية المتكاملة على Node.js، وبناء تطبيقات الويب وسطح المكتب به.
4
4
 
5
- > **ملاحظة عن الدقة:** الأمثلة المتقدمة (الفصول 11 إلى 14) **جُرِّبت فعلًا**: شُغّل خادم Kreft، وأُرسلت طلبات حقيقية، وفُتحت الصفحات في متصفح. وما لم نستطع التحقق منه ذكرناه صراحة في موضعه. الإطار ما زال تجريبيًا (`0.0.x-beta`)، فراجع `docs/README.md` (بالإنجليزية) عند أي اختلاف مع إصدارك.
5
+ > **ملاحظة عن الدقة:** الأمثلة المتقدمة (الفصول 11 إلى 20) **جُرِّبت فعلًا**: شُغّل خادم Kreft، وأُرسلت طلبات حقيقية، وفُتحت الصفحات في متصفح. وما لم نستطع التحقق منه ذكرناه صراحة في موضعه. الإطار ما زال تجريبيًا (`0.0.x-beta`)، فراجع `docs/README.md` (بالإنجليزية) عند أي اختلاف مع إصدارك.
6
6
 
7
7
  ## الفهرس
8
8
 
@@ -33,12 +33,21 @@
33
33
 
34
34
  15. [مرجع سريع وأخطاء شائعة ومصطلحات](15-appendix.md).
35
35
 
36
+ ### الجزء الرابع: أمثلة الخبراء
37
+
38
+ 16. [قوائم ضخمة بلا تباطؤ](16-virtual-list.md): النافذة الافتراضية لعرض 100,000 صف فما فوق.
39
+ 17. [مكوّن مركّب: قائمة اختيار ذكية](17-combobox.md): Combobox ببحث ولوحة مفاتيح وبوابة و`k-model`.
40
+ 18. [مزامنة بين التبويبات ودون اتصال](18-tabs-offline.md): حضور حيّ وطابور أوامر يرسل عند عودة الاتصال.
41
+ 19. [معالج متعدد الخطوات مع استئناف](19-wizard.md): مخزن محفوظ وتحقق لكل خطوة وحراسة للمسارات.
42
+ 20. [الأنابيب و`k-match` وأنواع الخصائص](20-pipes-match-props.md): صيغة أقصر وأوضح للقوالب والمكوّنات.
43
+
36
44
  ## كيف تقرأ هذا الكتاب؟
37
45
 
38
46
  - **مبتدئ:** الفصول 1 إلى 5 بالترتيب، ونفّذ التمارين في نهاية كل فصل.
39
47
  - **تعرف JavaScript وتريد تطبيقًا كاملًا:** 4 ثم 6 ثم 7 ثم 11 و12.
40
48
  - **تريد النشر:** 9 (وانتبه إلى قسم `<base>`).
41
49
  - **تريد سطح المكتب:** 10.
50
+ - **تريد مستوى الخبير في الواجهة:** 16 إلى 20 بعد أن تتقن الفصول 2 و3 و4.
42
51
 
43
52
  ## ما الذي سنبنيه؟
44
53
 
@@ -48,6 +57,8 @@
48
57
  - متجر بسلة ودفع آمن لا يكرر الطلبات ولا يبيع ما نفد.
49
58
  - مكتبة مكوّنات (نافذة وجدول وتوجيهات).
50
59
  - لوحة أرقام حيّة وتقرير يومي مجدول.
60
+ - قائمة افتراضية تعرض 100,000 صف بلا تباطؤ.
61
+ - قائمة اختيار ذكية، ومزامنة حيّة بين التبويبات، ومعالج إعداد يستأنف بعد التحديث.
51
62
 
52
63
  ## تشغيل أمثلة الكتاب
53
64
 
Binary file
package/docs/README.md CHANGED
@@ -55,34 +55,34 @@ node --version # v22.5.0 or higher
55
55
 
56
56
  ### Install the command
57
57
 
58
- Pick one way.
58
+ Always ask npm for the newest version with `@latest`. Pick one way.
59
59
 
60
60
  **1. Once, for every project (recommended).** The `kreft` command then works in any folder (the package is called `kreftjs`):
61
61
 
62
62
  ```bash
63
- npm i -g kreftjs
63
+ npm install -g kreftjs@latest
64
64
  ```
65
65
 
66
- **2. Without installing anything.** `npx` downloads Kreft, runs it and forgets it. Good for trying it out:
66
+ **2. Inside one project.** The version is saved in the project's `package.json`, so everyone who clones the project, your server and your CI get the same one:
67
67
 
68
68
  ```bash
69
- npx kreftjs new my-app
69
+ npm install -D kreftjs@latest
70
+ npx kreftjs dev
70
71
  ```
71
72
 
72
- **3. Inside one project.** The command is saved in the project's `package.json`, so everyone who clones the project gets the same version, and so does your server or CI:
73
+ **3. Without installing anything.** `npx` downloads Kreft, runs it and forgets it. Good for trying it out:
73
74
 
74
75
  ```bash
75
- npm i -D kreftjs
76
- npx kreftjs dev
76
+ npx kreftjs@latest new my-app
77
77
  ```
78
78
 
79
79
  > [!NOTE]
80
- > The scripts of a new project (`npm run dev`, `build`, `serve` …) call `kreftjs`. With way 1 that is the global command. With way 3, npm finds it in the project's `node_modules` by itself. On a server where you did not install it globally, run `npm i -D kreftjs` in the project before `npm run build` or `npm run serve`.
80
+ > The scripts of a new project (`npm run dev`, `build`, `serve` …) call `kreftjs`. With way 1 that is the global command. With way 2, npm finds it in the project's `node_modules` by itself. On a server where it is not installed globally, run `npm install -D kreftjs@latest` in the project before `npm run build` or `npm run serve`.
81
81
 
82
82
  ### Check that it works
83
83
 
84
84
  ```bash
85
- kreft --version # for example 0.0.20-beta
85
+ kreft --version # for example 0.0.22-beta
86
86
  kreft about # Kreft and Node.js versions, this project's settings and drivers
87
87
  kreft list # every command your version has
88
88
  ```
@@ -100,7 +100,7 @@ installs colors and snippets for `.kreft` files in VS Code (also Cursor and VSCo
100
100
  ### Update
101
101
 
102
102
  ```bash
103
- npm i -g kreftjs@latest # the command (or: npm i -D kreftjs@latest inside a project)
103
+ npm install -g kreftjs@latest # the command (or: npm install -D kreftjs@latest inside a project)
104
104
  cd my-app
105
105
  kreft update --check # what would change
106
106
  kreft update --core # refresh this app's kreftjs.js and type files (a backup is saved)
@@ -381,6 +381,7 @@ Modifiers: `.prevent` `.stop` `.self` `.once` `.capture` `.passive`. Keys: `.ent
381
381
  | Directive | What it does |
382
382
  |---|---|
383
383
  | `k-if` / `k-elif` / `k-else` | Adds or removes the element. |
384
+ | `k-match="value"` + `k-case` / `k-default` on its children | Shows the child whose `k-case` equals the value. See [Choosing with k-match](#choosing-with-k-match). |
384
385
  | `k-each="item in list"` + `k-key="item.id"` | Repeats the element. Also `(item, i) in list`, `(value, key) in object`, `n in 10`. |
385
386
  | `k-model="name"` | Two-way binding for input, textarea, select, checkbox and radio. Modifiers `.trim` `.number` `.lazy`. |
386
387
  | `k-show` | Hides with CSS instead of removing. |
@@ -475,6 +476,62 @@ app.directive('focus', {
475
476
  <div k-tooltip:top={message}></div> <!-- binding.arg = 'top', binding.value = message -->
476
477
  ```
477
478
 
479
+ ### Choosing with k-match
480
+
481
+ When one value decides what to show, `k-match` reads better than a chain of `k-if` / `k-elif`:
482
+
483
+ ```html
484
+ <div k-match="status">
485
+ <b k-case="'ok'">Done</b>
486
+ <i k-case="'wait', 'queued'">Waiting</i>
487
+ <u k-default>Unknown</u>
488
+ </div>
489
+ ```
490
+
491
+ - The comparison is strict (`===`). Several values in one `k-case` (separated by commas) mean "one of these".
492
+ - Only the first matching case shows. `k-default` shows when none match; put it last.
493
+ - The children are turned into `k-if` / `k-elif` / `k-else`, so components, `<template>` and `k-animate` work on them as usual.
494
+
495
+ ### Pipes
496
+
497
+ Pass a value through a pipe with `|` to format it, instead of calling helper functions in the view:
498
+
499
+ ```html
500
+ <p>{price | currency:'EUR'}</p>
501
+ <p>{createdAt | ago}</p>
502
+ <p>{title | capitalize | truncate:30}</p>
503
+ <a title={name | upper}>{tags | join:' · '}</a>
504
+ ```
505
+
506
+ - Arguments follow a colon (`truncate:30`, `currency:'EUR'`) or sit in parentheses (`number(2)`). Pipes run left to right.
507
+ - `||`, `|=` and a `|` inside a string, a regular expression or brackets are not pipes.
508
+ - Numbers, currency and dates follow the app's language (`Kreftjs.locale`).
509
+
510
+ | Pipe | Result |
511
+ |---|---|
512
+ | `upper` `lower` `capitalize` `trim` | Text case and spaces. |
513
+ | `truncate:n` | Cuts text to `n` characters and adds `…`. |
514
+ | `currency:'USD'` | `$1,234.50`, in the current language. |
515
+ | `number:digits` `percent:digits` | `1,234.5`, `45%` (0.45). |
516
+ | `date:'short'` `time` | `short`, `medium` (default), `long`, `full`; or an `Intl.DateTimeFormat` options object. |
517
+ | `ago` | `3 hours ago`, `in 2 days`. A date like `2026-11-05 15:39:58` from the Kreft database is read as UTC. |
518
+ | `bytes` | `1.5 KB`. |
519
+ | `json` | Pretty-printed JSON, for debugging. |
520
+ | `fallback:'n/a'` | The value, or the fallback when it is empty. |
521
+ | `join:sep` `first` `last` `slice:a:b` `reverse` `sortBy:'key':'desc'` `count` | Lists. |
522
+
523
+ **Your own pipe.** Any function in the component works as a pipe, and wins over a built-in with the same name:
524
+
525
+ ```html
526
+ const wrap = (value, left = '«', right = '»') => left + value + right
527
+
528
+ <view><p>{name | wrap}</p> <p>{name | wrap:'[':']'}</p></view>
529
+ ```
530
+
531
+ For the whole app: `Kreftjs.pipe('shout', (value) => String(value).toUpperCase() + '!')`.
532
+
533
+ > `a | b` used to be a bitwise OR. In a view it is now a pipe; write bit operations inside a function in the script.
534
+
478
535
  ## Props, events and slots
479
536
 
480
537
  ### Using a component
@@ -516,6 +573,23 @@ prop admin = false
516
573
 
517
574
  The default's type decides how plain text attributes are read: `age="30"` becomes the number `30`, and `admin` with no value becomes `true`. A child cannot change a prop; send an event instead.
518
575
 
576
+ ### Typed props
577
+
578
+ Write the type the way TypeScript does. A wrong value, or a missing required prop, warns while you develop (in `kreft dev` and in a built app's console):
579
+
580
+ ```html
581
+ prop tone: 'info' | 'warn' = 'info' // only these values; 'info' when not given
582
+ prop label: string // required: leaving it out warns
583
+ prop count?: number // optional
584
+ prop items: string[] = []
585
+ prop onSave: (id: number) => void
586
+ ```
587
+
588
+ - Checked: `string` `number` `boolean` `null` `undefined` `object` `Date`, `T[]`, functions, literal values (`'sm'`, `3`, `true`) and unions of them. Your own type names (`User`), `any` and `unknown` are not checked.
589
+ - A prop is required when it has no default, no `?` and its type has no `null` / `undefined`.
590
+ - `number`, `boolean` and `string` also decide how a plain attribute is read: `count="5"` becomes `5`.
591
+
592
+
519
593
  ### Events: child to parent
520
594
 
521
595
  ```html
@@ -1217,10 +1291,36 @@ Generate one with `kreft make:controller PostController --api` (also `--model Po
1217
1291
  |---|---|
1218
1292
  | `req.input('name', default)`, `req.all()`, `req.only('a', 'b')`, `req.except(…)`, `req.has(…)`, `req.filled(…)` | Values from the body and the query. |
1219
1293
  | `req.boolean('x')`, `req.integer('page', 1)`, `req.string('q')` | A value in the right type. |
1220
- | `await req.validate(rules)` | The same rules as `form()`, plus `unique:table,column` and `exists:table,column`. Returns only the checked fields, or answers `422`. |
1294
+ | `await req.validate(rules, messages, names)` | The same rules as `form()`, plus `unique:table,column` and `exists:table,column`. Returns only the checked fields, or answers `422`. `messages` are your own texts (`{ 'title.unique': 'That title is already used.' }`, or `{ unique: '…' }` for every field); `names` are nicer field names (`{ email: 'e-mail address' }`). |
1221
1295
  | `req.params`, `req.query`, `req.body`, `req.header('x')`, `req.bearerToken()`, `req.ip()`, `req.user` | The rest. |
1222
1296
  | `req.file('avatar').store('avatars')` | Saves an upload with a random name. |
1223
1297
  | `response(data, 201)`, `response(data).header('X', 'y')`, `abort(403, 'message')` | Answers. |
1298
+ | `response.stream('/path/video.mp4', { type, download, headers })` | Sends a file with byte ranges. See [Video and audio files](#video-and-audio-files). |
1299
+
1300
+ ### Video and audio files
1301
+
1302
+ To serve a video or audio file, return `response.stream(path)` instead of reading the file yourself:
1303
+
1304
+ ```js
1305
+ const path = require('path')
1306
+
1307
+ Route.get('/videos/:name', (req) =>
1308
+ response.stream(path.join(__dirname, '../storage/videos', path.basename(req.params.name))))
1309
+
1310
+ Route.get('/podcast/:id/download', (req) =>
1311
+ response.stream('/var/media/episode.mp3', { download: true })) // saved as a file instead of played
1312
+ ```
1313
+
1314
+ - **Seeking works.** The server answers `Range` requests with `206 Partial Content`, so a player can jump to any minute without downloading the rest, and Safari, which refuses to play video without ranges, plays it. A range that does not fit the file gets `416`.
1315
+ - **Only the needed bytes are read**, and reading stops when the viewer leaves or seeks elsewhere.
1316
+ - **Caching is built in.** The answer carries `ETag` and `Last-Modified`, so an unchanged file is answered with `304`. `HEAD` requests get the headers only.
1317
+ - **A missing file is a plain `404`**, not a crash.
1318
+ - Options: `type` (the media type; guessed from the extension for `mp4`, `webm`, `mov`, `mp3`, `m4a`, `ogg`, `wav`, `flac`, `m3u8`, `mpd`, `vtt` …), `download: true` (adds `Content-Disposition: attachment`), and `headers` (extra headers).
1319
+
1320
+ > [!WARNING]
1321
+ > Never build the path straight from the request. Use `path.basename(...)` as above, or look the file up in your database, so nobody can ask for `../../.env`.
1322
+
1323
+ `kreft dev` and `kreft serve` also send the static media files of your app (`.mp4`, `.webm`, `.mp3`, HLS playlists …) with ranges, so a `<video src="/intro.mp4" controls>` seeks without any code.
1224
1324
 
1225
1325
  ### Form requests, resources and policies
1226
1326
 
@@ -1276,6 +1376,8 @@ class Post extends Model {
1276
1376
  module.exports = Post
1277
1377
  ```
1278
1378
 
1379
+ **Always set `static fillable`.** Without it a model lets `create()` and `update()` set every column except `id`, so `Post.create(req.all())` would save `{ "is_admin": true }` from a visitor. The controllers made by `kreft make:controller --model` read only the `fillable` columns (`req.only(...Model.fillable)`) and stop with an error until you set it. Kreft prints a warning when a model without `fillable` is given a privilege-looking column (`is_admin`, `role`, `permissions` …).
1380
+
1279
1381
  The table is the plural of the class name (`Post` is `posts`); set `static table = '…'` for another one.
1280
1382
 
1281
1383
  ### Querying
@@ -1756,7 +1858,7 @@ Apache only knows the files on disk, so without these rules `/docs` or any other
1756
1858
  > [!NOTE]
1757
1859
  > This hosts the front end only. Login, the database and everything under `/api` need the Node.js server, which Apache does not run.
1758
1860
 
1759
- **A Node.js plan or a VPS.** Upload the whole project (without `node_modules`), not `dist/`. Add Kreft to its dependencies with `npm i kreftjs`, build with `npm run build`, and start the server with `npm run serve` (it reads the `PORT` your host sets). The generated `start` script runs the development server, so do not use it in production.
1861
+ **A Node.js plan or a VPS.** Upload the whole project (without `node_modules`), not `dist/`. Add Kreft to its dependencies with `npm install kreftjs@latest`, build with `npm run build`, and start the server with `npm run serve` (it reads the `PORT` your host sets). The generated `start` script runs the development server, so do not use it in production.
1760
1862
 
1761
1863
 
1762
1864
  On a VPS, keep it running with PM2 and put nginx in front of it for your domain and HTTPS:
@@ -1781,6 +1883,30 @@ server {
1781
1883
  Then `sudo certbot --nginx -d example.com` adds HTTPS. Set `APP_ENV=production` and a real `APP_KEY` in `.env` first, and read the [Production checklist](#production-checklist).
1782
1884
 
1783
1885
 
1886
+ ### Publish the website (kreftjs.com)
1887
+
1888
+ The Kreft website is the `demo/` app plus the `docs/` manual. Three commands make it, and a fourth puts it online:
1889
+
1890
+ ```bash
1891
+ npm install kreftjs@latest # in a project that uses Kreft; in this repository, plain: npm install
1892
+ npm run build # writes the finished site to dist/kreftjs-com
1893
+ ```
1894
+
1895
+ Then upload what is *inside* `dist/kreftjs-com` (not the folder itself, and including the hidden `.htaccess`) to the folder kreftjs.com is served from, normally `public_html` (see [Upload to a host](#upload-to-a-host)). Open https://kreftjs.com and reload a page such as `/docs` to check that the clean addresses work.
1896
+
1897
+ `npm run build` runs `kreft build demo --out dist/kreftjs-com --base / --include docs`. `--base /` makes every path start at the site's root, and `--include docs` copies the manual in. To look at the result before you upload it, run `npm run preview`.
1898
+
1899
+ **Releasing a new version of the package.** This is done by the maintainer, before the site is built, so the site and the package agree:
1900
+
1901
+ ```bash
1902
+ npm test # the regression tests must pass
1903
+ npm version prerelease --preid=beta # or: patch, minor (changes package.json and makes a git tag)
1904
+ npm publish --tag latest # needs npm login; the package is then installed with kreftjs@latest
1905
+ npm run build # then upload dist/kreftjs-com as above
1906
+ ```
1907
+
1908
+ Check that people get it with `npm view kreftjs version` and a fresh `npm install -g kreftjs@latest`.
1909
+
1784
1910
  ### Ready HTML for every page: `--prerender`
1785
1911
 
1786
1912
  ```bash
@@ -2165,10 +2291,80 @@ curl -H "Authorization: Bearer a-long-random-secret" -d '{"jsonrpc":"2.0","id":1
2165
2291
  > [!WARNING]
2166
2292
  > The MCP database tool can still read every other table. Use a database account with read-only access and no access to credentials, and keep the token out of version control.
2167
2293
 
2294
+ ### Calling other MCP servers (client)
2295
+
2296
+ Your app can also call tools on a remote MCP server over Streamable HTTP:
2297
+
2298
+ ```js
2299
+ const { Mcp } = require('kreftjs/server')
2300
+
2301
+ const quran = await Mcp.connect('https://mcp.quran.ai') // { headers: { Authorization: 'Bearer …' } } if needed
2302
+ const tools = await quran.tools()
2303
+ const verses = await quran.call('fetch_quran', { ayahs: '1:1-2' })
2304
+ await quran.close()
2305
+ ```
2306
+
2307
+ `call` returns the structured result (or the text, parsed as JSON when possible) and throws if the tool reports an error. Only `http(s)` URLs are accepted, redirects are refused, and a response over `maxBytes` (10 MB) is cut off. Never pass a URL a visitor typed: your server would fetch addresses of their choosing, including your internal network (SSRF). Use a fixed or allow-listed URL.
2308
+
2309
+ ## Running `kreft dev` outside a project
2310
+
2311
+ `kreft dev` and `kreft serve` start only in a Kreft project: a folder with `index.html`, a `frontend/` folder, `.kreft` pages, a backend or a `kreftjs.config.json`. In any other folder (an empty one, your home folder) they stop and say so, instead of serving whatever is there. `kreft dev --force` serves the folder anyway. `kreft run dev` is not a command: inside a project use `kreft dev`, or `npm run dev`.
2312
+
2313
+ ## Kreft Shield
2314
+
2315
+ Floods and scanners from one address can slow a server to a crawl. Turn on the Shield with one line in `.env` (changes apply without a restart):
2316
+
2317
+ ```
2318
+ SHIELD=true
2319
+ ```
2320
+
2321
+ For every visitor address (the same `req.ip()` that `throttle` uses, so set `TRUSTED_PROXIES` behind a proxy):
2322
+
2323
+ | Protection | Default | Setting |
2324
+ |---|---|---|
2325
+ | Rate limit: over the limit answers `429` with `Retry-After` | 300 requests in 10 s | `SHIELD_MAX_REQUESTS`, `SHIELD_WINDOW_SEC` |
2326
+ | Probing counts more: `401`, `403`, `404` and `405` answers weigh 5 requests | 5 | `SHIELD_ERROR_WEIGHT` |
2327
+ | Concurrency: requests in flight at once from one address | 60 | `SHIELD_MAX_CONCURRENT` |
2328
+ | Ban: this many rate-limit hits within the strike window | 3 hits in 10 min | `SHIELD_STRIKES`, `SHIELD_STRIKE_WINDOW_MIN` |
2329
+ | Ban length, doubling at every repeat up to 24 h | 15 min | `SHIELD_BAN_MINUTES` |
2330
+ | Connections: open sockets from one address; extra ones are closed (stops slowloris floods that never finish a request) | 100 | `SHIELD_MAX_CONNECTIONS` |
2331
+ | Overload: when the event loop lags, limits shrink to a quarter | 300 ms lag | `SHIELD_MAX_LAG_MS` |
2332
+
2333
+ IPv6 visitors are counted by their `/64` prefix, because one customer owns billions of addresses in it and could otherwise rotate through them.
2334
+
2335
+ **Behind nginx, Caddy or a CDN, set `TRUSTED_PROXIES`.** Without it every visitor looks like the proxy: all users would share one limit and one ban, and a proxy on the same computer (`127.0.0.1`) is never limited at all. The Shield prints a warning when it sees `X-Forwarded-For` from an address that is not trusted.
2336
+
2337
+ Never limited: addresses in `SHIELD_ALLOW="1.2.3.4,5.6.7.8"` and this computer (`127.0.0.1`, `::1`), unless `SHIELD_LOCAL=true` (use it to try the Shield on your own machine). A banned address is answered with a bare `429` before any work is done.
2338
+
2339
+ ```js
2340
+ const { Shield } = require('kreftjs/server')
2341
+ Shield.on('ban', (b) => Log.warning(`banned ${b.ip} for ${b.minutes} min: ${b.reason}`))
2342
+ Shield.bans() // who is banned now
2343
+ Shield.unban('1.2.3.4') // let an address back in
2344
+ Shield.ban('1.2.3.4', 60)
2345
+ Shield.status() // counters, limits, event-loop lag
2346
+ ```
2347
+
2348
+ The MCP tool `shield_status` returns the same. Bans are kept in memory, so a restart clears them. The Shield protects the application; a flood that fills your network connection has to be stopped by a firewall or a CDN in front of the server.
2349
+
2168
2350
  ## Memory watchdog
2169
2351
 
2170
2352
  Long-running servers can slowly collect memory. Start with `--gc` (or `AUTO_GC=1`) and Kreft samples the heap every 30 seconds (`AUTO_GC_INTERVAL_MS`). When it passes `AUTO_GC_THRESHOLD_MB`, Kreft clears its own caches and runs garbage collection. This is a safety net; find the leak if you see it often.
2171
2353
 
2354
+ The watchdog is a small memory manager:
2355
+
2356
+ - **Adaptive.** A cleanup also runs when the heap keeps growing (more than `AUTO_GC_GROWTH_MB_PER_MIN`, default 20) while already above 60% of the threshold, not only after it passes the limit.
2357
+ - **Least-used first.** The in-memory cache (`CACHE_STORE=memory`) keeps at most `CACHE_MEMORY_MAX` entries (default 5000) and, under pressure, drops expired and least recently used entries instead of everything. Register your own cache with `onMemoryPressure('name', (fraction) => dropped)`.
2358
+ - **Leak hint.** After each real GC (needs `--gc`) Kreft records the heap that remains. If it keeps rising by `AUTO_GC_LEAK_RISE_MB` (default 20) over several collections, it reports a suspected leak. `writeHeapSnapshot()` saves a `.heapsnapshot` for Chrome DevTools to show what holds the memory.
2359
+ - **Alerts.** `onMemoryAlert((alert) => …)` is called when memory is still high after `AUTO_GC_ALERT_AFTER` cleanups in a row (default 3), or a leak is suspected, at most once per 10 minutes. Without a listener the alert is printed as a warning.
2360
+ - **Report.** `getMemoryReport()` returns the samples, trend, GC and eviction counts, leak state and alerts as JSON; the MCP tool `memory_report` returns the same.
2361
+
2362
+ ```js
2363
+ const { getMemoryReport, onMemoryAlert } = require('kreftjs/server')
2364
+ onMemoryAlert((a) => Log.warning(a.message))
2365
+ Route.get('/admin/memory', () => getMemoryReport()).middleware('auth')
2366
+ ```
2367
+
2172
2368
  # Security
2173
2369
 
2174
2370
  ## Security overview
@@ -2351,7 +2547,7 @@ Set these in `.env` (never commit it). Variables that start with `PUBLIC_` are c
2351
2547
  |---|---|
2352
2548
  | `.kreft` keywords | `state` `derive` `action` `when` `watch` `on` `prop` `event` `use` `share` `receive` `import` `saved` `load` `layout` |
2353
2549
  | In the script | `send` `props` `bus` `store` `$route` `$router` `tick` and every `Kreft` function |
2354
- | In the view | `{expr}` `attr={expr}` `@event` `k-if` `k-elif` `k-else` `k-each` `k-key` `k-model` `k-show` `k-text` `k-html` `k-ref` `k-skip` `k-once` `k-memo` `k-animate` `{for}` |
2550
+ | In the view | `{expr}` `attr={expr}` `@event` `k-if` `k-elif` `k-else` `k-each` `k-key` `k-match` `k-case` `k-default` `k-model` `k-show` `k-text` `k-html` `k-ref` `k-skip` `k-once` `k-memo` `k-animate` `{for}` |
2355
2551
  | App | `Kreftjs.app(rootOrPath)` then `.component()` `.directive()` `.share()` `.use()` `.mount()` `.unmount()` |
2356
2552
  | Data | `store` (`$patch` `$reset` `$watch`) `state` `signal` `derived` `readonly` `saved` `storage` |
2357
2553
  | Reacting | `watch` `effect` `tick` `onCleanup` `onReady` `onUpdate` `onShow` `onHide` `onDestroy` |
@@ -0,0 +1,26 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>scope</key>
6
+ <string>text.html.kreft</string>
7
+ <key>settings</key>
8
+ <dict>
9
+ <key>shellVariables</key>
10
+ <array>
11
+ <dict>
12
+ <key>name</key>
13
+ <string>TM_COMMENT_START</string>
14
+ <key>value</key>
15
+ <string>&lt;!-- </string>
16
+ </dict>
17
+ <dict>
18
+ <key>name</key>
19
+ <string>TM_COMMENT_END</string>
20
+ <key>value</key>
21
+ <string> --&gt;</string>
22
+ </dict>
23
+ </array>
24
+ </dict>
25
+ </dict>
26
+ </plist>
@@ -0,0 +1,20 @@
1
+ <snippet>
2
+ <content><![CDATA[
3
+ state ${1:count} = ${2:0}
4
+
5
+ action ${3:add}() {
6
+ ${1:count}++
7
+ }
8
+
9
+ <view>
10
+ <button @click={${3:add}}>{${1:count}}</button>
11
+ </view>
12
+
13
+ <style scoped>
14
+ $0
15
+ </style>
16
+ ]]></content>
17
+ <tabTrigger>kreftjs</tabTrigger>
18
+ <scope>text.html.kreft</scope>
19
+ <description>A new .kreft component</description>
20
+ </snippet>
@@ -0,0 +1,39 @@
1
+ %YAML 1.2
2
+ ---
3
+ name: Kreft
4
+ scope: text.html.kreft
5
+ version: 2
6
+ file_extensions:
7
+ - kreft
8
+ - kreftjs
9
+ first_line_match: ^\s*<view\b
10
+
11
+ contexts:
12
+ main:
13
+ # <view>, <script> and <style> blocks are HTML (with JS and CSS inside)
14
+ - match: (?i)(?=<view\b)
15
+ embed: scope:text.html.basic
16
+ embed_scope: text.html.embedded.kreft
17
+ escape: (?i)(?<=</view>)
18
+ - match: (?i)(?=<script\b)
19
+ embed: scope:text.html.basic
20
+ embed_scope: text.html.embedded.kreft
21
+ escape: (?i)(?<=</script>)
22
+ - match: (?i)(?=<style\b)
23
+ embed: scope:text.html.basic
24
+ embed_scope: text.html.embedded.kreft
25
+ escape: (?i)(?<=</style>)
26
+ - match: <!--
27
+ scope: punctuation.definition.comment.begin.html
28
+ push: html-comment
29
+ # everything else at the top level is the component's JavaScript
30
+ - match: '^\s*(layout|prop|state|saved|derive|action|load|watch|when|on|event|use|share|receive)\b'
31
+ captures:
32
+ 1: keyword.control.kreft storage.type.kreft
33
+ - include: scope:source.js
34
+
35
+ html-comment:
36
+ - meta_scope: comment.block.html
37
+ - match: -->
38
+ scope: punctuation.definition.comment.end.html
39
+ pop: true
@@ -0,0 +1,95 @@
1
+ Copyright 2026 Maxamuud Xuseen
2
+
3
+ Elastic License 2.0
4
+
5
+ URL: https://www.elastic.co/licensing/elastic-license
6
+
7
+ ## Acceptance
8
+
9
+ By using the software, you agree to all of the terms and conditions below.
10
+
11
+ ## Copyright License
12
+
13
+ The licensor grants you a non-exclusive, royalty-free, worldwide,
14
+ non-sublicensable, non-transferable license to use, copy, distribute, make
15
+ available, and prepare derivative works of the software, in each case subject to
16
+ the limitations and conditions below.
17
+
18
+ ## Limitations
19
+
20
+ You may not provide the software to third parties as a hosted or managed
21
+ service, where the service provides users with access to any substantial set of
22
+ the features or functionality of the software.
23
+
24
+ You may not move, change, disable, or circumvent the license key functionality
25
+ in the software, and you may not remove or obscure any functionality in the
26
+ software that is protected by the license key.
27
+
28
+ You may not alter, remove, or obscure any licensing, copyright, or other notices
29
+ of the licensor in the software. Any use of the licensor’s trademarks is subject
30
+ to applicable law.
31
+
32
+ ## Patents
33
+
34
+ The licensor grants you a license, under any patent claims the licensor can
35
+ license, or becomes able to license, to make, have made, use, sell, offer for
36
+ sale, import and have imported the software, in each case subject to the
37
+ limitations and conditions in this license. This license does not cover any
38
+ patent claims that you cause to be infringed by modifications or additions to
39
+ the software. If you or your company make any written claim that the software
40
+ infringes or contributes to infringement of any patent, your patent license for
41
+ the software granted under these terms ends immediately. If your company makes
42
+ such a claim, your patent license ends immediately for work on behalf of your
43
+ company.
44
+
45
+ ## Notices
46
+
47
+ You must ensure that anyone who gets a copy of any part of the software from you
48
+ also gets a copy of these terms.
49
+
50
+ If you modify the software, you must include in any modified copies of the
51
+ software prominent notices stating that you have modified the software.
52
+
53
+ ## No Other Rights
54
+
55
+ These terms do not imply any licenses other than those expressly granted in
56
+ these terms.
57
+
58
+ ## Termination
59
+
60
+ If you use the software in violation of these terms, such use is not licensed,
61
+ and your licenses will automatically terminate. If the licensor provides you
62
+ with a notice of your violation, and you cease all violation of this license no
63
+ later than 30 days after you receive that notice, your licenses will be
64
+ reinstated retroactively. However, if you violate these terms after such
65
+ reinstatement, any additional violation of these terms will cause your licenses
66
+ to terminate automatically and permanently.
67
+
68
+ ## No Liability
69
+
70
+ *As far as the law allows, the software comes as is, without any warranty or
71
+ condition, and the licensor will not be liable to you for any damages arising
72
+ out of these terms or the use or nature of the software, under any kind of
73
+ legal claim.*
74
+
75
+ ## Definitions
76
+
77
+ The **licensor** is the entity offering these terms, and the **software** is the
78
+ software the licensor makes available under these terms, including any portion
79
+ of it.
80
+
81
+ **you** refers to the individual or entity agreeing to these terms.
82
+
83
+ **your company** is any legal entity, sole proprietorship, or other kind of
84
+ organization that you work for, plus all organizations that have control over,
85
+ are under the control of, or are under common control with that
86
+ organization. **control** means ownership of substantially all the assets of an
87
+ entity, or the power to direct its management and policies by vote, contract, or
88
+ otherwise. Control can be direct or indirect.
89
+
90
+ **your licenses** are all the licenses granted to you for the software under
91
+ these terms.
92
+
93
+ **use** means anything you do with the software requiring one of your licenses.
94
+
95
+ **trademark** means trademarks, service marks, and similar rights.
@@ -0,0 +1,7 @@
1
+ # Kreft for Sublime Text
2
+
3
+ Syntax for `.kreft` / `.kreftjs` files (extends Sublime's built-in HTML syntax, so `<script>` is JavaScript and `<style>` is CSS), comment toggling, and snippets.
4
+
5
+ Install: copy this folder to `Packages/Kreft` (Preferences > Browse Packages...) and reopen a `.kreft` file.
6
+
7
+ Limit: Kreft-only keywords (`state`, `action`, ...) get no special color yet.
@@ -0,0 +1,10 @@
1
+ <snippet>
2
+ <content><![CDATA[
3
+ action ${1:name}(${2}) {
4
+ $0
5
+ }
6
+ ]]></content>
7
+ <tabTrigger>action</tabTrigger>
8
+ <scope>text.html.kreft</scope>
9
+ <description>A function the view can call</description>
10
+ </snippet>
@@ -0,0 +1,8 @@
1
+ <snippet>
2
+ <content><![CDATA[
3
+ derive ${1:name} = ${2:expression}
4
+ ]]></content>
5
+ <tabTrigger>derive</tabTrigger>
6
+ <scope>text.html.kreft</scope>
7
+ <description>A value worked out from others</description>
8
+ </snippet>