tina4-nodejs 3.13.92 → 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 (193) hide show
  1. package/CLAUDE.md +170 -28
  2. package/README.md +2 -2
  3. package/package.json +13 -9
  4. package/packages/cli/dist/bin.js +33126 -30055
  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 +33062 -29908
  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/devMailbox.ts +20 -44
  14. package/packages/core/src/dispatchPipeline.ts +285 -0
  15. package/packages/core/src/dotenv.ts +185 -40
  16. package/packages/core/src/index.ts +7 -6
  17. package/packages/core/src/logger.ts +257 -36
  18. package/packages/core/src/mcp.ts +1 -1
  19. package/packages/core/src/messenger.ts +81 -13
  20. package/packages/core/src/metrics.ts +199 -961
  21. package/packages/core/src/middleware.ts +390 -123
  22. package/packages/core/src/queue.ts +188 -32
  23. package/packages/core/src/queueBackends/kafkaBackend.ts +109 -13
  24. package/packages/core/src/queueBackends/liteBackend.ts +13 -0
  25. package/packages/core/src/queueBackends/mongoBackend.ts +101 -9
  26. package/packages/core/src/queueBackends/rabbitmqBackend.ts +22 -4
  27. package/packages/core/src/rateLimiter.ts +10 -5
  28. package/packages/core/src/request.ts +6 -9
  29. package/packages/core/src/response.ts +46 -1
  30. package/packages/core/src/router.ts +29 -4
  31. package/packages/core/src/server.ts +751 -414
  32. package/packages/core/src/session.ts +244 -27
  33. package/packages/core/src/sessionHandlers/childError.ts +72 -0
  34. package/packages/core/src/sessionHandlers/databaseHandler.ts +338 -48
  35. package/packages/core/src/sessionHandlers/memcachedHandler.ts +181 -0
  36. package/packages/core/src/sessionHandlers/mongoClient.ts +293 -202
  37. package/packages/core/src/sessionHandlers/mongoHandler.ts +88 -8
  38. package/packages/core/src/sessionHandlers/respClient.ts +16 -143
  39. package/packages/core/src/sessionHandlers/sqlClient.ts +290 -0
  40. package/packages/core/src/sessionHandlers/syncBridge.ts +190 -0
  41. package/packages/core/src/sessionHandlers/syncSocket.ts +236 -0
  42. package/packages/core/src/testClient.ts +18 -5
  43. package/packages/core/src/trustedProxy.ts +249 -0
  44. package/packages/core/src/types.ts +29 -5
  45. package/packages/core/src/websocket.ts +66 -0
  46. package/packages/frond/dist/index.js +74 -31
  47. package/packages/frond/src/engine.ts +99 -33
  48. package/packages/orm/dist/index.js +26554 -23400
  49. package/packages/orm/src/adapters/firebird.ts +183 -56
  50. package/packages/orm/src/adapters/mongodb.ts +25 -4
  51. package/packages/orm/src/adapters/mssql.ts +114 -29
  52. package/packages/orm/src/adapters/mysql.ts +103 -40
  53. package/packages/orm/src/adapters/odbc.ts +44 -21
  54. package/packages/orm/src/adapters/postgres.ts +118 -26
  55. package/packages/orm/src/adapters/sqlDialect.ts +120 -0
  56. package/packages/orm/src/adapters/sqlite.ts +64 -25
  57. package/packages/orm/src/baseModel.ts +135 -40
  58. package/packages/orm/src/cachedDatabase.ts +43 -19
  59. package/packages/orm/src/connectTimeout.ts +265 -0
  60. package/packages/orm/src/database.ts +338 -198
  61. package/packages/orm/src/databaseResult.ts +65 -13
  62. package/packages/orm/src/databaseUrl.ts +484 -0
  63. package/packages/orm/src/docstore.ts +386 -145
  64. package/packages/orm/src/index.ts +13 -3
  65. package/packages/orm/src/migration.ts +18 -3
  66. package/packages/orm/src/queryBuilder.ts +38 -4
  67. package/packages/orm/src/sqlTranslator.ts +310 -4
  68. package/packages/orm/src/types.ts +15 -4
  69. package/types/cli/src/bin.d.ts +92 -0
  70. package/types/cli/src/commands/build.d.ts +2 -0
  71. package/types/cli/src/commands/generate.d.ts +47 -0
  72. package/types/cli/src/commands/init.d.ts +1 -0
  73. package/types/cli/src/commands/metrics.d.ts +6 -0
  74. package/types/cli/src/commands/migrate.d.ts +1 -0
  75. package/types/cli/src/commands/migrateCreate.d.ts +1 -0
  76. package/types/cli/src/commands/migrateRollback.d.ts +1 -0
  77. package/types/cli/src/commands/migrateStatus.d.ts +1 -0
  78. package/types/cli/src/commands/queue.d.ts +20 -0
  79. package/types/cli/src/commands/routes.d.ts +1 -0
  80. package/types/cli/src/commands/seed.d.ts +1 -0
  81. package/types/cli/src/commands/serve.d.ts +6 -0
  82. package/types/cli/src/commands/test.d.ts +1 -0
  83. package/types/core/src/ai.d.ts +64 -0
  84. package/types/core/src/api.d.ts +262 -0
  85. package/types/core/src/auth.d.ts +177 -0
  86. package/types/core/src/authGate.d.ts +20 -0
  87. package/types/core/src/background.d.ts +34 -0
  88. package/types/core/src/cache.d.ts +163 -0
  89. package/types/core/src/constants.d.ts +38 -0
  90. package/types/core/src/container.d.ts +44 -0
  91. package/types/core/src/context/chunker.d.ts +31 -0
  92. package/types/core/src/context/index.d.ts +93 -0
  93. package/types/core/src/devAdmin.d.ts +179 -0
  94. package/types/core/src/devMailbox.d.ts +54 -0
  95. package/types/core/src/dispatchPipeline.d.ts +117 -0
  96. package/types/core/src/docs.d.ts +141 -0
  97. package/types/core/src/docsAutoDiscovery.d.ts +6 -0
  98. package/types/core/src/dotenv.d.ts +87 -0
  99. package/types/core/src/env.d.ts +28 -0
  100. package/types/core/src/errorOverlay.d.ts +36 -0
  101. package/types/core/src/events.d.ts +75 -0
  102. package/types/core/src/fakeData.d.ts +55 -0
  103. package/types/core/src/feedback.d.ts +90 -0
  104. package/types/core/src/graphql.d.ts +207 -0
  105. package/types/core/src/health.d.ts +22 -0
  106. package/types/core/src/htmlElement.d.ts +75 -0
  107. package/types/core/src/i18n.d.ts +37 -0
  108. package/types/core/src/index.d.ts +92 -0
  109. package/types/core/src/job.d.ts +39 -0
  110. package/types/core/src/logger.d.ts +200 -0
  111. package/types/core/src/mcp.d.ts +248 -0
  112. package/types/core/src/messenger.d.ts +191 -0
  113. package/types/core/src/metrics.d.ts +41 -0
  114. package/types/core/src/middleware.d.ts +330 -0
  115. package/types/core/src/mqtt.d.ts +257 -0
  116. package/types/core/src/mqttMessage.d.ts +67 -0
  117. package/types/core/src/plan.d.ts +96 -0
  118. package/types/core/src/projectIndex.d.ts +56 -0
  119. package/types/core/src/queue.d.ts +268 -0
  120. package/types/core/src/queueBackends/kafkaBackend.d.ts +117 -0
  121. package/types/core/src/queueBackends/liteBackend.d.ts +128 -0
  122. package/types/core/src/queueBackends/mongoBackend.d.ts +119 -0
  123. package/types/core/src/queueBackends/rabbitmqBackend.d.ts +55 -0
  124. package/types/core/src/rateLimiter.d.ts +49 -0
  125. package/types/core/src/request.d.ts +25 -0
  126. package/types/core/src/response.d.ts +28 -0
  127. package/types/core/src/routeDiscovery.d.ts +12 -0
  128. package/types/core/src/router.d.ts +366 -0
  129. package/types/core/src/scss.d.ts +19 -0
  130. package/types/core/src/server.d.ts +146 -0
  131. package/types/core/src/service.d.ts +115 -0
  132. package/types/core/src/session.d.ts +341 -0
  133. package/types/core/src/sessionHandlers/childError.d.ts +34 -0
  134. package/types/core/src/sessionHandlers/databaseHandler.d.ts +97 -0
  135. package/types/core/src/sessionHandlers/memcachedHandler.d.ts +60 -0
  136. package/types/core/src/sessionHandlers/mongoClient.d.ts +35 -0
  137. package/types/core/src/sessionHandlers/mongoHandler.d.ts +109 -0
  138. package/types/core/src/sessionHandlers/respClient.d.ts +22 -0
  139. package/types/core/src/sessionHandlers/sqlClient.d.ts +39 -0
  140. package/types/core/src/sessionHandlers/syncBridge.d.ts +91 -0
  141. package/types/core/src/sessionHandlers/syncSocket.d.ts +49 -0
  142. package/types/core/src/sessionHandlers/valkeyHandler.d.ts +65 -0
  143. package/types/core/src/static.d.ts +2 -0
  144. package/types/core/src/test.d.ts +94 -0
  145. package/types/core/src/testClient.d.ts +36 -0
  146. package/types/core/src/testing.d.ts +58 -0
  147. package/types/core/src/trustedProxy.d.ts +44 -0
  148. package/types/core/src/types.d.ts +242 -0
  149. package/types/core/src/validator.d.ts +52 -0
  150. package/types/core/src/websocket.d.ts +402 -0
  151. package/types/core/src/websocketBackplane.d.ts +166 -0
  152. package/types/core/src/websocketConnection.d.ts +54 -0
  153. package/types/core/src/wsdl.d.ts +101 -0
  154. package/types/frond/src/engine.d.ts +263 -0
  155. package/types/frond/src/index.d.ts +2 -0
  156. package/types/orm/src/adapters/firebird.d.ts +183 -0
  157. package/types/orm/src/adapters/mongodb.d.ts +81 -0
  158. package/types/orm/src/adapters/mssql.d.ts +77 -0
  159. package/types/orm/src/adapters/mysql.d.ts +67 -0
  160. package/types/orm/src/adapters/odbc.d.ts +94 -0
  161. package/types/orm/src/adapters/postgres.d.ts +86 -0
  162. package/types/orm/src/adapters/sqlDialect.d.ts +71 -0
  163. package/types/orm/src/adapters/sqlite.d.ts +68 -0
  164. package/types/orm/src/autoCrud.d.ts +73 -0
  165. package/types/orm/src/baseModel.d.ts +427 -0
  166. package/types/orm/src/cachedDatabase.d.ts +190 -0
  167. package/types/orm/src/connectTimeout.d.ts +100 -0
  168. package/types/orm/src/database.d.ts +655 -0
  169. package/types/orm/src/databaseResult.d.ts +109 -0
  170. package/types/orm/src/databaseUrl.d.ts +125 -0
  171. package/types/orm/src/docstore.d.ts +241 -0
  172. package/types/orm/src/fakeData.d.ts +22 -0
  173. package/types/orm/src/index.d.ts +43 -0
  174. package/types/orm/src/migration.d.ts +275 -0
  175. package/types/orm/src/model.d.ts +7 -0
  176. package/types/orm/src/query.d.ts +14 -0
  177. package/types/orm/src/queryBuilder.d.ts +193 -0
  178. package/types/orm/src/realtime/index.d.ts +7 -0
  179. package/types/orm/src/realtime/models/attachment.d.ts +43 -0
  180. package/types/orm/src/realtime/models/channel.d.ts +32 -0
  181. package/types/orm/src/realtime/models/channelMember.d.ts +32 -0
  182. package/types/orm/src/realtime/models/message.d.ts +36 -0
  183. package/types/orm/src/realtime/models/workspace.d.ts +26 -0
  184. package/types/orm/src/realtime/realtime.d.ts +24 -0
  185. package/types/orm/src/realtime/storage.d.ts +61 -0
  186. package/types/orm/src/seeder.d.ts +118 -0
  187. package/types/orm/src/sqlTranslator.d.ts +258 -0
  188. package/types/orm/src/types.d.ts +148 -0
  189. package/types/orm/src/validation.d.ts +6 -0
  190. package/types/swagger/src/generator.d.ts +46 -0
  191. package/types/swagger/src/index.d.ts +2 -0
  192. package/types/swagger/src/ui.d.ts +11 -0
  193. package/packages/core/src/sessionHandlers/redisHandler.ts +0 -206
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The dispatch pipeline: the concerns of `dispatch`, named and extracted.
3
+ *
4
+ * `dispatch` was a 485-line closure at cyclomatic complexity 65 against a
5
+ * ceiling of 10, on the path of every request, nested inside `startServer`
6
+ * (which is why that measured 45 as well). These are its concerns as
7
+ * standalone functions, so each can be read and tested without standing up a
8
+ * server.
9
+ *
10
+ * PROLOGUE_STAGES run before anything else, in order. They are extracted FIRST
11
+ * because they close over nothing from `startServer` - only the raw
12
+ * request/response - so no context object is needed for them at all. The later
13
+ * stages need `router`, `staticDir`, `port` and `middleware`, and follow.
14
+ * `sessionAutoStart` is the one prologue stage that runs INSIDE the dispatch
15
+ * try-block, so a TINA4_SESSION_STRICT refusal renders a 500 like every other
16
+ * request error instead of rejecting `dispatch` into an unhandled rejection
17
+ * that takes the worker down (ADR-0021; parity with Python, where the raise
18
+ * leaves the request path and the ASGI server turns it into a 500).
19
+ *
20
+ * Ordering here is BEHAVIOUR, not taste:
21
+ * * `headStripIntercept` MUST run before anything can write. Node streams its
22
+ * response, so there is no single exit point to strip at - the interception
23
+ * IS the mechanism (ADR-0011: the CONTRACT is the outcome, and Ruby and
24
+ * Python satisfy it by stripping late at their single return instead).
25
+ * * `sessionAutoStart` wraps `end` after that, so its save-and-set-cookie
26
+ * runs on the real `end` rather than on the HEAD interceptor's.
27
+ *
28
+ * @see tina4-ruby/lib/tina4/dispatch_pipeline.rb - the same extraction, and
29
+ * the source of the stage-list-as-data pattern.
30
+ */
31
+ import type { IncomingMessage, ServerResponse } from "node:http";
32
+ import type { Tina4Request } from "./types.js";
33
+ /**
34
+ * The prologue, in order. Exported as DATA so the pipeline can be asserted and
35
+ * compared across frameworks without reading an implementation.
36
+ */
37
+ export declare const PROLOGUE_STAGES: readonly ["resetRequestCaches", "headStripIntercept", "sessionAutoStart"];
38
+ /**
39
+ * After the prologue, before a route is looked up.
40
+ *
41
+ * `wrapResponseEnd` MUST come before the global pass: it installs the end()
42
+ * wrapper that injects the dev toolbar and captures the request, and a
43
+ * middleware that short-circuits still has to be captured.
44
+ */
45
+ export declare const REQUEST_STAGES: readonly ["blockAiPortReload", "wrapResponseEnd", "runGlobalMiddlewarePass"];
46
+ /**
47
+ * A matched route, in order - and the order is BEHAVIOUR (ADR-0012):
48
+ * POST-MATCH globals -> auth gate -> the route's OWN middleware -> handler.
49
+ *
50
+ * The globals run BEFORE the gate so a rate limiter can throttle a brute-force
51
+ * login and an access log records the 401 - neither is possible if they only
52
+ * run on authenticated requests. The route's own middleware stays AFTER the
53
+ * gate, so middleware attached to a secured route never processes an
54
+ * unauthenticated request.
55
+ *
56
+ * `runGlobalMiddlewarePass` appears here AND in REQUEST_STAGES on purpose:
57
+ * one function, two phases. That split IS ADR-0012.
58
+ */
59
+ export declare const ROUTE_STAGES: readonly ["runGlobalMiddlewarePass", "enforceRouteAuth", "runRouteMiddlewares", "invokeRouteHandler", "renderIfTemplateRoute"];
60
+ /**
61
+ * Nothing matched a route: the fallback chain, walked until one answers.
62
+ *
63
+ * Order is BEHAVIOUR: a template beats the landing page (so a project's own
64
+ * pages/index.twig wins at "/"), 405 beats static (a known path with the wrong
65
+ * method is not a missing file), and the 404 is terminal.
66
+ *
67
+ * This chain runs AFTER matching because routes beat files (ADR-0010): a file
68
+ * from a build step or a careless deploy must never shadow a reviewed route.
69
+ *
70
+ * server.ts holds the same order as an array of the real FUNCTIONS - that is
71
+ * what dispatch actually walks. dispatchPipeline.test.ts asserts the two agree,
72
+ * so this list cannot drift from the runner.
73
+ */
74
+ export declare const FALLBACK_STAGES: readonly ["serveTemplateFallback", "serveLandingPage", "serveMethodNotAllowed", "serveStaticAsset", "serveNotFound"];
75
+ /** The catch arm. Everything above throws into this one. */
76
+ export declare const ERROR_STAGES: readonly ["renderDispatchError"];
77
+ /**
78
+ * Request-scoped DB query cache boundary.
79
+ *
80
+ * Clears the request-scoped cache on every live connection at the START of each
81
+ * request so it never serves rows across requests (persistent-mode connections
82
+ * are left alone). The ORM is loaded lazily and may be absent, so this is
83
+ * best-effort: a failure here must never break a request. Mirrors Python's
84
+ * dispatcher calling `Database.reset_request_caches()`.
85
+ */
86
+ export declare function resetRequestCaches(): Promise<void>;
87
+ /**
88
+ * RFC 9110 s9.3.2: the server MUST NOT send content in a HEAD response.
89
+ *
90
+ * Intercepts `write` / `end` so every code path - an explicit `Router.head()`
91
+ * handler, the GET auto-fallback, 405 and 404 responses - drops its body.
92
+ * Content-Length is preserved when present, so cache validators, link checkers
93
+ * and monitoring probes still see the size the equivalent GET would have sent.
94
+ *
95
+ * No-op for any method other than HEAD.
96
+ *
97
+ * @param rawReq Node's incoming message, read for the method
98
+ * @param rawRes Node's server response, whose write/end are replaced in place
99
+ */
100
+ export declare function headStripIntercept(rawReq: IncomingMessage, rawRes: ServerResponse): void;
101
+ /**
102
+ * Auto-start the session: read the cookie, create the session, then save it and
103
+ * set the cookie when the response ends.
104
+ *
105
+ * The incoming cookie is read by the SAME configured name the write side emits
106
+ * (`TINA4_SESSION_NAME`, default `tina4_session`) via the shared
107
+ * `sessionCookieName()` resolver - otherwise a renamed cookie would be written
108
+ * but never read back and the session would silently never resume. A whole
109
+ * cookie pair is matched by its exact `name=` prefix (split on ";", trim,
110
+ * startsWith) so `tina4_session` never matches `tina4_session_foo=` nor a value
111
+ * mid-header. Parity with Python `core/server._init_session`.
112
+ *
113
+ * @param rawReq Node's incoming message, read for cookies and the proxy scheme
114
+ * @param rawRes Node's server response, whose `end` is wrapped
115
+ * @param req The Tina4 request the session is attached to
116
+ */
117
+ export declare function sessionAutoStart(rawReq: IncomingMessage, rawRes: ServerResponse, req: Tina4Request): Promise<void>;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Tina4 Live API RAG — `Docs` module.
3
+ *
4
+ * Walks framework packages (`@tina4/core`, `@tina4/orm`, `@tina4/swagger`,
5
+ * `@tina4/frond`) and the user project's `src/` tree via lightweight TS
6
+ * regex parsing (no AST — works on .ts files without importing them, so
7
+ * user-code import errors don't break reflection).
8
+ *
9
+ * Exposes ranked search, class/method specs, a flat index, MCP-style
10
+ * static mirrors, and a Markdown drift/sync helper. Zero new runtime
11
+ * dependencies — Node stdlib only.
12
+ *
13
+ * Spec: plan/v3/22-LIVE-API-RAG.md
14
+ *
15
+ * Method names follow Tina4 Node.js convention (camelCase) — see PHP
16
+ * `Tina4\Docs` and Python `tina4_python.docs.Docs` for parity references.
17
+ */
18
+ export interface DocsHit {
19
+ fqn: string;
20
+ kind: "class" | "method" | "function" | "property";
21
+ name: string;
22
+ signature: string;
23
+ summary: string;
24
+ file: string;
25
+ line: number;
26
+ version: string;
27
+ source: "framework" | "user" | "vendor";
28
+ visibility: "public" | "protected" | "private";
29
+ static?: boolean;
30
+ class?: string;
31
+ score: number;
32
+ }
33
+ export interface MethodSpec {
34
+ name: string;
35
+ fqn: string;
36
+ class: string;
37
+ kind: "method";
38
+ signature: string;
39
+ summary: string;
40
+ docblock: string;
41
+ file: string;
42
+ line: number;
43
+ visibility: "public" | "protected" | "private";
44
+ static: boolean;
45
+ source: "framework" | "user" | "vendor";
46
+ version: string;
47
+ params: Array<{
48
+ name: string;
49
+ type: string;
50
+ default?: string | null;
51
+ }>;
52
+ return: string;
53
+ }
54
+ export interface ClassSpec {
55
+ fqn: string;
56
+ kind: "class";
57
+ name: string;
58
+ file: string;
59
+ line: number;
60
+ summary: string;
61
+ docblock: string;
62
+ source: "framework" | "user" | "vendor";
63
+ version: string;
64
+ methods: Array<Omit<MethodSpec, "params" | "return"> & {
65
+ params?: unknown[];
66
+ return?: string;
67
+ }>;
68
+ properties: unknown[];
69
+ }
70
+ export interface IndexEntry {
71
+ fqn: string;
72
+ kind: "class" | "method" | "function" | "property";
73
+ name: string;
74
+ signature: string;
75
+ summary: string;
76
+ file: string;
77
+ line: number;
78
+ version: string;
79
+ source: "framework" | "user" | "vendor";
80
+ visibility: "public" | "protected" | "private";
81
+ static?: boolean;
82
+ class?: string;
83
+ }
84
+ export interface DriftHit {
85
+ method: string;
86
+ line: number;
87
+ block: string;
88
+ }
89
+ export declare class Docs {
90
+ private projectRoot;
91
+ private frameworkRoots;
92
+ private version;
93
+ private indexCache;
94
+ private frameworkEntries;
95
+ private userEntries;
96
+ private userMtime;
97
+ private frameworkMtime;
98
+ constructor(projectRoot: string);
99
+ /**
100
+ * Search the merged framework + user index for query-matching entities.
101
+ * Source filter accepts `all` (default), `framework`, `user`, `vendor`.
102
+ * Private/underscore methods are excluded unless `includePrivate=true`.
103
+ */
104
+ search(query: string, k?: number, source?: string, includePrivate?: boolean): DocsHit[];
105
+ /**
106
+ * Resolve a class by exact FQN, documented public import path, or bare name.
107
+ *
108
+ * Node stores the bare class name as the FQN (`Database`), but a developer
109
+ * reading the docs may type the published path (`@tina4/orm.Database`,
110
+ * `orm/Database`) or just `Database`. Match exactly first, then by class
111
+ * name (last path segment), disambiguating by requiring the given segments
112
+ * to appear in the stored FQN/file (framework + shortest wins). Unknown
113
+ * names stay `null` — no false positives.
114
+ */
115
+ private resolveClassEntry;
116
+ /**
117
+ * Return the full spec for a single class, or `null` if not found.
118
+ */
119
+ classSpec(fqn: string): ClassSpec | null;
120
+ /**
121
+ * Return the spec for a single method, or `null` if unknown.
122
+ */
123
+ methodSpec(classFqn: string, methodName: string): MethodSpec | null;
124
+ /**
125
+ * Flat list of every reflected entity (classes + methods + functions),
126
+ * user + framework. Vendor entries are included here for completeness.
127
+ */
128
+ index(): IndexEntry[];
129
+ static mcpSearch(query: string, k?: number, projectRoot?: string, source?: string, includePrivate?: boolean): DocsHit[];
130
+ static mcpMethod(classFqn: string, name: string, projectRoot?: string): MethodSpec | null;
131
+ static mcpClass(fqn: string, projectRoot?: string): ClassSpec | null;
132
+ static checkDocs(mdPath: string, projectRoot?: string): {
133
+ drift: DriftHit[];
134
+ };
135
+ static syncDocs(mdPath: string, projectRoot?: string): void;
136
+ private static cached;
137
+ private ensureIndex;
138
+ private maxMtime;
139
+ private scoreEntry;
140
+ private renderGeneratedBlock;
141
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Write `.tina4/mcp.json` and (if `.git/` exists) append `.tina4/` to .gitignore.
3
+ *
4
+ * Returns true if the discovery file was written or updated.
5
+ */
6
+ export declare function writeMcpDiscovery(projectRoot: string, port: number): boolean;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Load environment variables from a root DIRECTORY or a single .env file.
3
+ *
4
+ * Pass a **directory** and it loads `<dir>/.env.local` then `<dir>/.env`, both
5
+ * first-wins, which IS the precedence real-env > `.env.local` > `.env`. That is
6
+ * the canonical form in all four frameworks.
7
+ *
8
+ * Before this, the ordering was the CALLER's job and this doc comment was the
9
+ * only place it was written down: load `.env.local` first, then `.env`, both
10
+ * with override=false. Every caller had to remember, and getting it wrong
11
+ * (override=true on `.env.local`) lets a stray gitignored file clobber an
12
+ * explicitly set real env var such as a production TINA4_SECRET. A rule nobody
13
+ * can forget beats a rule written in a comment.
14
+ *
15
+ * A **file** path still works exactly as before: only that file is read, and the
16
+ * caller owns the ordering.
17
+ *
18
+ * By default this does NOT override existing process.env values — it is
19
+ * first-wins, which is how a real env var always beats both files.
20
+ *
21
+ * Resolution order when `path` is omitted:
22
+ * 1. `TINA4_ENV_FILE` env var (if set and non-empty) — the named file, plus
23
+ * `.env.local` BESIDE it, so pointing at `.env.staging` does not silently
24
+ * stop honouring local overrides
25
+ * 2. the current working directory, as a root
26
+ *
27
+ * @param path - A root directory (canonical) OR a path to a single .env file.
28
+ * @param override - When true, overwrite keys already present in process.env.
29
+ * @returns The parsed key-value pairs. For the directory form this is the merge
30
+ * of both files, with `.env.local` winning on a duplicate key.
31
+ */
32
+ export declare function loadEnv(path?: string, override?: boolean): Record<string, string>;
33
+ /**
34
+ * Get an environment variable value with an optional default.
35
+ *
36
+ * @param key - The environment variable name.
37
+ * @param defaultValue - Value to return if the variable is not set.
38
+ * @returns The environment variable value, or the default.
39
+ */
40
+ export declare function getEnv(key: string, defaultValue?: string): string | undefined;
41
+ /**
42
+ * Get a required environment variable. Throws if not set.
43
+ *
44
+ * @param key - The environment variable name.
45
+ * @returns The environment variable value.
46
+ * @throws Error if the variable is not set.
47
+ */
48
+ /**
49
+ * Validate that required environment variables exist, and return them.
50
+ *
51
+ * Takes VARARGS and returns a map, matching Python, PHP and Ruby. It used to
52
+ * take one key and return that value, so checking five variables meant five
53
+ * calls that each failed on the first problem - an operator fixing a deployment
54
+ * got one name per restart instead of the whole list.
55
+ *
56
+ * @param keys - Variable names that must be set.
57
+ * @returns Every requested key mapped to its value.
58
+ * @throws Error naming ALL missing variables, not just the first.
59
+ */
60
+ export declare function requireEnv(...keys: string[]): Record<string, string>;
61
+ /**
62
+ * Check if an environment variable exists (is defined in process.env).
63
+ *
64
+ * @param key - The environment variable name.
65
+ * @returns true if the variable is set, false otherwise.
66
+ */
67
+ export declare function hasEnv(key: string): boolean;
68
+ /**
69
+ * Return all currently loaded environment variables.
70
+ *
71
+ * @returns A shallow copy of process.env as a record.
72
+ */
73
+ export declare function allEnv(): Record<string, string | undefined>;
74
+ /**
75
+ * Check if a value is truthy for env boolean checks.
76
+ *
77
+ * Accepts: "true", "True", "TRUE", "1", "yes", "Yes", "YES", "on", "On", "ON".
78
+ * Everything else is falsy (including empty string, undefined, not set).
79
+ *
80
+ * Mirrors Python's `is_truthy()` in `tina4_python.dotenv`.
81
+ */
82
+ export declare function isTruthy(val: string | undefined | null): boolean;
83
+ /**
84
+ * Remove all environment variables that were loaded by loadEnv().
85
+ * Useful for testing. Only removes keys set by loadEnv(), not pre-existing system env vars.
86
+ */
87
+ export declare function resetEnv(): void;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Typed environment-variable helpers.
3
+ *
4
+ * All methods are static so callers can write `Env.bool(...)` without
5
+ * instantiating anything — matching the Python/PHP/Ruby ports.
6
+ */
7
+ export declare class Env {
8
+ /**
9
+ * Read `name` and coerce to bool.
10
+ *
11
+ * Truthy values (case-insensitive after trim): `1`, `true`, `on`, `yes`,
12
+ * `y`, `t`. Falsy: `0`, `false`, `off`, `no`, `n`, `f`, empty string.
13
+ * Anything else returns the `defaultValue` — never throws.
14
+ */
15
+ static bool(name: string, defaultValue?: boolean): boolean;
16
+ /** Read `name` and coerce to int. Returns `defaultValue` on parse failure. */
17
+ static int(name: string, defaultValue?: number): number;
18
+ /** Read `name` and coerce to float. Returns `defaultValue` on parse failure. */
19
+ static float(name: string, defaultValue?: number): number;
20
+ /**
21
+ * Read `name` as a string. Returns `defaultValue` if unset.
22
+ *
23
+ * Whitespace is preserved — this is a pass-through for the raw env value.
24
+ * `Env.str("PATH")` is exactly `process.env.PATH ?? ""` with a more
25
+ * discoverable name.
26
+ */
27
+ static str(name: string, defaultValue?: string): string;
28
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Tina4 Debug — Rich error overlay for development mode.
3
+ *
4
+ * Renders a professional, syntax-highlighted HTML error page when an unhandled
5
+ * exception occurs in a route handler.
6
+ *
7
+ * import { renderErrorOverlay, renderProductionError, isDebugMode } from "./errorOverlay.js";
8
+ *
9
+ * try {
10
+ * await handler(req, res);
11
+ * } catch (err) {
12
+ * const html = isDebugMode()
13
+ * ? renderErrorOverlay(err as Error, req)
14
+ * : renderProductionError();
15
+ * res.html(html, 500);
16
+ * }
17
+ *
18
+ * Only activate when TINA4_DEBUG is true.
19
+ * In production, call renderProductionError() instead.
20
+ */
21
+ /**
22
+ * Render a rich HTML error overlay.
23
+ *
24
+ * @param error - The caught error.
25
+ * @param request - Optional request object with method, url, headers, etc.
26
+ * @returns Complete HTML page string.
27
+ */
28
+ export declare function renderErrorOverlay(error: Error, request?: any): string;
29
+ /**
30
+ * Render a safe, generic error page for production.
31
+ */
32
+ export declare function renderProductionError(statusCode?: number, message?: string, path?: string): string;
33
+ /**
34
+ * Check if TINA4_DEBUG is enabled.
35
+ */
36
+ export declare function isDebugMode(): boolean;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Tina4 Events — Simple observer pattern for decoupled communication.
3
+ *
4
+ * Zero-dependency event system. Fire events, register listeners.
5
+ *
6
+ * Events.on("user.created", (user) => console.log(`Welcome ${user.name}!`));
7
+ * Events.emit("user.created", { name: "Alice", email: "alice@example.com" });
8
+ *
9
+ * One-time listeners:
10
+ *
11
+ * Events.once("app.ready", () => console.log("App started!"));
12
+ */
13
+ export declare class Events {
14
+ /**
15
+ * Register a listener for an event.
16
+ * Higher priority runs first.
17
+ */
18
+ static on(event: string, callback: (...args: unknown[]) => void, priority?: number): void;
19
+ /**
20
+ * Register a listener that fires only once then auto-removes.
21
+ */
22
+ static once(event: string, callback: (...args: unknown[]) => void, priority?: number): void;
23
+ /**
24
+ * Remove a specific listener, or all listeners for an event.
25
+ *
26
+ * Events.off("user.created", handler) // remove specific
27
+ * Events.off("user.created") // remove all for event
28
+ */
29
+ static off(event: string, callback?: (...args: unknown[]) => void): void;
30
+ /**
31
+ * Fire an event synchronously. Returns array of listener results.
32
+ *
33
+ * Listener isolation (E1): each listener call is wrapped — a listener
34
+ * that THROWS does NOT abort the rest of emit(). The error is LOGGED
35
+ * (never silent) and the failed listener contributes a `null` slot, so
36
+ * N listeners always yield N results in priority order; surviving
37
+ * listeners run regardless of an earlier throw.
38
+ *
39
+ * Pass `{ strict: true }` to RE-RAISE on the first listener error
40
+ * instead of isolating it (later listeners then do NOT run).
41
+ *
42
+ * once() cleanup stays correct under isolation: the one-shot listener is
43
+ * spliced out BEFORE its callback runs, so a throw never leaves it
44
+ * registered.
45
+ */
46
+ static emit(event: string, ...args: unknown[]): unknown[];
47
+ static emit(event: string, options: {
48
+ strict?: boolean;
49
+ }, ...args: unknown[]): unknown[];
50
+ /**
51
+ * Emit an event and await all async listeners.
52
+ * Returns array of resolved results from each listener.
53
+ *
54
+ * Listener isolation (E1): identical to emit() — each awaited listener
55
+ * is isolated; a rejection/throw is LOGGED and contributes a `null`
56
+ * slot without aborting the others. `{ strict: true }` re-raises on the
57
+ * first error.
58
+ */
59
+ static emitAsync(event: string, ...args: unknown[]): Promise<unknown[]>;
60
+ static emitAsync(event: string, options: {
61
+ strict?: boolean;
62
+ }, ...args: unknown[]): Promise<unknown[]>;
63
+ /**
64
+ * Get all listener callbacks for an event (in priority order).
65
+ */
66
+ static listeners(event: string): Array<(...args: unknown[]) => void>;
67
+ /**
68
+ * List all registered event names.
69
+ */
70
+ static events(): string[];
71
+ /**
72
+ * Remove all listeners for all events.
73
+ */
74
+ static clear(): void;
75
+ }
@@ -0,0 +1,55 @@
1
+ export declare class FakeData {
2
+ private rng;
3
+ private seeded;
4
+ constructor(seed?: number);
5
+ /** Static factory — create a seeded FakeData instance. */
6
+ static seed(seed: number): FakeData;
7
+ /** Returns a random integer in [min, max) using the instance PRNG. */
8
+ private randInt;
9
+ /** Pick a random element from an array. */
10
+ private pick;
11
+ firstName(): string;
12
+ lastName(): string;
13
+ name(): string;
14
+ email(): string;
15
+ phone(): string;
16
+ address(): string;
17
+ city(): string;
18
+ country(): string;
19
+ zipCode(): string;
20
+ company(): string;
21
+ jobTitle(): string;
22
+ paragraph(sentences?: number): string;
23
+ sentence(words?: number): string;
24
+ word(): string;
25
+ integer(min?: number, max?: number): number;
26
+ numeric(min?: number, max?: number, decimals?: number): number;
27
+ boolean(): boolean;
28
+ date(start?: string, end?: string): string;
29
+ uuid(): string;
30
+ url(): string;
31
+ ipAddress(): string;
32
+ colorHex(): string;
33
+ /** Returns fake test credit card numbers (Luhn-valid test patterns). */
34
+ creditCard(): string;
35
+ currency(): string;
36
+ /**
37
+ * Returns multi-paragraph text.
38
+ * Matches Python's text() method.
39
+ */
40
+ text(paragraphs?: number): string;
41
+ /**
42
+ * Returns a random element from the given array.
43
+ * Matches Python's choice() method.
44
+ */
45
+ choice<T>(items: T[]): T;
46
+ /**
47
+ * Run seed files from a directory. Each file should export a default async function.
48
+ * Returns an array of executed file paths.
49
+ */
50
+ seedDir(seedDir?: string): Promise<string[]>;
51
+ /**
52
+ * Run a generator function `count` times and return the results.
53
+ */
54
+ run(fn: () => Record<string, unknown>, count?: number): Record<string, unknown>[];
55
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Customer feedback widget — Tier 4 port from tina4-python.
3
+ *
4
+ * End-users of a shipped Tina4 app give UX feedback via a floating bubble
5
+ * widget. Widget visibility + API are gated by TWO env flags:
6
+ *
7
+ * - TINA4_ENABLE_FEEDBACK master switch (explicit opt-in)
8
+ * - TINA4_FEEDBACK_WHITELIST comma-separated emails / user IDs
9
+ *
10
+ * Architecture (mirrors Python `tina4_python/dev_admin/__init__.py`
11
+ * lines 1440-1645):
12
+ *
13
+ * 1. Framework middleware injects <script src="/__feedback/widget.js">
14
+ * into HTML responses for whitelisted users only.
15
+ * 2. Widget POSTs to /__feedback/api/turn for each conversational turn.
16
+ * 3. That handler verifies whitelist + rate-limit, stamps the user
17
+ * identity server-side (client cannot fake `sender`), then forwards
18
+ * to the Rust agent's /feedback/intake.
19
+ *
20
+ * The widget is for END USERS of a shipped app — the /__dev paths get
21
+ * skipped so the dev admin's own chat bubble doesn't sit on top of the
22
+ * customer feedback bubble.
23
+ */
24
+ import type { RouteHandler, Tina4Request } from "./types.js";
25
+ import type { Router } from "./router.js";
26
+ /**
27
+ * Master switch — both this AND a non-empty whitelist must be set for
28
+ * the widget to render or the API to accept submissions. Mirrors
29
+ * Python's `_feedback_enabled()`.
30
+ */
31
+ export declare function feedbackEnabled(): boolean;
32
+ /**
33
+ * Comma-separated emails / user IDs in env, lowercased + trimmed.
34
+ * Returns [] when the master switch is off so callers can short-circuit
35
+ * with a single check. Mirrors Python's `_feedback_whitelist()`.
36
+ */
37
+ export declare function feedbackWhitelist(): string[];
38
+ /**
39
+ * Best-effort user identity from JWT/Bearer auth. Falls back to
40
+ * TINA4_FEEDBACK_DEV_USER (local dev convenience — lets the framework
41
+ * owner test the widget without a full auth setup). Mirrors Python's
42
+ * `_feedback_identify_user()`.
43
+ */
44
+ export declare function feedbackIdentifyUser(request: Tina4Request): string | null;
45
+ /**
46
+ * Returns [allowed, userId]. Both halves are required — feature off when
47
+ * either is falsy. Mirrors Python's `_feedback_is_whitelisted()`.
48
+ */
49
+ export declare function feedbackIsWhitelisted(request: Tina4Request): [boolean, string | null];
50
+ /**
51
+ * 5 turns/hour per user, sliding window. Prunes old timestamps lazily on
52
+ * every call (no background task needed). Mirrors Python's
53
+ * `_feedback_rate_limit_ok()`.
54
+ */
55
+ export declare function feedbackRateLimitOk(user: string): boolean;
56
+ /** Test-only: clear rate-limit state between cases. Not part of public API. */
57
+ export declare function _resetFeedbackRateLimit(): void;
58
+ /**
59
+ * Insert the widget <script> into HTML for whitelisted users. Called
60
+ * from the response pipeline right before the body is flushed. No-op if:
61
+ * - request path starts with /__dev or /__feedback (developer
62
+ * pages have their own chat trigger)
63
+ * - master switch / whitelist not set
64
+ * - user not in whitelist
65
+ * - html lacks </body>
66
+ * Idempotent — looks for the `data-tina4-feedback` marker and bails.
67
+ * Mirrors Python's `inject_feedback_widget()`.
68
+ */
69
+ export declare function injectFeedbackWidget(request: Tina4Request, html: string): string;
70
+ /**
71
+ * POST /__feedback/api/turn — proxy one conversational turn to the Rust
72
+ * agent's `/feedback/intake`. Server stamps `sender` from the verified
73
+ * identity so the client cannot inject who they are. Mirrors Python's
74
+ * `_api_feedback_turn()`.
75
+ */
76
+ export declare const handleFeedbackTurn: RouteHandler;
77
+ declare const WIDGET_BUNDLE_PATH: string;
78
+ /**
79
+ * GET /__feedback/widget.js — serve the widget bundle with no-cache
80
+ * headers so a broken bundle doesn't get stuck in browser caches.
81
+ * Mirrors Python's `_api_feedback_widget_js()`.
82
+ */
83
+ export declare const handleFeedbackWidgetJs: RouteHandler;
84
+ /**
85
+ * Register the two feedback routes on a Router. Called from the dev
86
+ * admin setup so the routes only exist when the dev surface is
87
+ * enabled — production deployments without TINA4_DEBUG also skip them.
88
+ */
89
+ export declare function registerFeedbackRoutes(router: Router): void;
90
+ export { WIDGET_BUNDLE_PATH };