tina4-nodejs 3.13.94 → 3.13.95

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.
Files changed (115) hide show
  1. package/CLAUDE.md +157 -28
  2. package/README.md +1 -1
  3. package/package.json +2 -1
  4. package/packages/cli/dist/bin.js +32418 -29638
  5. package/packages/cli/src/commands/metrics.ts +17 -11
  6. package/packages/cli/src/commands/serve.ts +10 -9
  7. package/packages/core/dist/index.js +32364 -29501
  8. package/packages/core/src/ai.ts +7 -1
  9. package/packages/core/src/auth.ts +191 -39
  10. package/packages/core/src/background.ts +19 -19
  11. package/packages/core/src/cache.ts +492 -49
  12. package/packages/core/src/devAdmin.ts +79 -32
  13. package/packages/core/src/dispatchPipeline.ts +285 -0
  14. package/packages/core/src/dotenv.ts +185 -40
  15. package/packages/core/src/index.ts +5 -4
  16. package/packages/core/src/logger.ts +257 -36
  17. package/packages/core/src/mcp.ts +1 -1
  18. package/packages/core/src/messenger.ts +9 -13
  19. package/packages/core/src/metrics.ts +199 -961
  20. package/packages/core/src/middleware.ts +390 -123
  21. package/packages/core/src/queue.ts +188 -32
  22. package/packages/core/src/queueBackends/kafkaBackend.ts +1 -1
  23. package/packages/core/src/queueBackends/liteBackend.ts +13 -0
  24. package/packages/core/src/queueBackends/mongoBackend.ts +101 -9
  25. package/packages/core/src/queueBackends/rabbitmqBackend.ts +22 -4
  26. package/packages/core/src/rateLimiter.ts +10 -5
  27. package/packages/core/src/request.ts +6 -9
  28. package/packages/core/src/response.ts +46 -1
  29. package/packages/core/src/router.ts +29 -4
  30. package/packages/core/src/server.ts +751 -414
  31. package/packages/core/src/session.ts +244 -27
  32. package/packages/core/src/sessionHandlers/databaseHandler.ts +338 -48
  33. package/packages/core/src/sessionHandlers/memcachedHandler.ts +181 -0
  34. package/packages/core/src/sessionHandlers/mongoClient.ts +293 -208
  35. package/packages/core/src/sessionHandlers/mongoHandler.ts +88 -8
  36. package/packages/core/src/sessionHandlers/respClient.ts +16 -147
  37. package/packages/core/src/sessionHandlers/sqlClient.ts +290 -0
  38. package/packages/core/src/sessionHandlers/syncBridge.ts +190 -0
  39. package/packages/core/src/sessionHandlers/syncSocket.ts +236 -0
  40. package/packages/core/src/testClient.ts +18 -5
  41. package/packages/core/src/trustedProxy.ts +249 -0
  42. package/packages/core/src/types.ts +29 -5
  43. package/packages/core/src/websocket.ts +66 -0
  44. package/packages/orm/dist/index.js +22367 -19504
  45. package/packages/orm/src/adapters/firebird.ts +183 -56
  46. package/packages/orm/src/adapters/mongodb.ts +25 -4
  47. package/packages/orm/src/adapters/mssql.ts +114 -29
  48. package/packages/orm/src/adapters/mysql.ts +103 -40
  49. package/packages/orm/src/adapters/odbc.ts +44 -21
  50. package/packages/orm/src/adapters/postgres.ts +118 -26
  51. package/packages/orm/src/adapters/sqlDialect.ts +120 -0
  52. package/packages/orm/src/adapters/sqlite.ts +60 -24
  53. package/packages/orm/src/baseModel.ts +135 -40
  54. package/packages/orm/src/cachedDatabase.ts +43 -19
  55. package/packages/orm/src/connectTimeout.ts +265 -0
  56. package/packages/orm/src/database.ts +237 -197
  57. package/packages/orm/src/databaseResult.ts +65 -13
  58. package/packages/orm/src/databaseUrl.ts +484 -0
  59. package/packages/orm/src/docstore.ts +386 -145
  60. package/packages/orm/src/index.ts +13 -3
  61. package/packages/orm/src/migration.ts +18 -3
  62. package/packages/orm/src/queryBuilder.ts +38 -4
  63. package/packages/orm/src/sqlTranslator.ts +310 -4
  64. package/packages/orm/src/types.ts +15 -4
  65. package/types/core/src/ai.d.ts +1 -1
  66. package/types/core/src/auth.d.ts +28 -5
  67. package/types/core/src/background.d.ts +3 -3
  68. package/types/core/src/cache.d.ts +15 -12
  69. package/types/core/src/dispatchPipeline.d.ts +117 -0
  70. package/types/core/src/dotenv.d.ts +38 -16
  71. package/types/core/src/index.d.ts +5 -6
  72. package/types/core/src/logger.d.ts +93 -16
  73. package/types/core/src/messenger.d.ts +2 -2
  74. package/types/core/src/metrics.d.ts +25 -61
  75. package/types/core/src/middleware.d.ts +134 -11
  76. package/types/core/src/queue.d.ts +54 -5
  77. package/types/core/src/queueBackends/kafkaBackend.d.ts +1 -1
  78. package/types/core/src/queueBackends/liteBackend.d.ts +9 -0
  79. package/types/core/src/queueBackends/mongoBackend.d.ts +24 -2
  80. package/types/core/src/queueBackends/rabbitmqBackend.d.ts +3 -3
  81. package/types/core/src/router.d.ts +14 -3
  82. package/types/core/src/server.d.ts +15 -0
  83. package/types/core/src/session.d.ts +87 -2
  84. package/types/core/src/sessionHandlers/databaseHandler.d.ts +60 -5
  85. package/types/core/src/sessionHandlers/memcachedHandler.d.ts +60 -0
  86. package/types/core/src/sessionHandlers/mongoClient.d.ts +16 -5
  87. package/types/core/src/sessionHandlers/mongoHandler.d.ts +51 -3
  88. package/types/core/src/sessionHandlers/respClient.d.ts +2 -2
  89. package/types/core/src/sessionHandlers/sqlClient.d.ts +39 -0
  90. package/types/core/src/sessionHandlers/syncBridge.d.ts +91 -0
  91. package/types/core/src/sessionHandlers/syncSocket.d.ts +49 -0
  92. package/types/core/src/trustedProxy.d.ts +44 -0
  93. package/types/core/src/types.d.ts +28 -5
  94. package/types/core/src/websocket.d.ts +26 -0
  95. package/types/orm/src/adapters/firebird.d.ts +55 -10
  96. package/types/orm/src/adapters/mongodb.d.ts +2 -2
  97. package/types/orm/src/adapters/mssql.d.ts +18 -11
  98. package/types/orm/src/adapters/mysql.d.ts +11 -10
  99. package/types/orm/src/adapters/odbc.d.ts +9 -12
  100. package/types/orm/src/adapters/postgres.d.ts +11 -10
  101. package/types/orm/src/adapters/sqlDialect.d.ts +71 -0
  102. package/types/orm/src/adapters/sqlite.d.ts +15 -3
  103. package/types/orm/src/baseModel.d.ts +45 -9
  104. package/types/orm/src/cachedDatabase.d.ts +18 -5
  105. package/types/orm/src/connectTimeout.d.ts +100 -0
  106. package/types/orm/src/database.d.ts +72 -26
  107. package/types/orm/src/databaseResult.d.ts +24 -0
  108. package/types/orm/src/databaseUrl.d.ts +125 -0
  109. package/types/orm/src/docstore.d.ts +102 -43
  110. package/types/orm/src/index.d.ts +5 -2
  111. package/types/orm/src/queryBuilder.d.ts +23 -3
  112. package/types/orm/src/sqlTranslator.d.ts +126 -2
  113. package/types/orm/src/types.d.ts +14 -4
  114. package/packages/core/src/sessionHandlers/redisHandler.ts +0 -219
  115. package/types/core/src/sessionHandlers/redisHandler.d.ts +0 -60
@@ -293,11 +293,56 @@ export function createResponse(res: ServerResponse): Tina4Response {
293
293
  return response.json({ error: true, code, message, status: statusCode }, statusCode);
294
294
  };
295
295
 
296
- response.file = function (filePath: string, options?: { download?: boolean; contentType?: string }): Tina4Response {
296
+ response.file = function (
297
+ filePath: string,
298
+ options?: { download?: boolean; contentType?: string; root?: string },
299
+ ): Tina4Response {
297
300
  if (res.headersSent) return response;
298
301
 
302
+ // SECURITY: confine the read. The natural spelling of a download route,
303
+ //
304
+ // response.file("downloads/" + name) // name = "../secret.env"
305
+ //
306
+ // served any file the process could read - measured over real HTTP at 200
307
+ // with the contents of a .env one directory above the intended one.
308
+ //
309
+ // TWO checks. Containment ALONE does not close it: that payload resolves to
310
+ // <project>/secret.env, which IS inside the project root, and the project
311
+ // root is exactly where .env lives. Rejecting ".." on the way in is the
312
+ // check that closes it; containment then catches absolute paths and
313
+ // symlinks, neither of which carries a ".." segment. Same shape as
314
+ // static.ts's startsWith guard, which this function never had.
315
+ // Containment ONLY when a root is declared. Defaulting to cwd broke every
316
+ // legitimate absolute path.
317
+ const base = options?.root ? nodePath.resolve(options.root) : null;
318
+ let forbidden = filePath.split(/[\\/]/).includes("..");
319
+
320
+ if (!forbidden) {
321
+ const candidate = (base === null || nodePath.isAbsolute(filePath)) ? filePath : nodePath.join(base, filePath);
322
+ let resolved = candidate;
323
+ try {
324
+ resolved = fs.realpathSync(candidate);
325
+ } catch {
326
+ /* missing file: fall through to the 404 below with the joined path */
327
+ }
328
+ if (base !== null && base !== nodePath.sep && resolved !== base && !resolved.startsWith(base + nodePath.sep)) {
329
+ forbidden = true;
330
+ } else {
331
+ filePath = resolved;
332
+ }
333
+ }
334
+
335
+ if (forbidden) {
336
+ // Refuse BEFORE reading: never load bytes we will not send.
337
+ res.statusCode = 403;
338
+ safeSetHeader("Content-Type", "text/plain");
339
+ safeEnd("Forbidden");
340
+ return response;
341
+ }
342
+
299
343
  if (!fs.existsSync(filePath)) {
300
344
  res.statusCode = 404;
345
+ safeSetHeader("Content-Type", "text/plain");
301
346
  safeEnd("File not found");
302
347
  return response;
303
348
  }
@@ -1,5 +1,6 @@
1
1
  import type { RouteHandler, RouteDefinition, RouteMeta, Middleware, MiddlewareSpec, Tina4Request, Tina4Response, WebSocketRouteHandler, WebSocketRouteDefinition } from "./types.js";
2
2
  import { isTruthy } from "./dotenv.js";
3
+ import { MiddlewareRunner, isMiddlewareClass } from "./middleware.js";
3
4
 
4
5
  /**
5
6
  * Whether `TINA4_TRAILING_SLASH_REDIRECT` is enabled.
@@ -821,16 +822,29 @@ export async function resolveStringMiddleware(spec: string): Promise<Middleware>
821
822
  /**
822
823
  * Resolve a single route-middleware spec to a middleware function. Functions
823
824
  * pass through unchanged; strings are resolved via resolveStringMiddleware.
825
+ * A middleware CLASS never reaches here — runRouteMiddlewares runs it through
826
+ * the MiddlewareRunner instead.
824
827
  */
825
828
  async function resolveMiddlewareSpec(spec: MiddlewareSpec): Promise<Middleware> {
826
- return typeof spec === "string" ? resolveStringMiddleware(spec) : spec;
829
+ return typeof spec === "string" ? resolveStringMiddleware(spec) : spec as Middleware;
827
830
  }
828
831
 
829
832
  /**
830
- * Run per-route middleware chain, then call the handler.
833
+ * Run the per-route middleware chain. Returns false when it short-circuited
834
+ * and the handler must be skipped.
831
835
  *
832
- * Accepts middleware functions and/or string specs (e.g. "ResponseCache:300").
833
- * Each spec is resolved to a middleware function just before it runs.
836
+ * Accepts middleware functions, middleware CLASSES, and string specs
837
+ * (e.g. "ResponseCache:300"). A function or string spec is resolved and
838
+ * invoked as `mw(req, res, next)` exactly as before.
839
+ *
840
+ * A CLASS runs its beforeX hooks through the SAME `MiddlewareRunner.runBefore`
841
+ * and the SAME return-value table as global middleware — no parallel runner.
842
+ * Its afterX hooks run with the global after pass once the handler is done
843
+ * (server.ts / testClient.ts append the route's classes to that list), because
844
+ * "after" means after the handler, not after this function. Every spec used to
845
+ * be invoked as `mw(req, res, next)`, which for a class throws "Class
846
+ * constructor cannot be invoked without 'new'", so a class attached per-route
847
+ * was inert. Python and PHP both ran per-route class hooks already.
834
848
  */
835
849
  export async function runRouteMiddlewares(
836
850
  middlewares: MiddlewareSpec[],
@@ -838,6 +852,17 @@ export async function runRouteMiddlewares(
838
852
  res: Tina4Response,
839
853
  ): Promise<boolean> {
840
854
  for (const spec of middlewares) {
855
+ if (isMiddlewareClass(spec)) {
856
+ // tina4: a class hook that returns a DIFFERENT [req, res] pair cannot be
857
+ // rebound here — this returns a bool and is public API (four of this
858
+ // repo's own test files import it). Node's req/res are per-request
859
+ // singletons, so every built-in returns the same pair back; widen the
860
+ // return type if that ever stops being true.
861
+ const [, , proceed] = await MiddlewareRunner.runBefore([spec], req, res);
862
+ if (!proceed || res.raw.writableEnded) return false;
863
+ continue;
864
+ }
865
+
841
866
  const mw = await resolveMiddlewareSpec(spec);
842
867
  let nextCalled = false;
843
868
  await mw(req, res, () => {