tina4-nodejs 3.13.92 → 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 (134) 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 +1260 -969
  5. package/packages/core/dist/index.js +1260 -969
  6. package/packages/core/src/devMailbox.ts +20 -44
  7. package/packages/core/src/index.ts +2 -2
  8. package/packages/core/src/messenger.ts +72 -0
  9. package/packages/core/src/queueBackends/kafkaBackend.ts +108 -12
  10. package/packages/core/src/sessionHandlers/childError.ts +72 -0
  11. package/packages/core/src/sessionHandlers/mongoClient.ts +9 -3
  12. package/packages/core/src/sessionHandlers/redisHandler.ts +18 -5
  13. package/packages/core/src/sessionHandlers/respClient.ts +5 -1
  14. package/packages/frond/dist/index.js +74 -31
  15. package/packages/frond/src/engine.ts +99 -33
  16. package/packages/orm/dist/index.js +3055 -2764
  17. package/packages/orm/src/adapters/sqlite.ts +4 -1
  18. package/packages/orm/src/database.ts +108 -8
  19. package/types/cli/src/bin.d.ts +92 -0
  20. package/types/cli/src/commands/build.d.ts +2 -0
  21. package/types/cli/src/commands/generate.d.ts +47 -0
  22. package/types/cli/src/commands/init.d.ts +1 -0
  23. package/types/cli/src/commands/metrics.d.ts +6 -0
  24. package/types/cli/src/commands/migrate.d.ts +1 -0
  25. package/types/cli/src/commands/migrateCreate.d.ts +1 -0
  26. package/types/cli/src/commands/migrateRollback.d.ts +1 -0
  27. package/types/cli/src/commands/migrateStatus.d.ts +1 -0
  28. package/types/cli/src/commands/queue.d.ts +20 -0
  29. package/types/cli/src/commands/routes.d.ts +1 -0
  30. package/types/cli/src/commands/seed.d.ts +1 -0
  31. package/types/cli/src/commands/serve.d.ts +6 -0
  32. package/types/cli/src/commands/test.d.ts +1 -0
  33. package/types/core/src/ai.d.ts +64 -0
  34. package/types/core/src/api.d.ts +262 -0
  35. package/types/core/src/auth.d.ts +154 -0
  36. package/types/core/src/authGate.d.ts +20 -0
  37. package/types/core/src/background.d.ts +34 -0
  38. package/types/core/src/cache.d.ts +160 -0
  39. package/types/core/src/constants.d.ts +38 -0
  40. package/types/core/src/container.d.ts +44 -0
  41. package/types/core/src/context/chunker.d.ts +31 -0
  42. package/types/core/src/context/index.d.ts +93 -0
  43. package/types/core/src/devAdmin.d.ts +179 -0
  44. package/types/core/src/devMailbox.d.ts +54 -0
  45. package/types/core/src/docs.d.ts +141 -0
  46. package/types/core/src/docsAutoDiscovery.d.ts +6 -0
  47. package/types/core/src/dotenv.d.ts +65 -0
  48. package/types/core/src/env.d.ts +28 -0
  49. package/types/core/src/errorOverlay.d.ts +36 -0
  50. package/types/core/src/events.d.ts +75 -0
  51. package/types/core/src/fakeData.d.ts +55 -0
  52. package/types/core/src/feedback.d.ts +90 -0
  53. package/types/core/src/graphql.d.ts +207 -0
  54. package/types/core/src/health.d.ts +22 -0
  55. package/types/core/src/htmlElement.d.ts +75 -0
  56. package/types/core/src/i18n.d.ts +37 -0
  57. package/types/core/src/index.d.ts +93 -0
  58. package/types/core/src/job.d.ts +39 -0
  59. package/types/core/src/logger.d.ts +123 -0
  60. package/types/core/src/mcp.d.ts +248 -0
  61. package/types/core/src/messenger.d.ts +191 -0
  62. package/types/core/src/metrics.d.ts +77 -0
  63. package/types/core/src/middleware.d.ts +207 -0
  64. package/types/core/src/mqtt.d.ts +257 -0
  65. package/types/core/src/mqttMessage.d.ts +67 -0
  66. package/types/core/src/plan.d.ts +96 -0
  67. package/types/core/src/projectIndex.d.ts +56 -0
  68. package/types/core/src/queue.d.ts +219 -0
  69. package/types/core/src/queueBackends/kafkaBackend.d.ts +117 -0
  70. package/types/core/src/queueBackends/liteBackend.d.ts +119 -0
  71. package/types/core/src/queueBackends/mongoBackend.d.ts +97 -0
  72. package/types/core/src/queueBackends/rabbitmqBackend.d.ts +55 -0
  73. package/types/core/src/rateLimiter.d.ts +49 -0
  74. package/types/core/src/request.d.ts +25 -0
  75. package/types/core/src/response.d.ts +28 -0
  76. package/types/core/src/routeDiscovery.d.ts +12 -0
  77. package/types/core/src/router.d.ts +355 -0
  78. package/types/core/src/scss.d.ts +19 -0
  79. package/types/core/src/server.d.ts +131 -0
  80. package/types/core/src/service.d.ts +115 -0
  81. package/types/core/src/session.d.ts +256 -0
  82. package/types/core/src/sessionHandlers/childError.d.ts +34 -0
  83. package/types/core/src/sessionHandlers/databaseHandler.d.ts +42 -0
  84. package/types/core/src/sessionHandlers/mongoClient.d.ts +24 -0
  85. package/types/core/src/sessionHandlers/mongoHandler.d.ts +61 -0
  86. package/types/core/src/sessionHandlers/redisHandler.d.ts +60 -0
  87. package/types/core/src/sessionHandlers/respClient.d.ts +22 -0
  88. package/types/core/src/sessionHandlers/valkeyHandler.d.ts +65 -0
  89. package/types/core/src/static.d.ts +2 -0
  90. package/types/core/src/test.d.ts +94 -0
  91. package/types/core/src/testClient.d.ts +36 -0
  92. package/types/core/src/testing.d.ts +58 -0
  93. package/types/core/src/types.d.ts +219 -0
  94. package/types/core/src/validator.d.ts +52 -0
  95. package/types/core/src/websocket.d.ts +376 -0
  96. package/types/core/src/websocketBackplane.d.ts +166 -0
  97. package/types/core/src/websocketConnection.d.ts +54 -0
  98. package/types/core/src/wsdl.d.ts +101 -0
  99. package/types/frond/src/engine.d.ts +263 -0
  100. package/types/frond/src/index.d.ts +2 -0
  101. package/types/orm/src/adapters/firebird.d.ts +138 -0
  102. package/types/orm/src/adapters/mongodb.d.ts +81 -0
  103. package/types/orm/src/adapters/mssql.d.ts +70 -0
  104. package/types/orm/src/adapters/mysql.d.ts +66 -0
  105. package/types/orm/src/adapters/odbc.d.ts +97 -0
  106. package/types/orm/src/adapters/postgres.d.ts +85 -0
  107. package/types/orm/src/adapters/sqlite.d.ts +56 -0
  108. package/types/orm/src/autoCrud.d.ts +73 -0
  109. package/types/orm/src/baseModel.d.ts +391 -0
  110. package/types/orm/src/cachedDatabase.d.ts +177 -0
  111. package/types/orm/src/database.d.ts +609 -0
  112. package/types/orm/src/databaseResult.d.ts +85 -0
  113. package/types/orm/src/docstore.d.ts +182 -0
  114. package/types/orm/src/fakeData.d.ts +22 -0
  115. package/types/orm/src/index.d.ts +40 -0
  116. package/types/orm/src/migration.d.ts +275 -0
  117. package/types/orm/src/model.d.ts +7 -0
  118. package/types/orm/src/query.d.ts +14 -0
  119. package/types/orm/src/queryBuilder.d.ts +173 -0
  120. package/types/orm/src/realtime/index.d.ts +7 -0
  121. package/types/orm/src/realtime/models/attachment.d.ts +43 -0
  122. package/types/orm/src/realtime/models/channel.d.ts +32 -0
  123. package/types/orm/src/realtime/models/channelMember.d.ts +32 -0
  124. package/types/orm/src/realtime/models/message.d.ts +36 -0
  125. package/types/orm/src/realtime/models/workspace.d.ts +26 -0
  126. package/types/orm/src/realtime/realtime.d.ts +24 -0
  127. package/types/orm/src/realtime/storage.d.ts +61 -0
  128. package/types/orm/src/seeder.d.ts +118 -0
  129. package/types/orm/src/sqlTranslator.d.ts +134 -0
  130. package/types/orm/src/types.d.ts +138 -0
  131. package/types/orm/src/validation.d.ts +6 -0
  132. package/types/swagger/src/generator.d.ts +46 -0
  133. package/types/swagger/src/index.d.ts +2 -0
  134. package/types/swagger/src/ui.d.ts +11 -0
@@ -0,0 +1,262 @@
1
+ export interface ApiResult {
2
+ http_code: number | null;
3
+ body: unknown;
4
+ headers: Record<string, string>;
5
+ error: string | null;
6
+ }
7
+ /**
8
+ * Result of {@link Api.download}. There is no `body` field — the response
9
+ * body went to disk. `path` is the destination on success and `null` on any
10
+ * error (missing dest, HTTP error status, transport failure); the file is not
11
+ * written on error. Keeps `http_code` (snake_case) for parity with
12
+ * {@link ApiResult} and the Python/PHP/Ruby `download` return.
13
+ */
14
+ export interface DownloadResult {
15
+ http_code: number | null;
16
+ headers: Record<string, string>;
17
+ error: string | null;
18
+ path: string | null;
19
+ }
20
+ /**
21
+ * An injectable transport seam (constructor option `transport`). When supplied
22
+ * it fully REPLACES the node:http/https network call. Called as
23
+ * `(method, url, headers, body, timeout)` and must return the same result
24
+ * shape every verb returns (`{ http_code, body, headers, error }`); may be sync
25
+ * or async.
26
+ *
27
+ * NOTE: Tina4's own test suite must NEVER inject a fake/canned transport — the
28
+ * no-mock rule stands, so framework tests always exercise the real network path
29
+ * against a real local server. This seam exists purely so *application*
30
+ * developers can unit-test code that calls an `Api` instance without a live
31
+ * server.
32
+ */
33
+ export type ApiTransport = (method: string, url: string, headers: Record<string, string>, body: Buffer | null, timeout: number) => ApiResult | Promise<ApiResult>;
34
+ /**
35
+ * Options for {@link Api.upload}. Supply the file EITHER as `filePath` (a file
36
+ * on disk) OR as `fileBytes` + `filename` (an in-memory payload) — a caller
37
+ * never needs a temp file.
38
+ */
39
+ export interface UploadOptions {
40
+ /** A file on disk. `filename` defaults to its basename. */
41
+ filePath?: string;
42
+ /** The form field the file is sent under (default `"file"`). */
43
+ fieldName?: string;
44
+ /** Additional text parts of the multipart body. */
45
+ extraFields?: Record<string, string>;
46
+ /** Extra per-call headers merged onto the request. */
47
+ headers?: Record<string, string>;
48
+ /** An in-memory payload (Buffer or string). Requires `filename` for a name. */
49
+ fileBytes?: Buffer | string;
50
+ /** Filename used in the Content-Disposition part header. */
51
+ filename?: string;
52
+ }
53
+ /**
54
+ * Constructor options for {@link Api}. Used as the second argument to
55
+ * `new Api(url, { ... })` — cross-framework parity with Python
56
+ * `Api(bearer_token=, ...)` kwargs added in 3.13.x.
57
+ */
58
+ export interface ApiOptions {
59
+ authHeader?: string;
60
+ timeout?: number;
61
+ ignoreSsl?: boolean;
62
+ /** Positive form of ignoreSsl — `verifySsl: false` disables verification. */
63
+ verifySsl?: boolean;
64
+ bearerToken?: string;
65
+ username?: string;
66
+ password?: string;
67
+ headers?: Record<string, string>;
68
+ /**
69
+ * Maximum automatic retries on a transient failure (default 0 = off, so
70
+ * existing callers are unaffected). When > 0, a transport error or a
71
+ * retryable status (429/5xx) is retried up to this many times with
72
+ * exponential backoff. NOTE: a retried non-idempotent request (POST/…)
73
+ * may be re-sent — retries are opt-in for that reason.
74
+ */
75
+ maxRetries?: number;
76
+ /** Base backoff in seconds, doubling each attempt (default 0.5). */
77
+ retryBackoff?: number;
78
+ /**
79
+ * Injectable transport seam (default undefined = the real network path).
80
+ * When supplied it REPLACES the node:http/https call. See {@link ApiTransport}.
81
+ * Tina4's own suite never injects it (no-mock rule) — it exists so
82
+ * application developers can unit-test their own code.
83
+ */
84
+ transport?: ApiTransport;
85
+ /**
86
+ * Opt-in per-client, in-memory cookie jar (default false = off, zero
87
+ * behaviour change). When true, `Set-Cookie` response headers are parsed and
88
+ * the accumulated `Cookie` header is sent on subsequent requests. Not
89
+ * persisted; scoped to this instance.
90
+ */
91
+ cookies?: boolean;
92
+ }
93
+ export declare class Api {
94
+ private baseUrl;
95
+ private headers;
96
+ private timeout;
97
+ private authHeader;
98
+ private ignoreSsl;
99
+ private maxRetries;
100
+ private retryBackoff;
101
+ private transportFn?;
102
+ private cookiesEnabled;
103
+ private cookies;
104
+ /**
105
+ * Construct an Api client.
106
+ *
107
+ * Two construction styles supported:
108
+ *
109
+ * // Legacy positional form
110
+ * new Api("https://api.example.com", "Bearer token", 30);
111
+ *
112
+ * // 3.13.1: ergonomic options bag (recommended) — cross-framework
113
+ * // parity with Python tina4_python.api.Api kwargs.
114
+ * new Api("https://api.example.com", { bearerToken: "sk-abc" });
115
+ * new Api("https://api.example.com", { username: "u", password: "p" });
116
+ * new Api("https://api.example.com", { headers: { "X-Tenant": "acme" } });
117
+ * new Api("https://self-signed.local", { verifySsl: false });
118
+ *
119
+ * Bearer wins over basic-auth when both passed. `verifySsl: false` is
120
+ * the positive form of `ignoreSsl: true`; `ignoreSsl` wins when both
121
+ * supplied for backward compatibility.
122
+ *
123
+ * `maxRetries` (default 0 = off) enables automatic retry with
124
+ * exponential backoff (`retryBackoff` seconds base, doubling each
125
+ * attempt) on a transport error or a retryable status (429/5xx). A
126
+ * retried non-idempotent request (POST/…) may be re-sent — retries are
127
+ * opt-in for that reason.
128
+ *
129
+ * new Api("https://api.example.com", { maxRetries: 3, retryBackoff: 0.5 });
130
+ *
131
+ * `transport` (default undefined = the real network path) is an injectable
132
+ * seam so USERS can unit-test their own code; `cookies` (default false)
133
+ * turns on a per-client, in-memory cookie jar.
134
+ */
135
+ constructor(baseUrl?: string, authHeaderOrOptions?: string | ApiOptions, timeout?: number);
136
+ /**
137
+ * Add custom headers to all subsequent requests.
138
+ */
139
+ addHeaders(headers: Record<string, string>): void;
140
+ /**
141
+ * Set Bearer token authentication.
142
+ */
143
+ setBearerToken(token: string): void;
144
+ /**
145
+ * Set Basic authentication.
146
+ */
147
+ setBasicAuth(username: string, password: string): void;
148
+ /**
149
+ * Disable SSL certificate verification (dev/self-signed certs only).
150
+ */
151
+ setIgnoreSsl(ignore: boolean): void;
152
+ /**
153
+ * HTTP GET request.
154
+ */
155
+ get(path: string, params?: Record<string, string>): Promise<ApiResult>;
156
+ /**
157
+ * HTTP POST request.
158
+ */
159
+ post(path: string, body?: unknown, contentType?: string): Promise<ApiResult>;
160
+ /**
161
+ * HTTP PUT request.
162
+ */
163
+ put(path: string, body?: unknown, contentType?: string): Promise<ApiResult>;
164
+ /**
165
+ * HTTP PATCH request.
166
+ */
167
+ patch(path: string, body?: unknown, contentType?: string): Promise<ApiResult>;
168
+ /**
169
+ * HTTP DELETE request.
170
+ */
171
+ delete(path: string, body?: unknown): Promise<ApiResult>;
172
+ /**
173
+ * Generic request method — public entry point for any HTTP method.
174
+ */
175
+ sendRequest(method: string, path: string, body?: unknown, contentType?: string): Promise<ApiResult>;
176
+ /**
177
+ * POST a `multipart/form-data` body — a file plus optional text fields.
178
+ *
179
+ * Two ways to supply the file, so a caller never needs a temp file:
180
+ *
181
+ * - `filePath` — a file on disk. `filename` defaults to its basename.
182
+ * - `fileBytes` + `filename` — an in-memory payload (Buffer or string).
183
+ *
184
+ * `fieldName` (default `"file"`) is the form field the file is sent under.
185
+ * `extraFields` become additional text parts. `headers` are extra per-call
186
+ * headers merged onto the request. The part's Content-Type is guessed from
187
+ * the filename (falling back to `application/octet-stream`).
188
+ *
189
+ * Returns the standard {@link ApiResult}. A missing file or no source given
190
+ * returns a clean error result (`http_code` null, `error` set) — it does NOT
191
+ * throw. Retry/backoff (if configured) applies, exactly like the verbs.
192
+ *
193
+ * await api.upload("/avatars", { filePath: "/tmp/me.png" });
194
+ * await api.upload("/avatars", { fileBytes: raw, filename: "me.png",
195
+ * extraFields: { user_id: "42" } });
196
+ */
197
+ upload(path: string, opts?: UploadOptions): Promise<ApiResult>;
198
+ /**
199
+ * Stream a GET response body to `destPath` in chunks.
200
+ *
201
+ * The body is written to disk `DOWNLOAD_CHUNK_SIZE` bytes at a time instead
202
+ * of being buffered whole in memory — safe for large payloads. Redirect
203
+ * following, the cross-origin auth strip, the cookie jar, and the SSL flag
204
+ * all apply, exactly like the other verbs.
205
+ *
206
+ * Returns {@link DownloadResult} — there is no `body` field (it went to
207
+ * disk). `path` is `destPath` on success and `null` on any error (missing
208
+ * dest, HTTP error status, or a transport failure); the destination file is
209
+ * not written on error.
210
+ */
211
+ download(path: string, destPath: string, params?: Record<string, string>): Promise<DownloadResult>;
212
+ private buildUrl;
213
+ /**
214
+ * Build the request headers (auth + cookie jar + extras) and serialize the
215
+ * body to a Buffer. Shared by every verb, upload, and download so the wire
216
+ * shape is identical and the transport seam sees exactly what the network
217
+ * path would.
218
+ */
219
+ private buildRequest;
220
+ /**
221
+ * Execute the request with opt-in retry/backoff.
222
+ *
223
+ * With `maxRetries` > 0, a transport failure (`http_code` null) or a
224
+ * retryable status (429/5xx) is retried up to `maxRetries` times with
225
+ * exponential backoff; any other outcome (2xx, 3xx, other 4xx) returns
226
+ * at once. A retried non-idempotent request may be re-sent — retries
227
+ * are opt-in for that reason.
228
+ */
229
+ private execute;
230
+ /** A single HTTP attempt — returns the standardized result. */
231
+ private attempt;
232
+ /**
233
+ * Invoke a user-injected transport and normalize its result. The transport
234
+ * is called with `(method, url, headers, body, timeout)` and its returned
235
+ * `Set-Cookie` headers (if any) feed the cookie jar.
236
+ */
237
+ private callTransport;
238
+ /**
239
+ * Perform the network request, following up to `redirectsLeft` redirects.
240
+ *
241
+ * node:http/https `request` does NOT auto-follow redirects. On a 3xx with a
242
+ * Location, this drains the intermediate response and re-issues to the new
243
+ * URL: 301/302/303 on a non-GET/HEAD become GET (body dropped, urllib
244
+ * behaviour); 307/308 preserve method + body. When the redirect target is a
245
+ * DIFFERENT origin, the Authorization and Cookie headers are stripped so a
246
+ * bearer token / session cookie never leaks to a host you didn't
247
+ * authenticate to.
248
+ */
249
+ private performRequest;
250
+ /** Buffer a response body, parse JSON if possible, and store cookies. */
251
+ private readResponse;
252
+ /** The accumulated `Cookie` request header, or null when the jar is empty. */
253
+ private cookieHeader;
254
+ /**
255
+ * Parse `Set-Cookie` response headers into the jar (when enabled). Only the
256
+ * leading `name=value` pair of each is kept (Path/HttpOnly/Expires ignored);
257
+ * a later value for the same name overwrites an earlier one.
258
+ */
259
+ private storeCookies;
260
+ /** Store cookies from a plain header record (the transport seam path). */
261
+ private storeCookiesFromRecord;
262
+ }
@@ -0,0 +1,154 @@
1
+ import type { Middleware } from "./types.js";
2
+ /**
3
+ * Ensure a usable TINA4_SECRET exists. Run ONCE at server boot, after env load
4
+ * and before auth is used. Mirrors Python's `ensure_dev_secret()`.
5
+ *
6
+ * Order:
7
+ * 1. TINA4_SECRET already set → no-op (return null).
8
+ * 2. NOT dev, OR CI, OR production → emit the actionable warning, return null.
9
+ * NEVER generates or persists a secret in CI / production / non-dev.
10
+ * 3. Otherwise (dev, not CI, not prod, blank secret) → generate a 32-byte hex
11
+ * secret, set it in process.env for THIS run immediately, then try to append
12
+ * it to <cwd>/.env.local (create if missing; never touch .env). On a write
13
+ * failure keep the in-memory secret and warn — boot must never crash.
14
+ *
15
+ * @param cwd - Directory to write .env.local into. Tests pass a temp dir; production passes nothing.
16
+ * @returns The newly-generated secret, or null when nothing was generated.
17
+ */
18
+ export declare function ensureDevSecret(cwd?: string): string | null;
19
+ /**
20
+ * Seconds of clock skew tolerated on the "nbf" (not-before) claim.
21
+ *
22
+ * Without this, a token minted on one host and validated on another a second
23
+ * behind is rejected for no real reason; RFC 7519 explicitly allows "a small
24
+ * leeway". Same value as the Python master's `_JWT_LEEWAY_SECONDS`.
25
+ */
26
+ export declare const JWT_LEEWAY_SECONDS = 60;
27
+ /**
28
+ * Pick the JWT algorithm: explicit argument, else TINA4_JWT_ALGORITHM, else HS256.
29
+ *
30
+ * Throws (naming the supported set and the env var) when asked for an algorithm
31
+ * we cannot sign — a silent downgrade to HS256 is the whole bug in python#106.
32
+ *
33
+ * @param algorithm - Explicit algorithm; wins over the environment when given.
34
+ */
35
+ export declare function resolveAlgorithm(algorithm?: string): string;
36
+ /**
37
+ * Create a signed JWT token.
38
+ *
39
+ * Secret is always read from `process.env.TINA4_SECRET`.
40
+ * Algorithm is read from `process.env.TINA4_JWT_ALGORITHM` (default "HS256");
41
+ * HS256 / HS384 / HS512 / RS256 are supported and anything else throws.
42
+ *
43
+ * The header's `alg` is always the algorithm that actually signed the token.
44
+ *
45
+ * No `nbf` (not-before) claim is stamped — parity with Python and PHP. Pass your
46
+ * own `nbf` in the payload to post-date a token; `validToken` enforces it.
47
+ *
48
+ * @param payload - Claims to encode (e.g. `{ userId: 1, role: "admin" }`)
49
+ * @param secretOrExpiresIn - Signing secret string, OR expiresIn number in MINUTES (back-compat with old 2-arg form)
50
+ * @param expiresIn - Lifetime in MINUTES (default 60). `0` ⇒ no `exp` claim (non-expiring). Only used when secret is a string.
51
+ * @param algorithm - Overrides TINA4_JWT_ALGORITHM for this call.
52
+ * @returns Signed JWT string: header.payload.signature
53
+ * @throws When the resolved algorithm is not one Tina4 can sign.
54
+ */
55
+ export declare function getToken(payload: Record<string, unknown>, secretOrExpiresIn?: string | number, expiresIn?: number, algorithm?: string): string;
56
+ /**
57
+ * Validate a JWT token. Returns the decoded payload on success, `null` if
58
+ * invalid/expired/malformed.
59
+ *
60
+ * 3.13.0 — return type changed from `boolean` to `Record<string, unknown> | null`.
61
+ * Matches the convention used by `jsonwebtoken` and the Python / PHP / Ruby
62
+ * Auth.validToken signatures shipped at the same time. Legacy
63
+ * `if (validToken(t))` patterns keep working because a non-null object is
64
+ * truthy and null is falsy.
65
+ *
66
+ * Secret is read from `process.env.TINA4_SECRET` when not passed explicitly.
67
+ * Algorithm is read from `process.env.TINA4_JWT_ALGORITHM` (default "HS256").
68
+ *
69
+ * Checks, in order: the header's `alg` must BE the expected algorithm (blocks alg
70
+ * substitution, including `alg: "none"`, before any signature work), then the
71
+ * signature, then `exp`, then `nbf` (with `JWT_LEEWAY_SECONDS` of clock skew).
72
+ */
73
+ export declare function validToken(token: string, secret?: string, algorithm?: string): Record<string, unknown> | null;
74
+ /**
75
+ * Get the JWT payload WITHOUT verifying signature or expiration.
76
+ */
77
+ export declare function getPayload(token: string): Record<string, unknown> | null;
78
+ /**
79
+ * Hash a password using PBKDF2-SHA256.
80
+ *
81
+ * @param password - Plaintext password
82
+ * @param salt - Hex-encoded salt (auto-generated if omitted)
83
+ * @param iterations - PBKDF2 iterations (default 260000)
84
+ * @returns Format: `pbkdf2_sha256$iterations$salt$hash` (all hex-encoded)
85
+ */
86
+ export declare function hashPassword(password: string, salt?: string, iterations?: number): string;
87
+ /**
88
+ * Check a password against a PBKDF2 hash string.
89
+ * Supports both $ and : delimiters for backward compatibility.
90
+ */
91
+ export declare function checkPassword(password: string, hash: string): boolean;
92
+ /**
93
+ * Auth middleware that extracts and verifies a Bearer JWT from the
94
+ * Authorization header. On success, attaches the decoded payload to
95
+ * `(request as any).auth`. On failure, sends a 401 JSON response.
96
+ *
97
+ * @param secret - Signing secret / PEM public key (default: TINA4_SECRET env var).
98
+ * @param algorithm - JWT algorithm. Omit it to honour TINA4_JWT_ALGORITHM (then
99
+ * HS256). It used to default to the literal "HS256", which SHADOWED the env
100
+ * var: an app on TINA4_JWT_ALGORITHM=HS512 minted HS512 tokens and this
101
+ * middleware verified them as HS256, rejecting every valid token.
102
+ */
103
+ export declare function authMiddleware(secret?: string, algorithm?: string): Middleware;
104
+ /**
105
+ * Refresh a JWT token — validate the existing token then re-sign
106
+ * with a fresh expiry.
107
+ *
108
+ * Secret is always read from `process.env.TINA4_SECRET`.
109
+ *
110
+ * @param token - Existing JWT to refresh
111
+ * @param expiresIn - New lifetime in MINUTES (default 60)
112
+ * @returns New signed JWT string, or null if the input token is invalid/expired
113
+ */
114
+ export declare function refreshToken(token: string, expiresIn?: number): string | null;
115
+ /**
116
+ * Extract a Bearer token from request headers and validate it.
117
+ *
118
+ * @param headers - Object with header keys (e.g. `{ authorization: "Bearer ..." }`)
119
+ * @param secret - HMAC secret or PEM public key
120
+ * @param algorithm - "HS256" or "RS256" (default "HS256")
121
+ * @returns Decoded payload, or null if missing/invalid
122
+ */
123
+ export declare function authenticateRequest(headers: Record<string, string | string[] | undefined>, secret?: string, algorithm?: string): Record<string, unknown> | null;
124
+ /**
125
+ * Compare an API key against an expected value.
126
+ * If `expected` is omitted, falls back to the `TINA4_API_KEY` env var.
127
+ *
128
+ * Uses constant-time comparison to prevent timing attacks.
129
+ *
130
+ * @param provided - The API key provided by the caller
131
+ * @param expected - The correct API key (defaults to `process.env.TINA4_API_KEY`)
132
+ * @returns true if the keys match
133
+ */
134
+ export declare function validateApiKey(provided: string, expected?: string): boolean;
135
+ /**
136
+ * Auth class that wraps the standalone auth functions so both patterns work:
137
+ *
138
+ * import { Auth } from "tina4-nodejs";
139
+ * const token = Auth.getToken(payload, secret);
140
+ *
141
+ * import { getToken } from "tina4-nodejs";
142
+ * const token = getToken(payload, secret);
143
+ */
144
+ export declare class Auth {
145
+ static getToken: typeof getToken;
146
+ static validToken: typeof validToken;
147
+ static getPayload: typeof getPayload;
148
+ static hashPassword: typeof hashPassword;
149
+ static checkPassword: typeof checkPassword;
150
+ static authMiddleware: typeof authMiddleware;
151
+ static refreshToken: typeof refreshToken;
152
+ static authenticateRequest: typeof authenticateRequest;
153
+ static validateApiKey: typeof validateApiKey;
154
+ }
@@ -0,0 +1,20 @@
1
+ import type { Tina4Request, Tina4Response } from "./types.js";
2
+ /** Just the auth-relevant fields of a matched route. */
3
+ export interface AuthGateRoute {
4
+ secure?: boolean;
5
+ noAuth?: boolean;
6
+ }
7
+ /**
8
+ * Enforce auth for a matched route.
9
+ *
10
+ * Returns `true` when the request is REJECTED — a 401 has already been written
11
+ * to `res.raw` and the caller must stop (not run the handler). Returns `false`
12
+ * when the route is public OR a valid token was presented (in which case
13
+ * `req.user` is populated and, for a body formToken, a `FreshToken` header is
14
+ * set). Auth is enforced only for a route that is `secure` and not `noAuth`,
15
+ * and never for `/__dev` dev-admin routes.
16
+ *
17
+ * Token sources, in priority order: `Authorization: Bearer` header, a
18
+ * `formToken` in the parsed body, then a session token.
19
+ */
20
+ export declare function enforceRouteAuth(req: Tina4Request, res: Tina4Response, match: AuthGateRoute, isDevAdmin: boolean): boolean;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Background tasks — periodic callbacks that run alongside the HTTP server.
3
+ *
4
+ * Mirrors Python's `tina4_python.core.server.background(fn, interval=1.0)`.
5
+ * Use this instead of `setInterval` directly, so timers integrate with the
6
+ * server lifecycle and clear cleanly on graceful shutdown (SIGTERM/SIGINT)
7
+ * or when `stopAllBackgroundTasks()` is called.
8
+ *
9
+ * import { background } from "@tina4/core";
10
+ *
11
+ * background(() => processQueue(), 2); // every 2 seconds
12
+ * background(async () => await healthCheck(), 30); // async also fine
13
+ *
14
+ * Errors thrown from a callback are caught and logged so a single failing
15
+ * task cannot bring down the rest of the timer wheel.
16
+ */
17
+ /**
18
+ * Register a callback to run periodically alongside the HTTP server.
19
+ *
20
+ * @param callback Function to call (sync or async, no arguments).
21
+ * @param intervalSeconds Seconds between invocations (default: 1).
22
+ * @returns A handle whose `stop()` clears just this one task.
23
+ */
24
+ export declare function background(callback: () => unknown | Promise<unknown>, intervalSeconds?: number): {
25
+ stop: () => void;
26
+ };
27
+ /**
28
+ * Clear every registered background task. Called automatically on SIGTERM/SIGINT;
29
+ * also called from the server's `close()` so a manual server shutdown stops
30
+ * the timer wheel along with HTTP listeners.
31
+ */
32
+ export declare function stopAllBackgroundTasks(): void;
33
+ /** Number of currently-registered background tasks (test helper). */
34
+ export declare function backgroundTaskCount(): number;
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Multi-backend response cache for GET requests.
3
+ *
4
+ * Backends are selected via the TINA4_CACHE_BACKEND env var:
5
+ * memory — in-process LRU cache (default, zero deps)
6
+ * file — JSON files in data/cache/
7
+ * redis — Redis (raw RESP over TCP, or the `redis` package when present)
8
+ * valkey — Valkey (Redis wire protocol — reuses the Redis backend)
9
+ * memcached — Memcached (zero-dep text protocol over TCP; SHA-256-hashed keys)
10
+ * mongodb — MongoDB TTL collection (optional `mongodb` driver, loaded dynamically)
11
+ * database — a `tina4_cache` table in any Tina4-supported DB (via @tina4/orm)
12
+ *
13
+ * Usage (the KV/module API is ASYNC on Node — Node is async-everywhere):
14
+ * import { responseCache, cacheGet, cacheSet, cacheDelete, cacheClear, cacheStats } from "./cache.js";
15
+ *
16
+ * // As middleware — caches GET responses for ttl seconds
17
+ * middleware.use(responseCache({ ttl: 60 }));
18
+ *
19
+ * // Direct usage (await — same semantics as the other 3 languages, async transport)
20
+ * await cacheSet("key", {"data": "value"}, 120);
21
+ * const value = await cacheGet("key");
22
+ * await cacheDelete("key");
23
+ * await cacheClear();
24
+ * const stats = await cacheStats();
25
+ *
26
+ * Availability + file-fallback (mirrors the Python master):
27
+ * Each network/driver backend reports availability (redis/valkey connect+AUTH+PING,
28
+ * memcached VERSION, mongo connect+ping, database connect). When the configured
29
+ * backend's service is unreachable (or its driver is missing / credentials are
30
+ * wrong), createBackend() logs a warning and falls back to the `file` backend —
31
+ * a real, persistent cache, never a silent no-op. The probe is asynchronous, so
32
+ * createBackend() returns a Promise.
33
+ *
34
+ * Asynchronous, NATIVE network I/O (NO child process):
35
+ * The CacheBackend interface is async (get/set/delete/clear/stats return
36
+ * Promises). The network backends use native async Node I/O — redis/valkey speak
37
+ * RESP over a node:net socket (with AUTH + SELECT db), memcached speaks its text
38
+ * protocol over node:net, and mongodb uses the optional `mongodb` driver. No
39
+ * execFileSync, no child processes — connections are pooled per backend instance
40
+ * so each cache op is a single async round-trip (~sub-ms locally), not a ~30-80ms
41
+ * process spawn. Local backends (memory/file/database-sqlite) resolve immediately.
42
+ *
43
+ * Environment (LOCKED — matches Python exactly):
44
+ * TINA4_CACHE_BACKEND — memory | file | redis | valkey | memcached | mongodb | database (default: memory)
45
+ * TINA4_CACHE_URL — connection URL (redis/valkey/memcached/mongo), or a SQL URL for `database`
46
+ * (database falls back to TINA4_DATABASE_URL)
47
+ * TINA4_CACHE_TTL — default TTL in seconds (default: 60)
48
+ * TINA4_CACHE_MAX_ENTRIES — max entries (default: 1000)
49
+ * TINA4_CACHE_DIR — file backend directory (default: data/cache)
50
+ * TINA4_CACHE_USERNAME — credentials when not embedded in the URL
51
+ * TINA4_CACHE_PASSWORD — credentials when not embedded in the URL
52
+ */
53
+ import type { Middleware } from "./types.js";
54
+ export interface ResponseCacheConfig {
55
+ /** Default TTL in seconds. 0 = disabled. Default: 60 */
56
+ ttl?: number;
57
+ /** Maximum cache entries. Default: 1000 */
58
+ maxEntries?: number;
59
+ /** Only cache these status codes. Default: [200] */
60
+ statusCodes?: number[];
61
+ /** Cache backend: memory | redis | file. Default: from env or memory */
62
+ backend?: string;
63
+ /** Redis URL. Default: from env or redis://localhost:6379 */
64
+ cacheUrl?: string;
65
+ /** File cache directory. Default: from env or data/cache */
66
+ cacheDir?: string;
67
+ }
68
+ interface CacheBackend {
69
+ get(key: string): Promise<unknown | undefined>;
70
+ set(key: string, value: unknown, ttl: number): Promise<void>;
71
+ delete(key: string): Promise<boolean>;
72
+ clear(): Promise<void>;
73
+ stats(): Promise<{
74
+ hits: number;
75
+ misses: number;
76
+ size: number;
77
+ backend: string;
78
+ }>;
79
+ name(): string;
80
+ /**
81
+ * Whether this backend is actually usable (driver present + service
82
+ * reachable). Local backends (memory/file) are always available; network /
83
+ * driver backends override this so the factory can fall back to the file
84
+ * backend. Mirrors the Python master's `is_available()`. Probed asynchronously
85
+ * (connect/AUTH/PING/VERSION/ping) so no child process is spawned.
86
+ */
87
+ isAvailable?(): Promise<boolean>;
88
+ /** One-time async connect/probe. Resolves once the backend has decided
89
+ * availability; createBackend() awaits this before falling back to file. */
90
+ ready?(): Promise<void>;
91
+ }
92
+ /** Public shape of a unified cache backend (for cross-package reuse). */
93
+ export type { CacheBackend };
94
+ /**
95
+ * Build a unified cache backend from explicit params or env vars.
96
+ *
97
+ * Backends: memory (default) | file | redis | valkey | memcached | mongodb |
98
+ * database. Unreachable network/driver backends fall back to the file backend.
99
+ * ASYNC because availability is probed asynchronously (connect/AUTH/PING/ping)
100
+ * — no child process. Exported so @tina4/orm can route its persistent DB query
101
+ * cache through the SAME backends (shared cross-instance) without duplicating
102
+ * the implementation. Callers `await createBackend(...)`.
103
+ */
104
+ export declare function createBackend(config?: {
105
+ backend?: string;
106
+ cacheUrl?: string;
107
+ cacheDir?: string;
108
+ maxEntries?: number;
109
+ }): Promise<CacheBackend>;
110
+ /**
111
+ * Response cache middleware for GET requests.
112
+ * Caches the full response body, content-type, and status code through the
113
+ * unified async backend. Cache key is method + url (including query string).
114
+ *
115
+ * The middleware is ASYNC: the before-path awaits `backend.get` (serve hit) and
116
+ * the after-path awaits `backend.set` (store on the captured `res.raw.end`).
117
+ * The framework's middleware chain (`runRouteMiddlewares` / `MiddlewareChain`)
118
+ * already awaits middleware, so async is transparent. Honors ttl/statusCodes/
119
+ * maxEntries. With the default `memory` backend behaviour is unchanged; a
120
+ * redis/etc. backend distributes cross-instance.
121
+ */
122
+ export declare function responseCache(config?: ResponseCacheConfig): Middleware;
123
+ /**
124
+ * Clear all cached responses (the responseCache middleware backend).
125
+ * ASYNC on Node — callers `await clearCache()` — because the backend may be a
126
+ * network backend (redis/etc.). Resets the backend's namespace; mirrors the
127
+ * Python ResponseCache.clear_cache() which clears its backend.
128
+ */
129
+ export declare function clearCache(): Promise<void>;
130
+ /**
131
+ * Get KV cache stats — reports the same backend that cacheGet/cacheSet/cacheDelete use,
132
+ * so a value stored via cacheSet() is reflected here. Mirrors cache_stats() in the
133
+ * Python / PHP / Ruby frameworks. (Identical to cacheBackendStats(), kept for parity naming.)
134
+ * ASYNC on Node — callers `await cacheStats()`.
135
+ */
136
+ export declare function cacheStats(): Promise<{
137
+ hits: number;
138
+ misses: number;
139
+ size: number;
140
+ backend: string;
141
+ }>;
142
+ /** Get a value from the cache by key. Returns undefined on miss. */
143
+ export declare function cacheGet(key: string): Promise<unknown | undefined>;
144
+ /** Store a value in the cache with optional TTL (seconds). */
145
+ export declare function cacheSet(key: string, value: unknown, ttl?: number): Promise<void>;
146
+ /** Delete a key from the cache. Returns true if it existed. */
147
+ export declare function cacheDelete(key: string): Promise<boolean>;
148
+ /** Clear all entries from the cache. */
149
+ export declare function cacheClear(): Promise<void>;
150
+ /** Remove expired entries from the cache. Returns count removed. */
151
+ export declare function sweep(): Promise<number>;
152
+ /** Return cache statistics from the active backend. */
153
+ export declare function cacheBackendStats(): Promise<{
154
+ hits: number;
155
+ misses: number;
156
+ size: number;
157
+ backend: string;
158
+ }>;
159
+ /** Reset the default backend (for testing). Closes any pooled connection. */
160
+ export declare function _resetBackend(): void;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Tina4 Constants — HTTP status codes and content types.
3
+ *
4
+ * Standard constants for use in route handlers across all Tina4 frameworks.
5
+ *
6
+ * import { HTTP_OK, HTTP_CREATED, APPLICATION_JSON } from "@tina4/core";
7
+ *
8
+ * get("/api/users", async (request, response) => {
9
+ * return response(users, HTTP_OK);
10
+ * });
11
+ */
12
+ export declare const HTTP_OK = 200;
13
+ export declare const HTTP_CREATED = 201;
14
+ export declare const HTTP_ACCEPTED = 202;
15
+ export declare const HTTP_NO_CONTENT = 204;
16
+ export declare const HTTP_MOVED = 301;
17
+ export declare const HTTP_REDIRECT = 302;
18
+ export declare const HTTP_NOT_MODIFIED = 304;
19
+ export declare const HTTP_BAD_REQUEST = 400;
20
+ export declare const HTTP_UNAUTHORIZED = 401;
21
+ export declare const HTTP_FORBIDDEN = 403;
22
+ export declare const HTTP_NOT_FOUND = 404;
23
+ export declare const HTTP_METHOD_NOT_ALLOWED = 405;
24
+ export declare const HTTP_CONFLICT = 409;
25
+ export declare const HTTP_GONE = 410;
26
+ export declare const HTTP_UNPROCESSABLE = 422;
27
+ export declare const HTTP_TOO_MANY = 429;
28
+ export declare const HTTP_SERVER_ERROR = 500;
29
+ export declare const HTTP_BAD_GATEWAY = 502;
30
+ export declare const HTTP_UNAVAILABLE = 503;
31
+ export declare const APPLICATION_JSON = "application/json";
32
+ export declare const APPLICATION_XML = "application/xml";
33
+ export declare const APPLICATION_FORM = "application/x-www-form-urlencoded";
34
+ export declare const APPLICATION_OCTET = "application/octet-stream";
35
+ export declare const TEXT_HTML = "text/html; charset=utf-8";
36
+ export declare const TEXT_PLAIN = "text/plain; charset=utf-8";
37
+ export declare const TEXT_CSV = "text/csv";
38
+ export declare const TEXT_XML = "text/xml";