tina4-nodejs 3.11.2 → 3.11.5

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- "version": "3.11.2",
6
+ "version": "3.11.5",
7
7
 
8
8
  "type": "module",
9
9
  "description": "Tina4 for Node.js/TypeScript — 54 built-in features, zero dependencies",
@@ -88,13 +88,19 @@ export function parseFields(fieldsStr: string): Array<[string, string]> {
88
88
  }
89
89
 
90
90
  export function parseCliArgs(args: string[]): { flags: Record<string, string | boolean>; positional: string[] } {
91
+ // Boolean-only flags that never take a value argument
92
+ const booleanFlags = new Set(["no-browser", "no-reload", "production", "managed", "all", "clear"]);
93
+
91
94
  const flags: Record<string, string | boolean> = {};
92
95
  const positional: string[] = [];
93
96
  let i = 0;
94
97
  while (i < args.length) {
95
98
  if (args[i].startsWith("--")) {
96
99
  const key = args[i].slice(2);
97
- if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
100
+ if (booleanFlags.has(key)) {
101
+ flags[key] = true;
102
+ i += 1;
103
+ } else if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
98
104
  flags[key] = args[i + 1];
99
105
  i += 2;
100
106
  } else {
@@ -37,48 +37,13 @@ export async function serveProject(options: ServeOptions): Promise<void> {
37
37
  staticDir,
38
38
  });
39
39
 
40
- // Watch for file changes.
41
- //
42
- // Templates and static assets are re-read from disk every request in dev mode,
43
- // so we only need to touch the router when a .ts/.js route file actually
44
- // changes. Clearing the router on every edit (including templates) leaves a
45
- // brief window where the router is empty — any request hitting that window
46
- // gets a 404 whose response path bypasses the dev toolbar injection, so the
47
- // toolbar appears to "vanish" after a hot reload. Route-file-only clearing
48
- // matches the behaviour of Python's DevReload and the fix made in PHP v3.10.87.
49
- const noReload = ["true", "1", "yes"].includes((process.env.TINA4_NO_RELOAD ?? "").toLowerCase());
50
- const watchDirs = [routesDir, ormDir, modelsDir, templatesDir].filter((d) => existsSync(d));
51
- let watcher: { close: () => void } | null = null;
52
- if (!noReload) {
53
- watcher = watchForChanges(watchDirs, async ({ code }) => {
54
- if (!code) {
55
- // Template/CSS/JS asset change — nothing to do in the server. The
56
- // browser will re-fetch on its own reload cycle and the request will
57
- // be served against the existing route set with the toolbar intact.
58
- return;
59
- }
60
- try {
61
- // Re-discover routes. discoverRoutes() cache-busts imports via ?t=<timestamp>,
62
- // so the new modules are loaded fresh. Build the new list first, then
63
- // replace the router's state in one back-to-back block to minimise the
64
- // window where the router is empty.
65
- const { discoverRoutes } = await import("../../../core/src/index.js");
66
- const routes = await discoverRoutes(routesDir);
67
- server.router.clear();
68
- for (const route of routes) {
69
- server.router.addRoute(route);
70
- }
71
- console.log(` Reloaded ${routes.length} route(s)`);
72
- } catch (err) {
73
- console.error(" Error reloading routes:", err);
74
- }
75
- });
76
- }
40
+ // File watching is handled by the Rust CLI (tina4 serve). The framework
41
+ // only needs POST /__dev/api/reload to update the mtime counter for browser polling.
42
+ // No internal file watcher.
77
43
 
78
44
  // Graceful shutdown
79
45
  const shutdown = () => {
80
46
  console.log("\n Shutting down...");
81
- watcher?.close();
82
47
  server.close();
83
48
  process.exit(0);
84
49
  };
@@ -437,6 +437,9 @@ export class DevAdmin {
437
437
  // Dashboard
438
438
  { method: "GET", pattern: "/__dev", handler: handleDashboard },
439
439
  { method: "GET", pattern: "/__dev/", handler: handleDashboard },
440
+ // Reload — called by Rust CLI on file changes
441
+ { method: "GET", pattern: "/__dev/api/mtime", handler: handleMtime },
442
+ { method: "POST", pattern: "/__dev/api/reload", handler: handleReload },
440
443
  // Status & system
441
444
  { method: "GET", pattern: "/__dev/api/status", handler: handleStatus(router) },
442
445
  { method: "GET", pattern: "/__dev/api/system", handler: handleSystem },
@@ -526,6 +529,23 @@ const handleDashboard: RouteHandler = (_req, res) => {
526
529
  res.raw.end(spa);
527
530
  };
528
531
 
532
+ // Reload mtime counter — updated by POST /__dev/api/reload from Rust CLI
533
+ let _reloadMtime = 0;
534
+ let _reloadFile = "";
535
+
536
+ const handleMtime: RouteHandler = async (_req, res) => {
537
+ res.json({ mtime: _reloadMtime, file: _reloadFile });
538
+ };
539
+
540
+ const handleReload: RouteHandler = async (req, res) => {
541
+ _reloadMtime = Math.floor(Date.now() / 1000);
542
+ const body = req.body as Record<string, unknown> | undefined;
543
+ _reloadFile = (body?.file as string) || "";
544
+ const reloadType = (body?.type as string) || "reload";
545
+ console.log(` External reload trigger: ${reloadType}${_reloadFile ? ` (${_reloadFile})` : ""}`);
546
+ res.json({ ok: true, type: reloadType });
547
+ };
548
+
529
549
  function handleStatus(router: Router): RouteHandler {
530
550
  return async (_req, res) => {
531
551
  const mem = process.memoryUsage();