tina4-nodejs 3.10.50 → 3.10.55
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 +1 -1
- package/packages/cli/src/bin.ts +3 -1
- package/packages/cli/src/commands/serve.ts +23 -14
- package/packages/core/src/devAdmin.ts +1 -1
- package/packages/core/src/server.ts +41 -2
- package/packages/orm/src/adapters/mongodb.ts +679 -0
- package/packages/orm/src/adapters/odbc.ts +413 -0
- package/packages/orm/src/adapters/sqlite.ts +23 -3
- package/packages/orm/src/autoCrud.ts +15 -6
- package/packages/orm/src/baseModel.ts +32 -5
- package/packages/orm/src/database.ts +73 -2
- package/packages/orm/src/databaseResult.ts +26 -6
- package/packages/orm/src/index.ts +4 -0
- package/packages/orm/src/query.ts +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tina4-nodejs",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.55",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Tina4 for Node.js/TypeScript — 54 built-in features, zero dependencies",
|
|
6
6
|
"keywords": ["tina4", "framework", "web", "api", "orm", "graphql", "websocket", "typescript"],
|
package/packages/cli/src/bin.ts
CHANGED
|
@@ -46,6 +46,7 @@ const HELP = `
|
|
|
46
46
|
Options:
|
|
47
47
|
--port <number> Server port (default: 7148)
|
|
48
48
|
--no-browser Don't open the browser on serve
|
|
49
|
+
--no-reload Disable file watcher / live-reload on serve
|
|
49
50
|
--all Install AI context for all tools (with ai command)
|
|
50
51
|
--force Overwrite existing AI context files (with ai command)
|
|
51
52
|
--help Show this help message
|
|
@@ -85,11 +86,12 @@ async function main(): Promise<void> {
|
|
|
85
86
|
const portIndex = args.indexOf("--port");
|
|
86
87
|
const port = portIndex !== -1 ? parseInt(args[portIndex + 1], 10) : 7148;
|
|
87
88
|
const noBrowser = args.includes("--no-browser");
|
|
89
|
+
const noReload = args.includes("--no-reload");
|
|
88
90
|
|
|
89
91
|
// Kill any existing process on the port
|
|
90
92
|
killProcessOnPort(port);
|
|
91
93
|
|
|
92
|
-
await serveProject({ port, noBrowser });
|
|
94
|
+
await serveProject({ port, noBrowser, noReload });
|
|
93
95
|
break;
|
|
94
96
|
}
|
|
95
97
|
case "migrate": {
|
|
@@ -4,9 +4,14 @@ import { existsSync } from "node:fs";
|
|
|
4
4
|
export interface ServeOptions {
|
|
5
5
|
port?: number;
|
|
6
6
|
noBrowser?: boolean;
|
|
7
|
+
noReload?: boolean;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export async function serveProject(options: ServeOptions): Promise<void> {
|
|
11
|
+
if (options.noReload) {
|
|
12
|
+
process.env.TINA4_NO_RELOAD = "true";
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
const port = options.port ?? 7148;
|
|
11
16
|
const cwd = process.cwd();
|
|
12
17
|
|
|
@@ -33,26 +38,30 @@ export async function serveProject(options: ServeOptions): Promise<void> {
|
|
|
33
38
|
});
|
|
34
39
|
|
|
35
40
|
// Watch for file changes and reload routes
|
|
41
|
+
const noReload = ["true", "1", "yes"].includes((process.env.TINA4_NO_RELOAD ?? "").toLowerCase());
|
|
36
42
|
const watchDirs = [routesDir, ormDir, modelsDir, templatesDir].filter((d) => existsSync(d));
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
let watcher: { close: () => void } | null = null;
|
|
44
|
+
if (!noReload) {
|
|
45
|
+
watcher = watchForChanges(watchDirs, async () => {
|
|
46
|
+
try {
|
|
47
|
+
const { discoverRoutes } = await import("../../../core/src/index.js");
|
|
48
|
+
// Clear routes BEFORE re-discovery to avoid stale duplicates
|
|
49
|
+
server.router.clear();
|
|
50
|
+
const routes = await discoverRoutes(routesDir);
|
|
51
|
+
for (const route of routes) {
|
|
52
|
+
server.router.addRoute(route);
|
|
53
|
+
}
|
|
54
|
+
console.log(` Reloaded ${routes.length} route(s)`);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.error(" Error reloading routes:", err);
|
|
45
57
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.error(" Error reloading routes:", err);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
51
60
|
|
|
52
61
|
// Graceful shutdown
|
|
53
62
|
const shutdown = () => {
|
|
54
63
|
console.log("\n Shutting down...");
|
|
55
|
-
watcher
|
|
64
|
+
watcher?.close();
|
|
56
65
|
server.close();
|
|
57
66
|
process.exit(0);
|
|
58
67
|
};
|
|
@@ -2467,7 +2467,7 @@ function renderBubbleChart(files,depGraph,scanMode){
|
|
|
2467
2467
|
bubbles.forEach(function(b,i){if(b.f.path===src)srcIdx=i;});
|
|
2468
2468
|
if(srcIdx===null)return;
|
|
2469
2469
|
(depGraph[src]||[]).forEach(function(tgt){
|
|
2470
|
-
var tgtName=tgt
|
|
2470
|
+
var tgtName=basename(tgt);
|
|
2471
2471
|
var tgtIdx=nameIdx[tgtName];
|
|
2472
2472
|
if(tgtIdx!==undefined&&srcIdx!==tgtIdx)edges.push([srcIdx,tgtIdx]);
|
|
2473
2473
|
});
|
|
@@ -673,7 +673,17 @@ ${reset}
|
|
|
673
673
|
const requestId = Date.now().toString(36);
|
|
674
674
|
|
|
675
675
|
// Wrap res.raw.end to inject dev toolbar and capture requests
|
|
676
|
-
|
|
676
|
+
// Skip toolbar injection on the AI port (no-reload behaviour)
|
|
677
|
+
const isAiPortRequest = !!(rawReq as any)._tina4AiPort;
|
|
678
|
+
|
|
679
|
+
// AI port: block /__dev_reload so AI tools never trigger a browser reload
|
|
680
|
+
if (isAiPortRequest && pathname === "/__dev_reload") {
|
|
681
|
+
res.raw.writeHead(404, { "Content-Type": "application/json" });
|
|
682
|
+
res.raw.end(JSON.stringify({ error: "Not available on AI port" }));
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (isDevMode() && !pathname.startsWith("/__dev") && !isAiPortRequest) {
|
|
677
687
|
const originalEnd = res.raw.end.bind(res.raw);
|
|
678
688
|
|
|
679
689
|
const wrappedEnd: typeof res.raw.end = function (
|
|
@@ -873,7 +883,35 @@ ${reset}
|
|
|
873
883
|
// Determine server mode label
|
|
874
884
|
const serverMode = isDebug ? "single" : (cluster.isWorker ? "cluster-worker" : "single");
|
|
875
885
|
|
|
886
|
+
// AI dual-port: start a second HTTP server on port+1 when in debug mode
|
|
887
|
+
// and TINA4_NO_AI_PORT is not set. This port serves requests without
|
|
888
|
+
// dev-reload or toolbar injection so AI coding tools get stable responses.
|
|
889
|
+
const noAiPort = isTruthy(process.env.TINA4_NO_AI_PORT ?? "");
|
|
890
|
+
let aiServer: ReturnType<typeof createServer> | null = null;
|
|
891
|
+
let aiPort = port + 1;
|
|
892
|
+
|
|
893
|
+
if (isDebug && !noAiPort) {
|
|
894
|
+
aiServer = createServer(async (req, res) => {
|
|
895
|
+
// Tag the request so dispatch knows it came from the AI port
|
|
896
|
+
(req as any)._tina4AiPort = true;
|
|
897
|
+
await dispatch(req, res);
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
aiServer.on("error", (err: any) => {
|
|
901
|
+
if (err.code === "EADDRINUSE") {
|
|
902
|
+
Log.warn(`AI port ${aiPort} in use — skipping`);
|
|
903
|
+
aiServer = null;
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
aiServer.listen(aiPort, host);
|
|
908
|
+
}
|
|
909
|
+
|
|
876
910
|
// Banner goes to stdout via console.log — NOT through the framework logger
|
|
911
|
+
const aiPortLine = (isDebug && !noAiPort)
|
|
912
|
+
? `\n AI Port: http://localhost:${aiPort} (no-reload)`
|
|
913
|
+
: "";
|
|
914
|
+
|
|
877
915
|
console.log(`${color}
|
|
878
916
|
______ _ __ __
|
|
879
917
|
/_ __/(_)___ ____ _/ // /
|
|
@@ -886,7 +924,7 @@ ${reset}
|
|
|
886
924
|
Server: http://${displayHost}:${port} (${serverMode})
|
|
887
925
|
Swagger: http://localhost:${port}/swagger
|
|
888
926
|
Dashboard: http://localhost:${port}/__dev
|
|
889
|
-
Debug: ${isDebug ? "ON" : "OFF"} (Log level: ${logLevel})
|
|
927
|
+
Debug: ${isDebug ? "ON" : "OFF"} (Log level: ${logLevel})${aiPortLine}
|
|
890
928
|
`);
|
|
891
929
|
const noBrowser = isTruthy(process.env.TINA4_NO_BROWSER);
|
|
892
930
|
if (!noBrowser) {
|
|
@@ -894,6 +932,7 @@ ${reset}
|
|
|
894
932
|
}
|
|
895
933
|
resolvePromise({
|
|
896
934
|
close: () => {
|
|
935
|
+
if (aiServer) aiServer.close();
|
|
897
936
|
server.close();
|
|
898
937
|
// Close database if ORM was initialized
|
|
899
938
|
import("../../orm/src/index.js").then((orm) => orm.closeDatabase()).catch(() => {});
|