tina4-nodejs 3.13.82 → 3.13.83
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/CLAUDE.md +2 -2
- package/package.json +1 -1
- package/packages/core/src/server.ts +98 -9
package/CLAUDE.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.
|
|
1
|
+
# CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.83)
|
|
2
2
|
|
|
3
3
|
> This file helps AI assistants (Claude, Copilot, Cursor, etc.) understand and work on this codebase effectively.
|
|
4
4
|
|
|
5
5
|
## What This Project Is
|
|
6
6
|
|
|
7
|
-
Tina4 for Node.js/TypeScript v3.13.
|
|
7
|
+
Tina4 for Node.js/TypeScript v3.13.83 - The Intelligent Native Application 4ramework. A convention-over-configuration structural paradigm. The developer writes TypeScript; Tina4 is invisible infrastructure.
|
|
8
8
|
|
|
9
9
|
The philosophy: zero ceremony, batteries included, file system as source of truth.
|
|
10
10
|
|
package/package.json
CHANGED
|
@@ -34,6 +34,73 @@ const BUILTIN_ERROR_TEMPLATES_DIR = resolve(__dirname, "..", "templates");
|
|
|
34
34
|
/** Built-in public directory for framework-bundled static assets. */
|
|
35
35
|
const BUILTIN_PUBLIC_DIR = resolve(__dirname, "..", "public");
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Whether the framework's bundled Swagger UI assets (public/swagger/*) may be
|
|
39
|
+
* served.
|
|
40
|
+
*
|
|
41
|
+
* Static files are resolved BEFORE routes, so the shipped
|
|
42
|
+
* public/swagger/index.html answered `GET /swagger` even when
|
|
43
|
+
* `swaggerEnabled()` was false -- serving the Swagger UI in production and
|
|
44
|
+
* bypassing the documented TINA4_SWAGGER_ENABLED / TINA4_DEBUG gate entirely.
|
|
45
|
+
* The symptom was a 200 on /swagger with a 404 on /swagger/openapi.json (the
|
|
46
|
+
* gated route never registered, the static file still won).
|
|
47
|
+
*
|
|
48
|
+
* Set from `swaggerEnabled()` at boot. Stays false when swagger is disabled OR
|
|
49
|
+
* when the swagger module fails to load -- fail closed, never expose.
|
|
50
|
+
*/
|
|
51
|
+
let swaggerAssetsEnabled = false;
|
|
52
|
+
|
|
53
|
+
/** Bundled Swagger UI asset paths that must honour the swagger gate. */
|
|
54
|
+
function isSwaggerAssetPath(pathname: string): boolean {
|
|
55
|
+
return pathname === "/swagger" || pathname.startsWith("/swagger/");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Build the startup banner's optional surface lines (issue #99).
|
|
60
|
+
*
|
|
61
|
+
* Only advertise a surface that is actually REACHABLE. In production, or with
|
|
62
|
+
* TINA4_DEBUG off, /swagger and /__dev return 404 -- printing them anyway both
|
|
63
|
+
* misleads an operator into believing a dev surface is exposed and sends a
|
|
64
|
+
* developer to a dead link.
|
|
65
|
+
*
|
|
66
|
+
* Kept as a pure function of (port, two booleans) so the contract is unit
|
|
67
|
+
* testable without booting a server and grepping stdout. Parity: Python
|
|
68
|
+
* banner_surface_lines, PHP App::bannerSurfaceLines, Ruby
|
|
69
|
+
* Tina4.banner_surface_lines.
|
|
70
|
+
*
|
|
71
|
+
* @returns [swaggerLine, dashboardLine] -- each empty, or a newline plus the
|
|
72
|
+
* banner row, ready to interpolate.
|
|
73
|
+
*/
|
|
74
|
+
export function bannerSurfaceLines(
|
|
75
|
+
port: number,
|
|
76
|
+
opts: { swaggerEnabled: boolean; devAdminEnabled: boolean },
|
|
77
|
+
): [string, string] {
|
|
78
|
+
return [
|
|
79
|
+
opts.swaggerEnabled ? `\n Swagger: http://localhost:${port}/swagger` : "",
|
|
80
|
+
opts.devAdminEnabled ? `\n Dashboard: http://localhost:${port}/__dev` : "",
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Whether the startup banner should ADVERTISE /swagger (issue #99).
|
|
86
|
+
*
|
|
87
|
+
* Mirrors `swaggerEnabled()` in packages/swagger/src/ui.ts: an explicit
|
|
88
|
+
* TINA4_SWAGGER_ENABLED wins, otherwise fall back to TINA4_DEBUG.
|
|
89
|
+
*
|
|
90
|
+
* Read from env here rather than importing the swagger package, because the
|
|
91
|
+
* CLUSTER PRIMARY prints its banner before any optional module is loaded (and a
|
|
92
|
+
* dynamic import for one banner line is not worth the boot cost). Keep this in
|
|
93
|
+
* sync with ui.ts -- it is the same two-line contract.
|
|
94
|
+
*/
|
|
95
|
+
function swaggerAdvertised(): boolean {
|
|
96
|
+
const TRUTHY = ["true", "1", "yes", "on"];
|
|
97
|
+
const raw = (process.env.TINA4_SWAGGER_ENABLED ?? "").trim().toLowerCase();
|
|
98
|
+
if (raw === "") {
|
|
99
|
+
return TRUTHY.includes((process.env.TINA4_DEBUG ?? "").trim().toLowerCase());
|
|
100
|
+
}
|
|
101
|
+
return TRUTHY.includes(raw);
|
|
102
|
+
}
|
|
103
|
+
|
|
37
104
|
/**
|
|
38
105
|
* Apply pending DB migrations on startup — NON-BREAKING.
|
|
39
106
|
*
|
|
@@ -787,6 +854,15 @@ export async function startServer(config?: Tina4Config): Promise<{
|
|
|
787
854
|
const logLevel = (process.env.TINA4_LOG_LEVEL ?? "DEBUG").toUpperCase();
|
|
788
855
|
|
|
789
856
|
if (!isBannerSuppressed()) {
|
|
857
|
+
// Only advertise a surface that is actually reachable (issue #99).
|
|
858
|
+
// Cluster mode is the production path: debug is OFF, so /__dev always
|
|
859
|
+
// 404s here and is never advertised; /swagger only when explicitly on.
|
|
860
|
+
// Cluster mode is the production path: debug is OFF, so /__dev never
|
|
861
|
+
// advertises here.
|
|
862
|
+
const [swaggerLine] = bannerSurfaceLines(port, {
|
|
863
|
+
swaggerEnabled: swaggerAdvertised(),
|
|
864
|
+
devAdminEnabled: false,
|
|
865
|
+
});
|
|
790
866
|
console.log(`${color}
|
|
791
867
|
______ _ __ __
|
|
792
868
|
/_ __/(_)___ ____ _/ // /
|
|
@@ -796,9 +872,7 @@ export async function startServer(config?: Tina4Config): Promise<{
|
|
|
796
872
|
${reset}
|
|
797
873
|
Tina4 Node.js v${TINA4_VERSION} — The Intelligent Native Application 4ramework
|
|
798
874
|
|
|
799
|
-
Server: http://${displayHost}:${port} (cluster, ${numCPUs} workers)
|
|
800
|
-
Swagger: http://localhost:${port}/swagger
|
|
801
|
-
Dashboard: http://localhost:${port}/__dev
|
|
875
|
+
Server: http://${displayHost}:${port} (cluster, ${numCPUs} workers)${swaggerLine}
|
|
802
876
|
Debug: OFF (Log level: ${logLevel})
|
|
803
877
|
`);
|
|
804
878
|
}
|
|
@@ -1000,7 +1074,10 @@ ${reset}
|
|
|
1000
1074
|
// when disabled.
|
|
1001
1075
|
try {
|
|
1002
1076
|
const swagger = await import("../../swagger/src/index.js");
|
|
1003
|
-
|
|
1077
|
+
// Single source of truth for BOTH the gated routes and the bundled
|
|
1078
|
+
// public/swagger assets (which static serving would otherwise expose).
|
|
1079
|
+
swaggerAssetsEnabled = swagger.swaggerEnabled();
|
|
1080
|
+
if (!swaggerAssetsEnabled) {
|
|
1004
1081
|
// Skip the rest of the swagger block when disabled.
|
|
1005
1082
|
throw new Error("__swagger_disabled__");
|
|
1006
1083
|
}
|
|
@@ -1273,8 +1350,14 @@ ${reset}
|
|
|
1273
1350
|
if (existsSync(srcPublicDir) && tryServeStatic(srcPublicDir, req, res)) {
|
|
1274
1351
|
return;
|
|
1275
1352
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1353
|
+
// Framework-bundled assets. The Swagger UI lives here (public/swagger/),
|
|
1354
|
+
// and static files resolve BEFORE routes -- so this path MUST honour the
|
|
1355
|
+
// swagger gate or /swagger is served in production regardless of
|
|
1356
|
+
// TINA4_SWAGGER_ENABLED / TINA4_DEBUG.
|
|
1357
|
+
if (swaggerAssetsEnabled || !isSwaggerAssetPath(pathname)) {
|
|
1358
|
+
if (tryServeStatic(BUILTIN_PUBLIC_DIR, req, res)) {
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1278
1361
|
}
|
|
1279
1362
|
|
|
1280
1363
|
// Match route
|
|
@@ -1602,6 +1685,14 @@ ${reset}
|
|
|
1602
1685
|
: "";
|
|
1603
1686
|
|
|
1604
1687
|
if (!isBannerSuppressed()) {
|
|
1688
|
+
// Only advertise a surface that is actually reachable (issue #99). With
|
|
1689
|
+
// debug off / in production these endpoints 404, and printing a dead URL
|
|
1690
|
+
// both misleads an operator into believing a dev surface is exposed and
|
|
1691
|
+
// sends a developer to a 404.
|
|
1692
|
+
const [swaggerLine, dashboardLine] = bannerSurfaceLines(port, {
|
|
1693
|
+
swaggerEnabled: swaggerAdvertised(),
|
|
1694
|
+
devAdminEnabled: isDebug,
|
|
1695
|
+
});
|
|
1605
1696
|
console.log(`${color}
|
|
1606
1697
|
______ _ __ __
|
|
1607
1698
|
/_ __/(_)___ ____ _/ // /
|
|
@@ -1611,9 +1702,7 @@ ${reset}
|
|
|
1611
1702
|
${reset}
|
|
1612
1703
|
Tina4 Node.js v${TINA4_VERSION} — The Intelligent Native Application 4ramework
|
|
1613
1704
|
|
|
1614
|
-
Server: http://${displayHost}:${port} (${serverMode})
|
|
1615
|
-
Swagger: http://localhost:${port}/swagger
|
|
1616
|
-
Dashboard: http://localhost:${port}/__dev
|
|
1705
|
+
Server: http://${displayHost}:${port} (${serverMode})${swaggerLine}${dashboardLine}
|
|
1617
1706
|
Debug: ${isDebug ? "ON" : "OFF"} (Log level: ${logLevel})${dualPortLines}
|
|
1618
1707
|
`);
|
|
1619
1708
|
}
|