kreftjs 0.0.11-beta → 0.0.13-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/bin/build.js CHANGED
@@ -229,6 +229,7 @@ ${indent(components.join('\n\n'))}
229
229
  : html.replace(/<head>/i, `<head>\n <base href="${b}">`);
230
230
  }
231
231
  write(dist, 'index.html', html);
232
+ addCacheBusters(dist);
232
233
  if (htaccess) writeHtaccess(dist, html);
233
234
 
234
235
  const report = () => {
@@ -252,6 +253,22 @@ ${indent(components.join('\n\n'))}
252
253
  report();
253
254
  };
254
255
 
256
+ // A host keeps .js and .css for days (Hostinger: 7), and a new build keeps the same file names, so visitors would run the old
257
+ // runtime and old pages after an upgrade (the welcome page still saying "Kreft 0.0.6-beta"). A short hash of each file goes into its
258
+ // address (kreftjs.js?v=3f2a9c1b): a changed file is a new address, an unchanged one stays cached.
259
+ function addCacheBusters(dist) {
260
+ const file = path.join(dist, 'index.html');
261
+ const html = fs.readFileSync(file, 'utf8');
262
+ const out = html.replace(/(<(?:script|link)\b[^>]*?\b(?:src|href)=)(["'])([^"']+)\2/gi, (m, pre, q, url) => {
263
+ if (/^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(url) || /[?#]/.test(url) || !/\.(?:m?js|css)$/i.test(url)) return m;
264
+ const target = path.join(dist, url.replace(/^\//, ''));
265
+ if (!target.startsWith(dist + path.sep) || !fs.existsSync(target) || !fs.statSync(target).isFile()) return m;
266
+ const hash = require('crypto').createHash('sha1').update(fs.readFileSync(target)).digest('hex').slice(0, 8);
267
+ return pre + q + url + '?v=' + hash + q;
268
+ });
269
+ if (out !== html) fs.writeFileSync(file, out);
270
+ }
271
+
255
272
  // Apache (Hostinger, cPanel …) answers /docs with a 404, because only index.html exists on disk. These rules send every page address
256
273
  // (no file extension, not under /api/) that is not a real file, or a folder with its own index.html (a prerendered page), to index.html,
257
274
  // so reloading a page works even when a folder has the page's name (the docs/ folder next to the /docs page). A missing file such as
@@ -273,6 +290,13 @@ function writeHtaccess(dist, html) {
273
290
  RewriteCond %{REQUEST_URI} !\\.[A-Za-z0-9]+$
274
291
  RewriteRule . ${base}index.html [L]
275
292
  </IfModule>
293
+
294
+ # index.html names the other files with their hashes, so it must always be asked for again
295
+ <IfModule mod_headers.c>
296
+ <FilesMatch "^index\\.html$">
297
+ Header set Cache-Control "no-cache"
298
+ </FilesMatch>
299
+ </IfModule>
276
300
  `);
277
301
  }
278
302
 
package/docs/README.md CHANGED
@@ -82,7 +82,7 @@ npx kreftjs dev
82
82
  ### Check that it works
83
83
 
84
84
  ```bash
85
- kreft --version # for example 0.0.11-beta
85
+ kreft --version # for example 0.0.13-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
  ```
@@ -1751,7 +1751,7 @@ RewriteCond %{REQUEST_URI} !\.[A-Za-z0-9]+$
1751
1751
  RewriteRule . /index.html [L]
1752
1752
  ```
1753
1753
 
1754
- Apache only knows the files on disk, so without these rules `/docs` or any other clean address gives a 404 when someone opens or reloads it. With them, every page address (no file extension, not under `/api/`) that is not a real file, or a folder with its own `index.html`, opens `index.html` and the router takes over. A missing file such as `app.js`, or an `/api/` address, stays a plain 404, so code that asks for data never receives the page's HTML. Upload the file too (it starts with a dot, so some file managers hide it; turn on "show hidden files"). If the site lives in a folder, build with `--base /folder/` and the rules follow it. An `.htaccess` you keep in `public/` is used as it is. A relative base (`--base ./`, used by the desktop build) gets no file.
1754
+ Apache only knows the files on disk, so without these rules `/docs` or any other clean address gives a 404 when someone opens or reloads it. With them, every page address (no file extension, not under `/api/`) that is not a real file, or a folder with its own `index.html`, opens `index.html` and the router takes over. A missing file such as `app.js`, or an `/api/` address, stays a plain 404, so code that asks for data never receives the page's HTML. Upload the file too (it starts with a dot, so some file managers hide it; turn on "show hidden files"). If the site lives in a folder, build with `--base /folder/` and the rules follow it. An `.htaccess` you keep in `public/` is used as it is. `kreft build` also puts a short hash into the address of each script and style sheet (`app.js?v=28a965f6`), and the `.htaccess` tells the host never to keep `index.html`. Hosts cache `.js` and `.css` for days, so without the hash visitors would keep running the old runtime and old pages after you upload a new build. A relative base (`--base ./`, used by the desktop build) gets no file.
1755
1755
 
1756
1756
  > [!NOTE]
1757
1757
  > This hosts the front end only. Login, the database and everything under `/api` need the Node.js server, which Apache does not run.
package/kreftjs.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Type definitions for Kreft 0.0.11-beta
1
+ // Type definitions for Kreft 0.0.13-beta
2
2
  // Use from TypeScript or for editor hints in plain JavaScript (// @ts-check).
3
3
 
4
4
  export interface Signal<T> { value: T }
package/kreftjs.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Kreft v0.0.11-beta — reactive by nature
2
+ * Kreft v0.0.13-beta — reactive by nature
3
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.
@@ -5036,7 +5036,7 @@
5036
5036
  stores, storeLog, busLog,
5037
5037
  };
5038
5038
 
5039
- const VERSION = '0.0.11-beta';
5039
+ const VERSION = '0.0.13-beta';
5040
5040
  const Kreftjs = {
5041
5041
  version: VERSION,
5042
5042
  // PUBLIC_ values from .env (kreft dev and kreft build put them here); never secrets
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kreftjs",
3
- "version": "0.0.11-beta",
3
+ "version": "0.0.13-beta",
4
4
  "description": "A tiny reactive framework with its own component language (.kreft files): router with nested routes, stores, saved data, animations, an API client, hot reload, DevTools and a template engine.",
5
5
  "license": "Elastic-2.0",
6
6
  "main": "kreftjs.js",
@@ -14,7 +14,7 @@
14
14
  "start": "node bin/kreftjs.js dev --api demo/db.json --db sqlite:demo/database/demo.db --migrations demo/database/migrations --server demo/server --backend demo --seed",
15
15
  "build": "node bin/kreftjs.js build demo --out dist/demo --include docs",
16
16
  "preview": "node bin/kreftjs.js dev dist --api demo/db.json --db sqlite:demo/database/demo.db --migrations demo/database/migrations --server demo/server --backend demo --seed",
17
- "test": "node --test test/security.test.js test/prerender.test.js test/seo.test.js test/rebrand.test.js test/noauth.test.js test/winspawn.test.js test/htaccess.test.js test/cookie-secure.test.js test/password-rule.test.js",
17
+ "test": "node --test test/security.test.js test/prerender.test.js test/seo.test.js test/rebrand.test.js test/noauth.test.js test/winspawn.test.js test/htaccess.test.js test/cookie-secure.test.js test/password-rule.test.js test/headline.test.js",
18
18
  "electron": "kreftjs electron",
19
19
  "electron:dev": "kreftjs electron:dev",
20
20
  "electron:build": "kreftjs electron:build"
@@ -92,7 +92,7 @@ state docs = (Kreftjs.env.PUBLIC_DOCS_URL || 'http://kreftjs.com').replace(/\/+$
92
92
  <div class="badges">
93
93
  <span class="badge danger-badge">HTTP {status}</span>
94
94
  <span class="badge solid">{currentTitle}</span>
95
- <span class="badge">Kreft {Kreftjs.version || '0.0.11-beta'}</span>
95
+ <span class="badge">Kreft {Kreftjs.version || '0.0.13-beta'}</span>
96
96
  </div>
97
97
  <div class="actions">
98
98
  <button class="sm" @click={copyReport}>{copied ? 'Copied Markdown' : 'Copy Report'}</button>
@@ -189,7 +189,7 @@ state docs = (Kreftjs.env.PUBLIC_DOCS_URL || 'http://kreftjs.com').replace(/\/+$
189
189
  <h3>Kreft Runtime</h3>
190
190
  <table>
191
191
  <tbody>
192
- <tr><th>Engine</th><td><code>Kreft v{Kreftjs.version || '0.0.11-beta'}</code></td></tr>
192
+ <tr><th>Engine</th><td><code>Kreft v{Kreftjs.version || '0.0.13-beta'}</code></td></tr>
193
193
  <tr><th>Sanitizer</th><td><code>Active (cleanHtml / SafeHtml)</code></td></tr>
194
194
  <tr><th>Environment</th><td><code>development (APP_DEBUG=true)</code></td></tr>
195
195
  </tbody>
@@ -1,4 +1,4 @@
1
- // Type definitions for Kreft 0.0.11-beta
1
+ // Type definitions for Kreft 0.0.13-beta
2
2
  // Use from TypeScript or for editor hints in plain JavaScript (// @ts-check).
3
3
 
4
4
  export interface Signal<T> { value: T }
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Kreft v0.0.11-beta — reactive by nature
2
+ * Kreft v0.0.13-beta — reactive by nature
3
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.
@@ -5036,7 +5036,7 @@
5036
5036
  stores, storeLog, busLog,
5037
5037
  };
5038
5038
 
5039
- const VERSION = '0.0.11-beta';
5039
+ const VERSION = '0.0.13-beta';
5040
5040
  const Kreftjs = {
5041
5041
  version: VERSION,
5042
5042
  // PUBLIC_ values from .env (kreft dev and kreft build put them here); never secrets
@@ -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.11-beta'}</span>
115
+ <span class="info-value">Kreft {Kreftjs.version || '0.0.13-beta'}</span>
116
116
  </div>
117
117
  <div class="info-item">
118
118
  <span class="info-label">Active Database</span>
@@ -4,6 +4,22 @@
4
4
  state name = 'Enterprise User'
5
5
  state count = 0
6
6
  state copied = false
7
+
8
+ // the headline changes every few seconds: each line slides in from the right with a flash of red light (not for people who asked for less motion)
9
+ state rotating = [
10
+ 'Build full-stack apps in plain words.',
11
+ 'The JavaScript framework that reads like English.',
12
+ 'Components, backend and database in one small framework.',
13
+ 'A small framework for the whole stack.',
14
+ ]
15
+ state rotIndex = 0
16
+ derive rotWord = [rotating[rotIndex]]
17
+ let rotTimer
18
+ on ready {
19
+ const calm = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches
20
+ if (!calm) rotTimer = setInterval(() => { rotIndex = (rotIndex + 1) % rotating.length }, 3800)
21
+ }
22
+ on destroy { clearInterval(rotTimer) }
7
23
  derive double = count * 2
8
24
 
9
25
  state docs = (Kreftjs.env.PUBLIC_DOCS_URL || 'http://kreftjs.com').replace(/\/+$/, '') // where "Documentation" links go: PUBLIC_DOCS_URL in .env
@@ -64,17 +80,18 @@ action copy() {
64
80
  <section class="hero-grid">
65
81
  <!-- Left Column: Copy, Headlines & CTA Badges -->
66
82
  <div class="hero-copy">
67
- <p class="version-pill"><span class="version-dot" aria-hidden="true"></span>Kreft v{Kreftjs.version}</p>
68
- <h1 class="hero-headline">
69
- The Enterprise<br>
70
- JavaScript<br>
71
- Framework.
83
+ <div class="hero-meta">
84
+ <p class="version-pill"><span class="version-dot" aria-hidden="true"></span>Kreft v{Kreftjs.version}</p>
85
+ <p class="hero-kicker">JavaScript framework · Laravel-friendly</p>
86
+ </div>
87
+ <h1 class="hero-headline" aria-label="Build full-stack apps in plain words.">
88
+ <span aria-hidden="true">{for w in rotWord key w}<span class="rotator">{w}</span>{/for}</span>
72
89
  </h1>
73
90
 
74
91
  <p class="hero-tagline">Built for scale, speed, and safety.</p>
75
92
 
76
93
  <p class="hero-summary">
77
- Kreft is a zero-dependency, full-stack JavaScript framework featuring its own component language, built-in SQL ORM, state management, migrations, auth, queues, and desktop packaging—all self-contained with no build steps required.
94
+ Kreft is a zero-dependency, full-stack JavaScript framework featuring its own component language, built-in SQL ORM, state management, migrations, auth, queues, and desktop packaging—all self-contained with no build steps required. Routes, controllers, models, migrations and commands will feel familiar if you know Laravel.
78
95
  </p>
79
96
 
80
97
  <!-- CTA Buttons -->
@@ -90,6 +107,7 @@ action copy() {
90
107
  <span class="badge highlight">✓ 0 Dependencies</span>
91
108
  <span class="badge">✓ Built-in SQL ORM</span>
92
109
  <span class="badge">✓ Authentication &amp; Gates</span>
110
+ <span class="badge">✓ Laravel-friendly</span>
93
111
  <span class="badge">✓ TypeScript &amp; JS Choice</span>
94
112
  </div>
95
113
  </div>
@@ -213,7 +231,7 @@ action copy() {
213
231
  <style scoped>
214
232
  .welcome-container {
215
233
  position: relative;
216
- overflow: visible;
234
+ overflow-x: clip; /* the glow circles may reach past the screen edge without making the page scroll sideways */
217
235
  width: 100%;
218
236
  max-width: calc(var(--container, 1240px) + var(--container-pad, 28px) * 2);
219
237
  margin: 0 auto;
@@ -280,7 +298,7 @@ action copy() {
280
298
  display: inline-flex;
281
299
  align-items: center;
282
300
  gap: 8px;
283
- margin: 0 0 20px;
301
+ margin: 0;
284
302
  padding: 5px 12px;
285
303
  font-family: var(--font-mono, ui-monospace, monospace);
286
304
  font-size: 12.5px;
@@ -291,8 +309,21 @@ action copy() {
291
309
  border-radius: 9999px;
292
310
  }
293
311
  .version-dot { width: 7px; height: 7px; border-radius: 50%; background: #10b981; }
312
+ .hero-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 20px; }
313
+ .hero-kicker { margin: 0; padding: 5px 12px; font-size: 12.5px; font-weight: 600; letter-spacing: 0.01em; color: var(--muted); border: 1px dashed var(--border); border-radius: 9999px; }
314
+
315
+ /* the changing headline: each line slides in from the right (a marquee) with a flash of red light, then settles */
316
+ .rotator { display: block; animation: rotator-flash 0.9s ease-out; }
317
+ @keyframes rotator-flash {
318
+ 0% { opacity: 0; transform: translateX(1.4em); filter: blur(6px); color: #ef4444; text-shadow: 0 0 0.7em rgba(239, 68, 68, 0.95); }
319
+ 35% { opacity: 1; filter: blur(0); color: #ef4444; text-shadow: 0 0 0.55em rgba(239, 68, 68, 0.85); }
320
+ 100% { opacity: 1; transform: none; filter: none; color: var(--foreground); text-shadow: none; }
321
+ }
322
+ @media (prefers-reduced-motion: reduce) { .rotator { animation: none; } }
294
323
  .hero-headline {
295
- font-size: clamp(38px, 5.2vw, 66px);
324
+ font-size: clamp(30px, 3.9vw, 48px);
325
+ min-height: 3.3em; /* three lines for the longest headline, so the page below does not jump */
326
+ overflow: hidden;
296
327
  line-height: 1.05;
297
328
  letter-spacing: -0.04em;
298
329
  font-weight: 800;
@@ -388,6 +419,7 @@ action copy() {
388
419
 
389
420
  /* Code Terminal Window */
390
421
  .terminal-card {
422
+ min-width: 0;
391
423
  background: #0f172a;
392
424
  border-radius: 18px;
393
425
  box-shadow: 0 25px 60px -15px rgba(0, 0, 0, 0.4), 0 0 1px 1px rgba(255, 255, 255, 0.08);
@@ -611,24 +643,45 @@ dd { margin: 0; color: var(--muted); }
611
643
  .term button { background: var(--primary); color: var(--primary-foreground); border: none; padding: 6px 12px; border-radius: 4px; font-size: 12px; cursor: pointer; }
612
644
  .begin p { color: var(--muted); max-width: 40em; }
613
645
 
646
+ @media (max-width: 1100px) {
647
+ .hero-grid { gap: 32px; }
648
+ .floating-preview { width: 290px; }
649
+ }
614
650
  @media (max-width: 960px) {
615
- .hero-grid { grid-template-columns: 1fr; gap: 40px; }
616
- .hero-copy { max-width: 100%; }
617
- .hero-visual { justify-content: center; width: 100%; padding: 0; }
618
- .terminal-card { max-width: 100%; }
619
- .floating-preview { position: static; width: 100%; max-width: 100%; margin-top: 20px; right: 0; bottom: 0; }
651
+ /* minmax(0, 1fr): the code window may scroll inside itself, it must not make the whole page wider than the screen */
652
+ .hero-grid { grid-template-columns: minmax(0, 1fr); gap: 40px; }
653
+ .hero-copy, .hero-visual, .terminal-card { min-width: 0; max-width: 100%; }
654
+ /* the live card goes under the code window (a column), not beside it: two 100% wide boxes in one row squeezed both to nothing */
655
+ .hero-visual { flex-direction: column; align-items: center; gap: 20px; width: 100%; padding: 0; }
656
+ .terminal-card, .floating-preview { width: 100%; max-width: 560px; }
657
+ .floating-preview { position: static; margin: 0; right: 0; bottom: 0; }
620
658
  .inside ul { grid-template-columns: repeat(2, minmax(0, 1fr)); }
621
659
  }
622
660
  @media (max-width: 640px) {
623
- .welcome-container { padding: 24px 0 48px; }
624
- .hero-headline { font-size: 36px; }
661
+ .welcome-container { padding: 0 max(16px, env(safe-area-inset-right)) 40px max(16px, env(safe-area-inset-left)); }
662
+ .site-bar { height: 56px; }
663
+ .site-nav a { padding: 10px 12px; }
664
+ .hero-headline { font-size: 32px; min-height: 4.4em; }
625
665
  .hero-tagline { font-size: 20px; }
626
- .cta-actions { flex-direction: column; width: 100%; }
627
- .pill-btn { width: 100%; text-align: center; }
666
+ .hero-summary { font-size: 15px; }
667
+ .cta-actions { flex-direction: column; align-items: stretch; width: 100%; }
668
+ .pill-btn { width: 100%; min-height: 46px; text-align: center; }
628
669
  .feature-badges { gap: 6px; }
629
670
  .badge { font-size: 11.5px; padding: 5px 10px; }
671
+ .terminal-code { font-size: 12.5px; padding: 18px 16px 44px; }
672
+ .floating-preview { padding: 18px; }
673
+ .language, .inside, .begin { padding: 40px 0; }
630
674
  .inside ul { grid-template-columns: 1fr; }
631
675
  dl { grid-template-columns: 1fr; }
632
676
  dl div { grid-template-columns: 1fr; gap: 4px; }
677
+ .term { max-width: 100%; }
678
+ .term code { min-width: 0; overflow-x: auto; white-space: nowrap; }
679
+ .site-foot { flex-wrap: wrap; gap: 8px 16px; }
680
+ }
681
+ @media (max-width: 380px) {
682
+ .hero-headline { font-size: 28px; }
683
+ .version-pill, .hero-kicker { font-size: 11.5px; }
684
+ .terminal-code { font-size: 11.5px; }
685
+ .preview-badges { flex-wrap: wrap; }
633
686
  }
634
687
  </style>
@@ -118,7 +118,7 @@ state docs = (Kreftjs.env.PUBLIC_DOCS_URL || 'http://kreftjs.com').replace(/\/+$
118
118
  <div class="hero-badges">
119
119
  <span class="badge-exception">{currentTitle}</span>
120
120
  <span class="badge-env">Node.js {typeof process !== 'undefined' ? process.version : 'v22.0.0'}</span>
121
- <span class="badge-env">Kreft v0.0.11-beta</span>
121
+ <span class="badge-env">Kreft v0.0.13-beta</span>
122
122
  </div>
123
123
  <h1 class="hero-error-message">{currentMessage}</h1>
124
124
  </div>