tina4-nodejs 3.13.91 → 3.13.94

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 (135) hide show
  1. package/CLAUDE.md +16 -3
  2. package/README.md +1 -1
  3. package/package.json +12 -9
  4. package/packages/cli/dist/bin.js +1312 -987
  5. package/packages/core/dist/index.js +1312 -987
  6. package/packages/core/src/auth.ts +141 -21
  7. package/packages/core/src/devMailbox.ts +20 -44
  8. package/packages/core/src/index.ts +2 -2
  9. package/packages/core/src/messenger.ts +72 -0
  10. package/packages/core/src/queueBackends/kafkaBackend.ts +108 -12
  11. package/packages/core/src/sessionHandlers/childError.ts +72 -0
  12. package/packages/core/src/sessionHandlers/mongoClient.ts +9 -3
  13. package/packages/core/src/sessionHandlers/redisHandler.ts +18 -5
  14. package/packages/core/src/sessionHandlers/respClient.ts +5 -1
  15. package/packages/frond/dist/index.js +74 -31
  16. package/packages/frond/src/engine.ts +99 -33
  17. package/packages/orm/dist/index.js +3102 -2777
  18. package/packages/orm/src/adapters/sqlite.ts +4 -1
  19. package/packages/orm/src/database.ts +108 -8
  20. package/types/cli/src/bin.d.ts +92 -0
  21. package/types/cli/src/commands/build.d.ts +2 -0
  22. package/types/cli/src/commands/generate.d.ts +47 -0
  23. package/types/cli/src/commands/init.d.ts +1 -0
  24. package/types/cli/src/commands/metrics.d.ts +6 -0
  25. package/types/cli/src/commands/migrate.d.ts +1 -0
  26. package/types/cli/src/commands/migrateCreate.d.ts +1 -0
  27. package/types/cli/src/commands/migrateRollback.d.ts +1 -0
  28. package/types/cli/src/commands/migrateStatus.d.ts +1 -0
  29. package/types/cli/src/commands/queue.d.ts +20 -0
  30. package/types/cli/src/commands/routes.d.ts +1 -0
  31. package/types/cli/src/commands/seed.d.ts +1 -0
  32. package/types/cli/src/commands/serve.d.ts +6 -0
  33. package/types/cli/src/commands/test.d.ts +1 -0
  34. package/types/core/src/ai.d.ts +64 -0
  35. package/types/core/src/api.d.ts +262 -0
  36. package/types/core/src/auth.d.ts +154 -0
  37. package/types/core/src/authGate.d.ts +20 -0
  38. package/types/core/src/background.d.ts +34 -0
  39. package/types/core/src/cache.d.ts +160 -0
  40. package/types/core/src/constants.d.ts +38 -0
  41. package/types/core/src/container.d.ts +44 -0
  42. package/types/core/src/context/chunker.d.ts +31 -0
  43. package/types/core/src/context/index.d.ts +93 -0
  44. package/types/core/src/devAdmin.d.ts +179 -0
  45. package/types/core/src/devMailbox.d.ts +54 -0
  46. package/types/core/src/docs.d.ts +141 -0
  47. package/types/core/src/docsAutoDiscovery.d.ts +6 -0
  48. package/types/core/src/dotenv.d.ts +65 -0
  49. package/types/core/src/env.d.ts +28 -0
  50. package/types/core/src/errorOverlay.d.ts +36 -0
  51. package/types/core/src/events.d.ts +75 -0
  52. package/types/core/src/fakeData.d.ts +55 -0
  53. package/types/core/src/feedback.d.ts +90 -0
  54. package/types/core/src/graphql.d.ts +207 -0
  55. package/types/core/src/health.d.ts +22 -0
  56. package/types/core/src/htmlElement.d.ts +75 -0
  57. package/types/core/src/i18n.d.ts +37 -0
  58. package/types/core/src/index.d.ts +93 -0
  59. package/types/core/src/job.d.ts +39 -0
  60. package/types/core/src/logger.d.ts +123 -0
  61. package/types/core/src/mcp.d.ts +248 -0
  62. package/types/core/src/messenger.d.ts +191 -0
  63. package/types/core/src/metrics.d.ts +77 -0
  64. package/types/core/src/middleware.d.ts +207 -0
  65. package/types/core/src/mqtt.d.ts +257 -0
  66. package/types/core/src/mqttMessage.d.ts +67 -0
  67. package/types/core/src/plan.d.ts +96 -0
  68. package/types/core/src/projectIndex.d.ts +56 -0
  69. package/types/core/src/queue.d.ts +219 -0
  70. package/types/core/src/queueBackends/kafkaBackend.d.ts +117 -0
  71. package/types/core/src/queueBackends/liteBackend.d.ts +119 -0
  72. package/types/core/src/queueBackends/mongoBackend.d.ts +97 -0
  73. package/types/core/src/queueBackends/rabbitmqBackend.d.ts +55 -0
  74. package/types/core/src/rateLimiter.d.ts +49 -0
  75. package/types/core/src/request.d.ts +25 -0
  76. package/types/core/src/response.d.ts +28 -0
  77. package/types/core/src/routeDiscovery.d.ts +12 -0
  78. package/types/core/src/router.d.ts +355 -0
  79. package/types/core/src/scss.d.ts +19 -0
  80. package/types/core/src/server.d.ts +131 -0
  81. package/types/core/src/service.d.ts +115 -0
  82. package/types/core/src/session.d.ts +256 -0
  83. package/types/core/src/sessionHandlers/childError.d.ts +34 -0
  84. package/types/core/src/sessionHandlers/databaseHandler.d.ts +42 -0
  85. package/types/core/src/sessionHandlers/mongoClient.d.ts +24 -0
  86. package/types/core/src/sessionHandlers/mongoHandler.d.ts +61 -0
  87. package/types/core/src/sessionHandlers/redisHandler.d.ts +60 -0
  88. package/types/core/src/sessionHandlers/respClient.d.ts +22 -0
  89. package/types/core/src/sessionHandlers/valkeyHandler.d.ts +65 -0
  90. package/types/core/src/static.d.ts +2 -0
  91. package/types/core/src/test.d.ts +94 -0
  92. package/types/core/src/testClient.d.ts +36 -0
  93. package/types/core/src/testing.d.ts +58 -0
  94. package/types/core/src/types.d.ts +219 -0
  95. package/types/core/src/validator.d.ts +52 -0
  96. package/types/core/src/websocket.d.ts +376 -0
  97. package/types/core/src/websocketBackplane.d.ts +166 -0
  98. package/types/core/src/websocketConnection.d.ts +54 -0
  99. package/types/core/src/wsdl.d.ts +101 -0
  100. package/types/frond/src/engine.d.ts +263 -0
  101. package/types/frond/src/index.d.ts +2 -0
  102. package/types/orm/src/adapters/firebird.d.ts +138 -0
  103. package/types/orm/src/adapters/mongodb.d.ts +81 -0
  104. package/types/orm/src/adapters/mssql.d.ts +70 -0
  105. package/types/orm/src/adapters/mysql.d.ts +66 -0
  106. package/types/orm/src/adapters/odbc.d.ts +97 -0
  107. package/types/orm/src/adapters/postgres.d.ts +85 -0
  108. package/types/orm/src/adapters/sqlite.d.ts +56 -0
  109. package/types/orm/src/autoCrud.d.ts +73 -0
  110. package/types/orm/src/baseModel.d.ts +391 -0
  111. package/types/orm/src/cachedDatabase.d.ts +177 -0
  112. package/types/orm/src/database.d.ts +609 -0
  113. package/types/orm/src/databaseResult.d.ts +85 -0
  114. package/types/orm/src/docstore.d.ts +182 -0
  115. package/types/orm/src/fakeData.d.ts +22 -0
  116. package/types/orm/src/index.d.ts +40 -0
  117. package/types/orm/src/migration.d.ts +275 -0
  118. package/types/orm/src/model.d.ts +7 -0
  119. package/types/orm/src/query.d.ts +14 -0
  120. package/types/orm/src/queryBuilder.d.ts +173 -0
  121. package/types/orm/src/realtime/index.d.ts +7 -0
  122. package/types/orm/src/realtime/models/attachment.d.ts +43 -0
  123. package/types/orm/src/realtime/models/channel.d.ts +32 -0
  124. package/types/orm/src/realtime/models/channelMember.d.ts +32 -0
  125. package/types/orm/src/realtime/models/message.d.ts +36 -0
  126. package/types/orm/src/realtime/models/workspace.d.ts +26 -0
  127. package/types/orm/src/realtime/realtime.d.ts +24 -0
  128. package/types/orm/src/realtime/storage.d.ts +61 -0
  129. package/types/orm/src/seeder.d.ts +118 -0
  130. package/types/orm/src/sqlTranslator.d.ts +134 -0
  131. package/types/orm/src/types.d.ts +138 -0
  132. package/types/orm/src/validation.d.ts +6 -0
  133. package/types/swagger/src/generator.d.ts +46 -0
  134. package/types/swagger/src/index.d.ts +2 -0
  135. package/types/swagger/src/ui.d.ts +11 -0
@@ -0,0 +1,28 @@
1
+ import type { ServerResponse } from "node:http";
2
+ import type { Tina4Response } from "./types.js";
3
+ /**
4
+ * Set the default templates directory for render().
5
+ * Called by server.ts during startup.
6
+ */
7
+ export declare function setDefaultTemplatesDir(dir: string): void;
8
+ /**
9
+ * Return the global Frond engine, creating a default if needed.
10
+ */
11
+ export declare function getFrond(): Promise<InstanceType<any>>;
12
+ /**
13
+ * Return the singleton Frond engine for built-in framework templates.
14
+ * Syncs custom filters/globals from the user engine.
15
+ */
16
+ export declare function getFrameworkFrond(): Promise<InstanceType<any> | null>;
17
+ /**
18
+ * Register a pre-configured Frond engine for response.render().
19
+ */
20
+ export declare function setFrond(engine: InstanceType<any>): void;
21
+ export declare function createResponse(res: ServerResponse): Tina4Response;
22
+ /**
23
+ * Build a standard error response envelope (standalone helper).
24
+ *
25
+ * Usage:
26
+ * return response(errorResponse("VALIDATION_FAILED", "Email is required", 400), 400);
27
+ */
28
+ export declare function errorResponse(code: string, message: string, status?: number): Record<string, unknown>;
@@ -0,0 +1,12 @@
1
+ import type { RouteDefinition } from "./types.js";
2
+ export declare function discoverRoutes(routesDir: string): Promise<RouteDefinition[]>;
3
+ /**
4
+ * Re-run the most recent route scan — called by POST /__dev/api/reload so a
5
+ * newly-added OR edited file in src/routes/ registers without a server restart.
6
+ * A file is re-imported when it's new or its mtime increased; unchanged files
7
+ * are skipped. The router replaces routes by pattern, so a re-imported route
8
+ * overwrites the stale handler. No-op if discoverRoutes() has never been called.
9
+ */
10
+ export declare function rediscoverRoutes(): Promise<RouteDefinition[]>;
11
+ /** Test-only: reset the seen-files state so tests can replay the same dir. */
12
+ export declare function _resetRouteDiscovery(): void;
@@ -0,0 +1,355 @@
1
+ import type { RouteHandler, RouteDefinition, RouteMeta, Middleware, MiddlewareSpec, Tina4Request, Tina4Response, WebSocketRouteHandler, WebSocketRouteDefinition } from "./types.js";
2
+ /**
3
+ * Whether `TINA4_TRAILING_SLASH_REDIRECT` is enabled.
4
+ *
5
+ * Default: false. When true, a request to `/foo/` that has no exact match
6
+ * but matches `/foo` will be treated as a hit on `/foo` — callers can use
7
+ * the returned pattern to issue a 308 redirect (Python parity).
8
+ */
9
+ export declare function isTrailingSlashRedirectEnabled(): boolean;
10
+ interface MatchResult {
11
+ handler: RouteHandler;
12
+ params: Record<string, string | number>;
13
+ pattern: string;
14
+ meta?: RouteMeta;
15
+ middlewares?: MiddlewareSpec[];
16
+ template?: string;
17
+ secure?: boolean;
18
+ cached?: boolean;
19
+ noAuth?: boolean;
20
+ }
21
+ interface CompiledRoute {
22
+ pattern: string;
23
+ regex: RegExp;
24
+ paramNames: string[];
25
+ paramTypes: string[];
26
+ handler: RouteHandler;
27
+ meta?: RouteMeta;
28
+ filePath?: string;
29
+ middlewares?: MiddlewareSpec[];
30
+ secure?: boolean;
31
+ cached?: boolean;
32
+ noAuth?: boolean;
33
+ cacheStore?: Map<string, {
34
+ data: unknown;
35
+ expires: number;
36
+ }>;
37
+ cacheTtl?: number;
38
+ template?: string;
39
+ }
40
+ /**
41
+ * Thin reference to a registered WebSocket route, enabling chained modifiers
42
+ * — the WS analogue of {@link RouteRef}.
43
+ *
44
+ * Usage:
45
+ * router.websocket("/ws/secure", handler).secure();
46
+ */
47
+ export declare class WsRouteRef {
48
+ private route;
49
+ constructor(route: WebSocketRouteDefinition);
50
+ /** Mark this WS route as requiring a valid JWT on the upgrade handshake. */
51
+ secure(): this;
52
+ }
53
+ /**
54
+ * Thin reference to a registered route, enabling chained modifiers.
55
+ *
56
+ * Usage:
57
+ * router.get("/api/data", handler).secure().cache();
58
+ */
59
+ export declare class RouteRef {
60
+ private route;
61
+ constructor(route: CompiledRoute);
62
+ /** Mark this route as requiring bearer-token authentication. */
63
+ secure(): this;
64
+ /** Opt out of secure-by-default auth (for public write routes). */
65
+ noAuth(): this;
66
+ /** Mark this route's response as cacheable. */
67
+ cache(): this;
68
+ /**
69
+ * Append middleware to this route. Accepts middleware functions and/or
70
+ * string specs (e.g. `"ResponseCache:300"`), resolved when the route runs.
71
+ */
72
+ middleware(...middlewareClasses: MiddlewareSpec[]): this;
73
+ }
74
+ export interface RouteInfo {
75
+ method: string;
76
+ path: string;
77
+ handler: string;
78
+ middlewareCount: number;
79
+ cached: boolean;
80
+ secure: boolean;
81
+ }
82
+ export declare class Router {
83
+ private routes;
84
+ private wsRoutes;
85
+ /** Class-based middleware registered via `use()` / `Router.use()`. */
86
+ private static _classMiddlewares;
87
+ /**
88
+ * Register a class-based middleware (beforeX / afterX convention).
89
+ * Classes are stored globally and executed by MiddlewareRunner.
90
+ */
91
+ static use(middlewareClass: any): void;
92
+ /**
93
+ * Get all registered class-based middleware classes.
94
+ */
95
+ static getClassMiddlewares(): any[];
96
+ /**
97
+ * Clear all registered class-based middleware (useful for testing).
98
+ */
99
+ static clearClassMiddlewares(): void;
100
+ /**
101
+ * Add a raw route definition (used internally and by file-based routing).
102
+ */
103
+ addRoute(definition: RouteDefinition): RouteRef;
104
+ /**
105
+ * Register a GET route programmatically.
106
+ */
107
+ get(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
108
+ /**
109
+ * Register a POST route programmatically.
110
+ */
111
+ post(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
112
+ /**
113
+ * Register a PUT route programmatically.
114
+ */
115
+ put(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
116
+ /**
117
+ * Register a PATCH route programmatically.
118
+ */
119
+ patch(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
120
+ /**
121
+ * Register a DELETE route programmatically.
122
+ */
123
+ delete(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
124
+ /**
125
+ * Register an explicit HEAD route. By default the framework auto-handles
126
+ * HEAD by falling back to the GET route and stripping the body
127
+ * (RFC 9110 §9.3.2). Use this only when you need a HEAD handler that
128
+ * does something different from GET — e.g. cheaper existence-check
129
+ * logic, custom validator headers without the cost of building the body.
130
+ * The framework still strips the response body for you on the way out.
131
+ */
132
+ head(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
133
+ /**
134
+ * Register an explicit OPTIONS route. By default the framework auto-
135
+ * handles OPTIONS by building an Allow header from every method
136
+ * registered for the path and returning 204 (RFC 9110 §9.3.7). Use
137
+ * this to take over that behaviour.
138
+ */
139
+ options(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
140
+ /**
141
+ * Register a route that matches ANY HTTP method.
142
+ */
143
+ any(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
144
+ /**
145
+ * Create a route group with a shared prefix and optional middlewares.
146
+ */
147
+ group(prefix: string, callback: (group: RouteGroup) => void, middlewares?: MiddlewareSpec[]): void;
148
+ /**
149
+ * Match a request method + pathname to a registered route.
150
+ *
151
+ * When `TINA4_TRAILING_SLASH_REDIRECT=true` and the request path ends in
152
+ * a trailing slash that doesn't match a registered route, retry without
153
+ * the trailing slash. Returning the de-slashed pattern lets callers issue
154
+ * a 308 redirect instead of a hard 404 — Python parity.
155
+ */
156
+ match(method: string, path: string): MatchResult | null;
157
+ /**
158
+ * Return the list of HTTP methods registered for ``path``, in canonical
159
+ * order GET / POST / PUT / PATCH / DELETE / HEAD / OPTIONS. Used by the
160
+ * dispatcher to build the ``Allow:`` header on 405 / OPTIONS responses
161
+ * (RFC 9110 §10.2.1, §9.3.7).
162
+ *
163
+ * If GET is registered, HEAD is appended implicitly (HEAD auto-fallback).
164
+ * OPTIONS is appended whenever any method exists for the path (the
165
+ * framework auto-handles OPTIONS).
166
+ */
167
+ methodsAllowedForPath(path: string): string[];
168
+ /** Inner match against a list of compiled routes, no trailing-slash logic. */
169
+ private matchRoute;
170
+ /**
171
+ * Get all registered route definitions.
172
+ */
173
+ getRoutes(): RouteDefinition[];
174
+ /** Alias for getRoutes(). */
175
+ allRoutes(): RouteDefinition[];
176
+ /**
177
+ * List all routes in a debug-friendly format for CLI output.
178
+ */
179
+ listRoutes(): RouteInfo[];
180
+ /**
181
+ * Register a WebSocket route.
182
+ *
183
+ * A WS route is PUBLIC by default (mirrors GET). It can be marked secured in
184
+ * EITHER way the HTTP routes support:
185
+ * • imperatively — `websocket(path, fn, { secured: true })`, or chain the
186
+ * returned ref: `websocket(path, fn).secure()`;
187
+ * • decorator-style — a `_secured` flag on the handler function, set in
188
+ * either order relative to registration (the ref keeps a back-reference
189
+ * to the route so a later `.secure()` / `_secured` still flips it).
190
+ *
191
+ * When secured, the upgrade handshake requires a valid JWT (Authorization
192
+ * header / `bearer` subprotocol / `?token=`) or the upgrade is rejected.
193
+ */
194
+ websocket(path: string, handler: WebSocketRouteHandler, options?: {
195
+ secured?: boolean;
196
+ }): WsRouteRef;
197
+ /**
198
+ * Get all registered WebSocket route definitions.
199
+ */
200
+ getWebSocketRoutes(): WebSocketRouteDefinition[];
201
+ /**
202
+ * Match a WebSocket upgrade request path to a registered ws route.
203
+ * Returns the route only; use {@link matchWebSocketWithParams} when the
204
+ * upgrade handler needs the extracted `{param}` values.
205
+ */
206
+ matchWebSocket(pathname: string): WebSocketRouteDefinition | null;
207
+ /**
208
+ * Match a WebSocket upgrade path AND extract its `{param}` values, using the
209
+ * same pattern compiler as HTTP routes. A literal pattern (`/ws/chat`) still
210
+ * matches exactly with empty params; a parameterised pattern
211
+ * (`/ws/rtc/{room}`) matches `/ws/rtc/abc` and yields `{ room: "abc" }`.
212
+ * (Previously WS matching was exact-string only, so `{param}` routes never
213
+ * matched and `connection.params` was always empty.)
214
+ */
215
+ matchWebSocketWithParams(pathname: string): {
216
+ route: WebSocketRouteDefinition;
217
+ params: Record<string, string>;
218
+ } | null;
219
+ clear(): void;
220
+ /**
221
+ * Register a route for a specific HTTP method.
222
+ * Core registration method — all convenience methods delegate here.
223
+ */
224
+ static add(method: string, path: string, handler: RouteHandler, middleware?: MiddlewareSpec[], swaggerMeta?: RouteMeta, template?: string): RouteRef;
225
+ /**
226
+ * Register a GET route on the default global router.
227
+ */
228
+ static get(path: string, handler: RouteHandler, middleware?: MiddlewareSpec[], swaggerMeta?: RouteMeta, template?: string): RouteRef;
229
+ /**
230
+ * Register a POST route on the default global router.
231
+ */
232
+ static post(path: string, handler: RouteHandler, middleware?: MiddlewareSpec[], swaggerMeta?: RouteMeta, template?: string): RouteRef;
233
+ /**
234
+ * Register a PUT route on the default global router.
235
+ */
236
+ static put(path: string, handler: RouteHandler, middleware?: MiddlewareSpec[], swaggerMeta?: RouteMeta, template?: string): RouteRef;
237
+ /**
238
+ * Register a PATCH route on the default global router.
239
+ */
240
+ static patch(path: string, handler: RouteHandler, middleware?: MiddlewareSpec[], swaggerMeta?: RouteMeta, template?: string): RouteRef;
241
+ /**
242
+ * Register a DELETE route on the default global router.
243
+ */
244
+ static delete(path: string, handler: RouteHandler, middleware?: MiddlewareSpec[], swaggerMeta?: RouteMeta, template?: string): RouteRef;
245
+ /**
246
+ * Register a route that matches ANY HTTP method on the default global router.
247
+ */
248
+ static any(path: string, handler: RouteHandler, middleware?: MiddlewareSpec[], swaggerMeta?: RouteMeta, template?: string): RouteRef;
249
+ /**
250
+ * Register a WebSocket route on the default global router.
251
+ */
252
+ static websocket(path: string, handler: WebSocketRouteHandler, options?: {
253
+ secured?: boolean;
254
+ }): WsRouteRef;
255
+ /**
256
+ * Match a WebSocket upgrade path against routes on the default global router.
257
+ * Returns the matched route definition (with its `authRequired` flag) or null.
258
+ */
259
+ static matchWebSocket(pathname: string): WebSocketRouteDefinition | null;
260
+ static matchWebSocketWithParams(pathname: string): {
261
+ route: WebSocketRouteDefinition;
262
+ params: Record<string, string>;
263
+ } | null;
264
+ /** All WebSocket route definitions on the default global router. */
265
+ static getWebSocketRoutes(): WebSocketRouteDefinition[];
266
+ /**
267
+ * Create a route group on the default global router.
268
+ */
269
+ static group(prefix: string, callback: (group: RouteGroup) => void, middlewares?: MiddlewareSpec[]): void;
270
+ /**
271
+ * Supported typed-parameter constraints. Mirrored verbatim in
272
+ * tina4-python / tina4-php / tina4-ruby for cross-framework parity.
273
+ *
274
+ * Any type name not in this table throws at route registration time —
275
+ * we never silently fall through to the default matcher, because a
276
+ * typo like `{id:inetger}` would otherwise match anything and create
277
+ * a security footgun (see tina4-book#125).
278
+ */
279
+ private static readonly PARAM_TYPE_PATTERNS;
280
+ private compilePattern;
281
+ }
282
+ /**
283
+ * Route group for grouping routes under a shared prefix with optional middlewares.
284
+ */
285
+ export declare class RouteGroup {
286
+ private router;
287
+ private prefix;
288
+ private groupMiddlewares?;
289
+ constructor(router: Router, prefix: string, groupMiddlewares?: MiddlewareSpec[] | undefined);
290
+ private mergeMiddlewares;
291
+ get(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
292
+ post(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
293
+ put(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
294
+ patch(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
295
+ delete(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
296
+ any(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
297
+ /**
298
+ * Nested groups.
299
+ */
300
+ group(prefix: string, callback: (group: RouteGroup) => void, middlewares?: MiddlewareSpec[]): void;
301
+ }
302
+ /**
303
+ * Resolve a string-form middleware spec to a middleware function.
304
+ *
305
+ * Forms (parity with Python/PHP/Ruby):
306
+ * "ResponseCache" → responseCache() with the default/env TTL
307
+ * "ResponseCache:300" → responseCache({ ttl: 300 })
308
+ *
309
+ * The head before the first ":" names the middleware; any trailing
310
+ * colon-separated parts are its arguments (numeric parts are parsed as
311
+ * integers). Unknown names throw so a typo surfaces instead of silently
312
+ * dropping the middleware. `responseCache` is loaded via a dynamic import so
313
+ * the router carries no import-time dependency on the cache module.
314
+ *
315
+ * Exported so route dispatch (and tests) can turn a spec into a runnable
316
+ * middleware.
317
+ */
318
+ export declare function resolveStringMiddleware(spec: string): Promise<Middleware>;
319
+ /**
320
+ * Run per-route middleware chain, then call the handler.
321
+ *
322
+ * Accepts middleware functions and/or string specs (e.g. "ResponseCache:300").
323
+ * Each spec is resolved to a middleware function just before it runs.
324
+ */
325
+ export declare function runRouteMiddlewares(middlewares: MiddlewareSpec[], req: Tina4Request, res: Tina4Response): Promise<boolean>;
326
+ /**
327
+ * Default global router instance.
328
+ * Top-level get(), post(), etc. register routes here.
329
+ * The server merges these routes on startup.
330
+ */
331
+ export declare const defaultRouter: Router;
332
+ /**
333
+ * Top-level route registration functions — mirrors Python's decorator pattern.
334
+ *
335
+ * Usage:
336
+ * import { get, post } from "@tina4/core";
337
+ *
338
+ * get("/hello", async (req, res) => {
339
+ * res.json({ message: "Hello" });
340
+ * });
341
+ *
342
+ * post("/users/{id}", async (req, res) => {
343
+ * res.json({ id: req.params.id }, 201);
344
+ * });
345
+ */
346
+ export declare function get(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
347
+ export declare function post(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
348
+ export declare function put(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
349
+ export declare function patch(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
350
+ export declare function del(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
351
+ export declare function any(path: string, handler: RouteHandler, middlewares?: MiddlewareSpec[], meta?: RouteMeta): RouteRef;
352
+ export declare function websocket(path: string, handler: WebSocketRouteHandler, options?: {
353
+ secured?: boolean;
354
+ }): WsRouteRef;
355
+ export { del as delete };
@@ -0,0 +1,19 @@
1
+ export interface ScssConfig {
2
+ importPaths?: string[];
3
+ variables?: Record<string, string>;
4
+ }
5
+ export declare class ScssCompiler {
6
+ private _importPaths;
7
+ private _variables;
8
+ constructor(config?: ScssConfig);
9
+ /** Compile an SCSS string to CSS. */
10
+ compile(source: string): string;
11
+ /** Compile an SCSS file to CSS. */
12
+ compileFile(filePath: string): string;
13
+ /** Add a directory to the import resolution path. */
14
+ addImportPath(path: string): void;
15
+ /** Set or override an SCSS variable. */
16
+ setVariable(name: string, value: string): void;
17
+ /** Compile all .scss files in a directory into a single CSS output file. */
18
+ compileScss(scssDir?: string, output?: string, minify?: boolean): string;
19
+ }
@@ -0,0 +1,131 @@
1
+ import { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { Tina4Config } from "./types.js";
3
+ import { Router } from "./router.js";
4
+ /**
5
+ * Build the startup banner's optional surface lines (issue #99).
6
+ *
7
+ * Only advertise a surface that is actually REACHABLE. In production, or with
8
+ * TINA4_DEBUG off, /swagger and /__dev return 404 -- printing them anyway both
9
+ * misleads an operator into believing a dev surface is exposed and sends a
10
+ * developer to a dead link.
11
+ *
12
+ * Kept as a pure function of (port, two booleans) so the contract is unit
13
+ * testable without booting a server and grepping stdout. Parity: Python
14
+ * banner_surface_lines, PHP App::bannerSurfaceLines, Ruby
15
+ * Tina4.banner_surface_lines.
16
+ *
17
+ * @returns [swaggerLine, dashboardLine] -- each empty, or a newline plus the
18
+ * banner row, ready to interpolate.
19
+ */
20
+ export declare function bannerSurfaceLines(port: number, opts: {
21
+ swaggerEnabled: boolean;
22
+ devAdminEnabled: boolean;
23
+ }): [string, string];
24
+ /**
25
+ * Apply pending DB migrations on startup — NON-BREAKING.
26
+ *
27
+ * When a `migrations/` folder exists (with at least one `.sql` file, excluding
28
+ * `.down.sql`) and `TINA4_AUTO_MIGRATE` is not disabled (default "true";
29
+ * false/0/no/off disable), pending migrations are applied during boot so the
30
+ * schema is current with no manual `tina4 migrate` step. A failure here is
31
+ * logged LOUD via `Log.error` and the service STILL starts — a bad migration
32
+ * must never take the backend down. (The explicit `tina4 migrate` CLI stays
33
+ * fail-fast so CI still gets a non-zero exit. Only this startup hook swallows.)
34
+ *
35
+ * Disable with `TINA4_AUTO_MIGRATE=false` — e.g. multi-instance production that
36
+ * migrates as a separate deploy step (concurrent first-apply can race).
37
+ *
38
+ * @param migrationDir - migrations directory (default "migrations", relative to base)
39
+ * @param base - project root used to resolve the migrations directory
40
+ */
41
+ export declare function autoMigrateOnStartup(migrationDir?: string, base?: string): Promise<void>;
42
+ /**
43
+ * Refuse to boot if pre-3.12 un-prefixed env vars are still set.
44
+ *
45
+ * Tina4 v3.12 hard-renamed every framework-specific env var to use the
46
+ * `TINA4_` prefix. Booting silently with a legacy `DATABASE_URL` or
47
+ * `SECRET` would let auth, DB, or mail fall back to insecure defaults
48
+ * while the user thought their config was being read. Better to die
49
+ * loudly with a list of names to fix.
50
+ *
51
+ * Bypass with `TINA4_ALLOW_LEGACY_ENV=true` in CI / migration scripts
52
+ * that genuinely need both names set during a transition window.
53
+ */
54
+ export declare function _checkLegacyEnvVars(): void;
55
+ /**
56
+ * Resolve port and host with priority: explicit config > ENV var > default.
57
+ * Exported for testability.
58
+ *
59
+ * Host resolution prefers `TINA4_HOST` (the framework-prefixed name —
60
+ * matches Python parity) and falls back to the unprefixed `HOST` env var
61
+ * for backwards compatibility.
62
+ */
63
+ export declare function resolvePortAndHost(config?: {
64
+ port?: number;
65
+ host?: string;
66
+ }): {
67
+ port: number;
68
+ host: string;
69
+ };
70
+ /**
71
+ * Whether the boot banner should be suppressed. Set TINA4_SUPPRESS=true to
72
+ * silence the ASCII-art banner and route table on startup — useful in CI,
73
+ * test runners, and embedded contexts where stdout is consumed by another
74
+ * process.
75
+ */
76
+ export declare function isBannerSuppressed(): boolean;
77
+ /**
78
+ * Honour TINA4_TEMPLATE_ROUTING=off|false|0|no|disabled as an explicit kill
79
+ * switch. Default: enabled. Drop a file in src/templates/pages/ and it serves
80
+ * at the matching URL — the zero-config Tina4 convention. Operators who want
81
+ * explicit-only routing can set TINA4_TEMPLATE_ROUTING=off and every URL
82
+ * must be registered via get() / post() (or be a static file).
83
+ */
84
+ export declare function templateAutoRoutingEnabled(): boolean;
85
+ /**
86
+ * Return the canonical HTTP reason phrase for `status`. Falls back to a
87
+ * sensible label when an exotic status is used. Never returns an empty string.
88
+ */
89
+ export declare function httpReason(status: number): string;
90
+ /**
91
+ * Reset the production template cache. Tests use this between scenarios so
92
+ * a fresh scan picks up fixture files in a tmp project.
93
+ */
94
+ export declare function resetTemplateCache(): void;
95
+ /**
96
+ * Resolve a URL path to a template file in src/templates/pages/.
97
+ *
98
+ * Only files inside `src/templates/pages/` auto-route from a URL. Anything
99
+ * in `src/templates/` outside `pages/` (partials, layouts, base.twig,
100
+ * errors, components) is never served standalone.
101
+ *
102
+ * Dev mode: checks filesystem every time for live changes.
103
+ * Production: uses a cached lookup built once at startup.
104
+ *
105
+ * The whole feature can be turned off with `TINA4_TEMPLATE_ROUTING=off`.
106
+ */
107
+ export declare function resolveTemplate(pathname: string, templatesDir: string): string | null;
108
+ /**
109
+ * Start the Tina4 HTTP server.
110
+ * Thin wrapper around startServer() for cross-framework parity with PHP and Ruby.
111
+ */
112
+ export declare function start(config?: Tina4Config): Promise<{
113
+ close: () => void;
114
+ router: Router;
115
+ port: number;
116
+ }>;
117
+ /**
118
+ * Stop the running Tina4 server gracefully.
119
+ */
120
+ export declare function stop(): void;
121
+ /**
122
+ * Dispatch a raw Node.js request through the Tina4 router and write the response.
123
+ * Requires startServer() to have been called first.
124
+ * Useful for testing and embedding.
125
+ */
126
+ export declare function handle(rawReq: IncomingMessage, rawRes: ServerResponse): Promise<void>;
127
+ export declare function startServer(config?: Tina4Config): Promise<{
128
+ close: () => void;
129
+ router: Router;
130
+ port: number;
131
+ }>;
@@ -0,0 +1,115 @@
1
+ export interface ServiceOptions {
2
+ timing?: string;
3
+ daemon?: boolean;
4
+ interval?: number;
5
+ maxRetries?: number;
6
+ }
7
+ export interface ServiceContext {
8
+ running: boolean;
9
+ lastRun: Date | null;
10
+ name: string;
11
+ }
12
+ export type ServiceHandler = (context: ServiceContext) => Promise<void> | void;
13
+ export interface ServiceInfo {
14
+ name: string;
15
+ options: ServiceOptions;
16
+ running: boolean;
17
+ lastRun: Date | null;
18
+ retries: number;
19
+ }
20
+ /**
21
+ * Parse a single cron field and check if the given value matches.
22
+ * Supports: * (every), N/n (step), N,N,N (list), N-N (range), plain number.
23
+ */
24
+ export declare function matchCronField(field: string, value: number): boolean;
25
+ /**
26
+ * Check whether a Date matches a 5-field cron expression.
27
+ * Fields: minute hour dayOfMonth month dayOfWeek
28
+ */
29
+ export declare function matchesCron(expression: string, date: Date): boolean;
30
+ export declare abstract class Tina4Service {
31
+ private _running;
32
+ /** Main work loop — subclasses MUST override. */
33
+ abstract run(): Promise<void> | void;
34
+ /**
35
+ * Signal this service to stop. The next `shouldStop()` check returns true.
36
+ * Override for custom shutdown behaviour but always call `super.stop()`.
37
+ */
38
+ stop(): void;
39
+ /**
40
+ * Returns true once `stop()` has been called. Use inside `run()` loops
41
+ * as the exit condition:
42
+ *
43
+ * async run() {
44
+ * while (!this.shouldStop()) { ... }
45
+ * }
46
+ */
47
+ shouldStop(): boolean;
48
+ /**
49
+ * Return a callable that ServiceRunner can register. Used by
50
+ * ServiceRunner.registerService under the hood.
51
+ */
52
+ asHandler(): ServiceHandler;
53
+ }
54
+ export declare class ServiceRunner {
55
+ /**
56
+ * Register a service with a handler and options.
57
+ */
58
+ static register(name: string, handler: ServiceHandler, options?: ServiceOptions): void;
59
+ /**
60
+ * Register a class-based service (subclass of {@link Tina4Service}) by name.
61
+ *
62
+ * Wraps the service's `run()` method as the runner's handler. Defaults
63
+ * to `daemon: true` because Tina4Service subclasses manage their own
64
+ * loop inside `run()`. Override via `options`.
65
+ *
66
+ * class EmailWorker extends Tina4Service { async run() { ... } }
67
+ * ServiceRunner.registerService("emails", new EmailWorker());
68
+ * await ServiceRunner.start();
69
+ *
70
+ * Cross-framework parity with PHP `ServiceRunner::registerService` and
71
+ * Ruby `Tina4::ServiceRunner.register_service`.
72
+ */
73
+ static registerService(name: string, service: Tina4Service, options?: ServiceOptions): void;
74
+ /**
75
+ * Discover services from a directory. Each file should export
76
+ * { name, handler, timing?, interval?, daemon?, maxRetries? }.
77
+ */
78
+ static discover(serviceDir?: string): Promise<ServiceInfo[]>;
79
+ /**
80
+ * Start all registered services, or a specific one by name.
81
+ */
82
+ static start(name?: string): void;
83
+ /**
84
+ * Stop all running services, or a specific one by name.
85
+ */
86
+ static stop(name?: string): void;
87
+ /**
88
+ * List all registered services with their current state.
89
+ */
90
+ static list(): ServiceInfo[];
91
+ /**
92
+ * Check if a specific service is running.
93
+ */
94
+ static isRunning(name: string): boolean;
95
+ /**
96
+ * Remove a service from the registry (stops it first if running).
97
+ */
98
+ static remove(name: string): boolean;
99
+ /**
100
+ * Clear all registered services (stops them all first).
101
+ */
102
+ static clear(): void;
103
+ /**
104
+ * Check if a 5-field cron pattern matches the given (or current) date/time.
105
+ */
106
+ static matchCron(pattern: string, now?: Date): boolean;
107
+ /**
108
+ * Watch service files for changes and hot-reload in dev mode.
109
+ */
110
+ static watch(serviceDir?: string): void;
111
+ /**
112
+ * Stop watching service files.
113
+ */
114
+ static unwatch(): void;
115
+ }