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.
- package/README.md +10 -1
- package/bin/artisan.js +4 -2
- package/bin/backend/cache.js +31 -5
- package/bin/backend/gc.js +193 -43
- package/bin/backend/http.js +6 -2
- package/bin/backend/index.js +6 -0
- package/bin/backend/mcp-client.js +133 -0
- package/bin/backend/mcp.js +17 -0
- package/bin/backend/model.js +15 -1
- package/bin/backend/shield.js +266 -0
- package/bin/backend/stream.js +74 -0
- package/bin/build.js +12 -5
- package/bin/db-api.js +9 -1
- package/bin/kreftjs.js +50 -12
- package/book/ar/16-virtual-list.md +131 -0
- package/book/ar/17-combobox.md +169 -0
- package/book/ar/18-tabs-offline.md +168 -0
- package/book/ar/19-wizard.md +196 -0
- package/book/ar/20-pipes-match-props.md +75 -0
- package/book/ar/README.md +12 -1
- package/book/kreft-book-ar.docx +0 -0
- package/docs/README.md +209 -13
- package/editors/sublime/Comments.tmPreferences +26 -0
- package/editors/sublime/Component.sublime-snippet +20 -0
- package/editors/sublime/Kreft.sublime-syntax +39 -0
- package/editors/sublime/LICENSE +95 -0
- package/editors/sublime/README.md +7 -0
- package/editors/sublime/action.sublime-snippet +10 -0
- package/editors/sublime/derive.sublime-snippet +8 -0
- package/editors/sublime/for.sublime-snippet +12 -0
- package/editors/sublime/k-case.sublime-snippet +8 -0
- package/editors/sublime/k-if.sublime-snippet +8 -0
- package/editors/sublime/k-match.sublime-snippet +12 -0
- package/editors/sublime/kreft-wait.sublime-snippet +12 -0
- package/editors/sublime/layout.sublime-snippet +8 -0
- package/editors/sublime/load.sublime-snippet +8 -0
- package/editors/sublime/on.sublime-snippet +10 -0
- package/editors/sublime/pipe.sublime-snippet +8 -0
- package/editors/sublime/prop-typed.sublime-snippet +8 -0
- package/editors/sublime/saved.sublime-snippet +8 -0
- package/editors/sublime/section.sublime-snippet +10 -0
- package/editors/sublime/state.sublime-snippet +8 -0
- package/editors/sublime/watch.sublime-snippet +10 -0
- package/editors/sublime/when.sublime-snippet +10 -0
- package/editors/sublime/yield.sublime-snippet +8 -0
- package/editors/vscode/snippets/kreftjs.json +143 -15
- package/editors/zed/LICENSE +95 -0
- package/editors/zed/README.md +17 -0
- package/editors/zed/extension.toml +11 -0
- package/editors/zed/languages/kreft/brackets.scm +4 -0
- package/editors/zed/languages/kreft/config.toml +17 -0
- package/editors/zed/languages/kreft/highlights.scm +18 -0
- package/editors/zed/languages/kreft/indents.scm +5 -0
- package/editors/zed/languages/kreft/injections.scm +14 -0
- package/editors/zed/languages/kreft/outline.scm +3 -0
- package/kreftjs.d.ts +1 -1
- package/kreftjs.js +219 -14
- package/package.json +3 -2
- package/server.d.ts +44 -0
- package/templates/app/backend/app/Auth/Session.js +8 -4
- package/templates/app/backend/app/Http/Middleware/Authenticate.js +7 -4
- package/templates/app/backend/database/seeders/DatabaseSeeder.js +4 -0
- package/templates/app/frontend/kreftjs.d.ts +1 -1
- 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 إلى
|
|
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
|
|
package/book/kreft-book-ar.docx
CHANGED
|
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
|
|
63
|
+
npm install -g kreftjs@latest
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
**2.
|
|
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
|
-
|
|
69
|
+
npm install -D kreftjs@latest
|
|
70
|
+
npx kreftjs dev
|
|
70
71
|
```
|
|
71
72
|
|
|
72
|
-
**3.
|
|
73
|
+
**3. Without installing anything.** `npx` downloads Kreft, runs it and forgets it. Good for trying it out:
|
|
73
74
|
|
|
74
75
|
```bash
|
|
75
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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><!-- </string>
|
|
16
|
+
</dict>
|
|
17
|
+
<dict>
|
|
18
|
+
<key>name</key>
|
|
19
|
+
<string>TM_COMMENT_END</string>
|
|
20
|
+
<key>value</key>
|
|
21
|
+
<string> --></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.
|