tina4-nodejs 3.13.137 → 3.13.139

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 (255) hide show
  1. package/CLAUDE.md +33 -3
  2. package/COMMERCIAL-LICENSE.md +8 -0
  3. package/LICENSE +373 -0
  4. package/NOTICE +11 -0
  5. package/README.md +24 -2
  6. package/package.json +44 -6
  7. package/packages/cli/dist/bin.js +19979 -18102
  8. package/packages/cli/src/bin.ts +8 -0
  9. package/packages/cli/src/commands/build.ts +8 -0
  10. package/packages/cli/src/commands/generate.ts +8 -0
  11. package/packages/cli/src/commands/init.ts +8 -0
  12. package/packages/cli/src/commands/lint.ts +8 -0
  13. package/packages/cli/src/commands/migrate.ts +8 -0
  14. package/packages/cli/src/commands/migrateCreate.ts +8 -0
  15. package/packages/cli/src/commands/migrateRollback.ts +8 -0
  16. package/packages/cli/src/commands/migrateStatus.ts +8 -0
  17. package/packages/cli/src/commands/queue.ts +8 -0
  18. package/packages/cli/src/commands/routes.ts +8 -0
  19. package/packages/cli/src/commands/seed.ts +8 -0
  20. package/packages/cli/src/commands/serve.ts +11 -0
  21. package/packages/cli/src/commands/test.ts +8 -0
  22. package/packages/core/dist/index.js +19976 -18097
  23. package/packages/core/gallery/auth/src/routes/api/gallery/auth/login/post.ts +8 -0
  24. package/packages/core/gallery/auth/src/routes/api/gallery/auth/verify/get.ts +8 -0
  25. package/packages/core/gallery/auth/src/routes/gallery/auth/get.ts +8 -0
  26. package/packages/core/gallery/database/src/routes/api/gallery/db/notes/get.ts +8 -0
  27. package/packages/core/gallery/database/src/routes/api/gallery/db/notes/post.ts +8 -0
  28. package/packages/core/gallery/database/src/routes/api/gallery/db/tables/get.ts +8 -0
  29. package/packages/core/gallery/error-overlay/src/routes/api/gallery/crash/get.ts +8 -0
  30. package/packages/core/gallery/orm/src/routes/api/gallery/products/get.ts +8 -0
  31. package/packages/core/gallery/orm/src/routes/api/gallery/products/post.ts +8 -0
  32. package/packages/core/gallery/queue/src/lib/queueDb.ts +8 -0
  33. package/packages/core/gallery/queue/src/routes/api/gallery/queue/consume/post.ts +8 -0
  34. package/packages/core/gallery/queue/src/routes/api/gallery/queue/fail/post.ts +8 -0
  35. package/packages/core/gallery/queue/src/routes/api/gallery/queue/produce/post.ts +8 -0
  36. package/packages/core/gallery/queue/src/routes/api/gallery/queue/retry/post.ts +8 -0
  37. package/packages/core/gallery/queue/src/routes/api/gallery/queue/status/get.ts +8 -0
  38. package/packages/core/gallery/queue/src/routes/gallery/queue/get.ts +8 -0
  39. package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/get.ts +8 -0
  40. package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/post.ts +8 -0
  41. package/packages/core/gallery/templates/src/routes/gallery/page/get.ts +8 -0
  42. package/packages/core/gallery/templates/src/templates/gallery_page.twig +8 -0
  43. package/packages/core/public/THIRD-PARTY-NOTICES.md +55 -0
  44. package/packages/core/public/bundled-component-provenance.json +48 -0
  45. package/packages/core/public/js/tina4js.min.js +13 -3
  46. package/packages/core/public/licenses/swagger-ui-3.10.0-original-notice.txt +11 -0
  47. package/packages/core/public/licenses/swagger-ui-5.17.14-NOTICE.txt +2 -0
  48. package/packages/core/public/licenses/swagger-ui-Apache-2.0.txt +202 -0
  49. package/packages/core/public/licenses/tina4-css-MIT.txt +21 -0
  50. package/packages/core/public/licenses/tina4-dev-admin-components.json +240 -0
  51. package/packages/core/public/licenses/tina4-dev-admin-third-party.txt +919 -0
  52. package/packages/core/public/licenses/tina4-js-MIT.txt +20 -0
  53. package/packages/core/src/__feedback/widget.js +8 -0
  54. package/packages/core/src/_missing.ts +8 -0
  55. package/packages/core/src/ai.ts +8 -0
  56. package/packages/core/src/aiClient.ts +8 -0
  57. package/packages/core/src/api.ts +48 -7
  58. package/packages/core/src/auth.ts +94 -14
  59. package/packages/core/src/authGate.ts +35 -20
  60. package/packages/core/src/background.ts +8 -0
  61. package/packages/core/src/cache.ts +43 -4
  62. package/packages/core/src/constants.ts +8 -0
  63. package/packages/core/src/container.ts +8 -0
  64. package/packages/core/src/context/chunker.ts +8 -0
  65. package/packages/core/src/context/index.ts +8 -0
  66. package/packages/core/src/devAdmin.ts +185 -59
  67. package/packages/core/src/devMailbox.ts +8 -0
  68. package/packages/core/src/dispatchPipeline.ts +8 -0
  69. package/packages/core/src/docs.ts +8 -0
  70. package/packages/core/src/docsAutoDiscovery.ts +8 -0
  71. package/packages/core/src/docsParser.ts +8 -0
  72. package/packages/core/src/docsScanner.ts +44 -0
  73. package/packages/core/src/docsSignatures.ts +8 -0
  74. package/packages/core/src/dotenv.ts +8 -0
  75. package/packages/core/src/env.ts +8 -0
  76. package/packages/core/src/errorOverlay.ts +8 -0
  77. package/packages/core/src/events.ts +8 -0
  78. package/packages/core/src/fakeData.ts +8 -0
  79. package/packages/core/src/feedback.ts +8 -0
  80. package/packages/core/src/graphql.ts +108 -5
  81. package/packages/core/src/health.ts +8 -0
  82. package/packages/core/src/htmlElement.ts +8 -0
  83. package/packages/core/src/i18n.ts +8 -0
  84. package/packages/core/src/index.ts +9 -0
  85. package/packages/core/src/job.ts +16 -1
  86. package/packages/core/src/logger.ts +8 -0
  87. package/packages/core/src/mcp.test.ts +8 -0
  88. package/packages/core/src/mcp.ts +11 -1
  89. package/packages/core/src/messenger.ts +143 -86
  90. package/packages/core/src/metrics.ts +13 -0
  91. package/packages/core/src/middleware.ts +1181 -1097
  92. package/packages/core/src/mqtt.ts +8 -0
  93. package/packages/core/src/mqttMessage.ts +8 -0
  94. package/packages/core/src/optionalPackage.ts +48 -0
  95. package/packages/core/src/plan.ts +8 -0
  96. package/packages/core/src/portTakeover.ts +8 -0
  97. package/packages/core/src/projectIndex.ts +8 -0
  98. package/packages/core/src/projectIndexExtractors.ts +8 -0
  99. package/packages/core/src/projectIndexStorage.ts +8 -0
  100. package/packages/core/src/push.ts +24 -0
  101. package/packages/core/src/queue.ts +23 -2
  102. package/packages/core/src/queueBackends/kafkaBackend.ts +8 -0
  103. package/packages/core/src/queueBackends/liteBackend.ts +23 -0
  104. package/packages/core/src/queueBackends/mongoBackend.ts +61 -14
  105. package/packages/core/src/queueBackends/rabbitmqBackend.ts +8 -0
  106. package/packages/core/src/rateLimiter.ts +8 -0
  107. package/packages/core/src/request.ts +59 -10
  108. package/packages/core/src/response.ts +79 -5
  109. package/packages/core/src/routeDiscovery.ts +8 -0
  110. package/packages/core/src/router.ts +8 -0
  111. package/packages/core/src/server.ts +79 -25
  112. package/packages/core/src/service.ts +8 -0
  113. package/packages/core/src/session.ts +8 -0
  114. package/packages/core/src/sessionHandlers/childError.ts +8 -0
  115. package/packages/core/src/sessionHandlers/databaseHandler.ts +68 -3
  116. package/packages/core/src/sessionHandlers/memcachedHandler.ts +8 -0
  117. package/packages/core/src/sessionHandlers/mongoClient.ts +8 -0
  118. package/packages/core/src/sessionHandlers/mongoHandler.ts +8 -0
  119. package/packages/core/src/sessionHandlers/respClient.ts +8 -0
  120. package/packages/core/src/sessionHandlers/sqlClient.ts +8 -0
  121. package/packages/core/src/sessionHandlers/syncBridge.ts +8 -0
  122. package/packages/core/src/sessionHandlers/syncSocket.ts +8 -0
  123. package/packages/core/src/sessionHandlers/valkeyHandler.ts +8 -0
  124. package/packages/core/src/sso.ts +20 -4
  125. package/packages/core/src/ssoIdentity.ts +19 -0
  126. package/packages/core/src/ssrf.ts +221 -0
  127. package/packages/core/src/static.ts +8 -0
  128. package/packages/core/src/test.ts +8 -0
  129. package/packages/core/src/testClient.ts +11 -0
  130. package/packages/core/src/testing.ts +8 -0
  131. package/packages/core/src/transport.ts +369 -0
  132. package/packages/core/src/trustedProxy.ts +8 -0
  133. package/packages/core/src/types.ts +13 -0
  134. package/packages/core/src/validator.ts +8 -0
  135. package/packages/core/src/version.ts +8 -0
  136. package/packages/core/src/websocket.ts +12 -2
  137. package/packages/core/src/websocketBackplane.ts +49 -32
  138. package/packages/core/src/websocketConnection.ts +8 -0
  139. package/packages/core/src/wsdl.ts +197 -115
  140. package/packages/core/templates/errors/302.twig +8 -0
  141. package/packages/core/templates/errors/401.twig +8 -0
  142. package/packages/core/templates/errors/403.twig +8 -0
  143. package/packages/core/templates/errors/404.twig +8 -0
  144. package/packages/core/templates/errors/500.twig +8 -0
  145. package/packages/core/templates/errors/502.twig +8 -0
  146. package/packages/core/templates/errors/503.twig +8 -0
  147. package/packages/core/templates/errors/base.twig +8 -0
  148. package/packages/frond/dist/index.js +157 -17
  149. package/packages/frond/src/engine.ts +186 -17
  150. package/packages/frond/src/index.ts +8 -0
  151. package/packages/orm/dist/index.js +8641 -6765
  152. package/packages/orm/src/adapters/firebird.ts +50 -3
  153. package/packages/orm/src/adapters/mongodb.ts +8 -0
  154. package/packages/orm/src/adapters/mssql.ts +29 -7
  155. package/packages/orm/src/adapters/mysql.ts +17 -1
  156. package/packages/orm/src/adapters/odbc.ts +11 -0
  157. package/packages/orm/src/adapters/pg-types.d.ts +8 -0
  158. package/packages/orm/src/adapters/postgres.ts +34 -8
  159. package/packages/orm/src/adapters/sqlDialect.ts +57 -0
  160. package/packages/orm/src/adapters/sqlite.ts +26 -0
  161. package/packages/orm/src/autoCrud.ts +78 -36
  162. package/packages/orm/src/baseModel.ts +61 -29
  163. package/packages/orm/src/cachedDatabase.ts +67 -1
  164. package/packages/orm/src/connectTimeout.ts +8 -0
  165. package/packages/orm/src/database.ts +649 -401
  166. package/packages/orm/src/databaseResult.ts +8 -0
  167. package/packages/orm/src/databaseUrl.ts +8 -0
  168. package/packages/orm/src/docstore.ts +23 -1
  169. package/packages/orm/src/fakeData.ts +8 -0
  170. package/packages/orm/src/graph/adapters/arango.ts +8 -0
  171. package/packages/orm/src/graph/adapters/bolt.ts +8 -0
  172. package/packages/orm/src/graph/adapters/ultipa.ts +8 -0
  173. package/packages/orm/src/graph/connectTimeout.ts +8 -0
  174. package/packages/orm/src/graph/errors.ts +8 -0
  175. package/packages/orm/src/graph/graphAdapter.ts +8 -0
  176. package/packages/orm/src/graph/graphDatabase.ts +8 -0
  177. package/packages/orm/src/graph/graphUrl.ts +10 -1
  178. package/packages/orm/src/graph/shapes.ts +8 -0
  179. package/packages/orm/src/index.ts +147 -139
  180. package/packages/orm/src/migration.ts +8 -0
  181. package/packages/orm/src/model.ts +8 -0
  182. package/packages/orm/src/modelCollection.ts +8 -0
  183. package/packages/orm/src/point.ts +8 -0
  184. package/packages/orm/src/query.ts +149 -33
  185. package/packages/orm/src/queryBuilder.ts +8 -0
  186. package/packages/orm/src/realtime/index.ts +8 -0
  187. package/packages/orm/src/realtime/models/attachment.ts +8 -0
  188. package/packages/orm/src/realtime/models/channel.ts +8 -0
  189. package/packages/orm/src/realtime/models/channelMember.ts +8 -0
  190. package/packages/orm/src/realtime/models/message.ts +8 -0
  191. package/packages/orm/src/realtime/models/workspace.ts +8 -0
  192. package/packages/orm/src/realtime/realtime.ts +8 -0
  193. package/packages/orm/src/realtime/storage.ts +43 -12
  194. package/packages/orm/src/seeder.ts +8 -0
  195. package/packages/orm/src/seederTable.ts +8 -0
  196. package/packages/orm/src/seederTypes.ts +8 -0
  197. package/packages/orm/src/sqlTranslator.ts +185 -8
  198. package/packages/orm/src/types.ts +15 -0
  199. package/packages/orm/src/validation.ts +8 -0
  200. package/packages/swagger/src/generator.ts +8 -0
  201. package/packages/swagger/src/index.ts +8 -0
  202. package/packages/swagger/src/ui.ts +8 -0
  203. package/types/core/src/api.d.ts +6 -0
  204. package/types/core/src/auth.d.ts +30 -0
  205. package/types/core/src/background.d.ts +0 -16
  206. package/types/core/src/cache.d.ts +6 -0
  207. package/types/core/src/devAdmin.d.ts +18 -12
  208. package/types/core/src/docs.d.ts +0 -17
  209. package/types/core/src/errorOverlay.d.ts +0 -24
  210. package/types/core/src/events.d.ts +0 -12
  211. package/types/core/src/feedback.d.ts +0 -23
  212. package/types/core/src/graphql.d.ts +25 -21
  213. package/types/core/src/htmlElement.d.ts +0 -15
  214. package/types/core/src/index.d.ts +1 -0
  215. package/types/core/src/job.d.ts +1 -0
  216. package/types/core/src/messenger.d.ts +12 -1
  217. package/types/core/src/metrics.d.ts +2 -0
  218. package/types/core/src/middleware.d.ts +45 -1
  219. package/types/core/src/mqtt.d.ts +0 -33
  220. package/types/core/src/optionalPackage.d.ts +25 -0
  221. package/types/core/src/plan.d.ts +0 -10
  222. package/types/core/src/projectIndex.d.ts +0 -10
  223. package/types/core/src/push.d.ts +5 -0
  224. package/types/core/src/queue.d.ts +7 -1
  225. package/types/core/src/queueBackends/liteBackend.d.ts +9 -0
  226. package/types/core/src/queueBackends/mongoBackend.d.ts +10 -1
  227. package/types/core/src/request.d.ts +12 -0
  228. package/types/core/src/response.d.ts +13 -0
  229. package/types/core/src/server.d.ts +19 -0
  230. package/types/core/src/sessionHandlers/childError.d.ts +0 -15
  231. package/types/core/src/sessionHandlers/databaseHandler.d.ts +25 -1
  232. package/types/core/src/sso.d.ts +1 -0
  233. package/types/core/src/ssoIdentity.d.ts +10 -0
  234. package/types/core/src/ssrf.d.ts +19 -0
  235. package/types/core/src/transport.d.ts +28 -0
  236. package/types/core/src/trustedProxy.d.ts +0 -16
  237. package/types/core/src/types.d.ts +5 -0
  238. package/types/core/src/websocketBackplane.d.ts +1 -0
  239. package/types/core/src/wsdl.d.ts +8 -22
  240. package/types/frond/src/engine.d.ts +7 -1
  241. package/types/orm/src/adapters/firebird.d.ts +12 -0
  242. package/types/orm/src/adapters/mssql.d.ts +11 -0
  243. package/types/orm/src/adapters/mysql.d.ts +2 -0
  244. package/types/orm/src/adapters/postgres.d.ts +11 -0
  245. package/types/orm/src/adapters/sqlDialect.d.ts +25 -0
  246. package/types/orm/src/adapters/sqlite.d.ts +7 -0
  247. package/types/orm/src/baseModel.d.ts +5 -0
  248. package/types/orm/src/cachedDatabase.d.ts +27 -0
  249. package/types/orm/src/database.d.ts +46 -7
  250. package/types/orm/src/graph/graphUrl.d.ts +0 -8
  251. package/types/orm/src/index.d.ts +1 -1
  252. package/types/orm/src/query.d.ts +48 -2
  253. package/types/orm/src/realtime/storage.d.ts +2 -1
  254. package/types/orm/src/sqlTranslator.d.ts +70 -18
  255. package/types/orm/src/types.d.ts +6 -0
@@ -105,7 +105,6 @@ export declare class Messenger {
105
105
  private fromAddress;
106
106
  private fromName;
107
107
  private encryption;
108
- private useTls;
109
108
  /** Whether an SMTP host was actually configured (see the constructor). */
110
109
  private smtpConfigured;
111
110
  /** The local mailbox, present only when this messenger captures. */
@@ -116,6 +115,13 @@ export declare class Messenger {
116
115
  private imapPass;
117
116
  private imapEncryption;
118
117
  constructor(options?: MessengerOptions);
118
+ /**
119
+ * How the SMTP connection is secured (ADR-0071 section 1). Port 465 is the
120
+ * implicit-TLS submission port whatever the setting; `ssl` is implicit TLS on
121
+ * any port; `tls` / `starttls` upgrade with a required STARTTLS; `none` is
122
+ * plaintext on purpose.
123
+ */
124
+ private smtpTransport;
119
125
  /**
120
126
  * Read-only IMAP encryption mode for inspection / tests.
121
127
  * Returns one of "tls", "starttls", "none", "ssl".
@@ -142,6 +148,11 @@ export declare class Messenger {
142
148
  private prepareRecipients;
143
149
  private connectSmtpSocket;
144
150
  private requireSmtpResponse;
151
+ /**
152
+ * Connect, read the greeting, EHLO, and - for the STARTTLS transport - upgrade.
153
+ * STARTTLS is REQUIRED: when the server does not offer it the session fails
154
+ * here, before AUTH or MAIL FROM is ever sent.
155
+ */
145
156
  private openSmtpSession;
146
157
  private authenticateSmtp;
147
158
  private sendSmtpEnvelope;
@@ -3,4 +3,6 @@ export declare class MetricsEngineError extends Error {
3
3
  }
4
4
  export declare function enginePath(): string | null;
5
5
  export declare function fullAnalysis(root?: string): Record<string, any>;
6
+ /** The root the last full scan covered ("" before any scan). */
7
+ export declare function metricsScanRoot(): string;
6
8
  export declare function fileDetail(filePath: string): Record<string, any>;
@@ -164,6 +164,8 @@ export interface CorsConfig {
164
164
  }
165
165
  /** Reset the CORS warn-once ledger. Test seam. */
166
166
  export declare function resetCorsWarnings(): void;
167
+ /** The reasons warned about so far in this process (bounded by construction). */
168
+ export declare function corsWarningReasons(): string[];
167
169
  /**
168
170
  * The resolved CORS policy — ONE implementation of the rules.
169
171
  *
@@ -210,9 +212,35 @@ export declare class CorsPolicy {
210
212
  /**
211
213
  * The CORS headers for a request origin. `isPreflight` adds Max-Age, which
212
214
  * the Fetch Standard only defines for a preflight response.
215
+ *
216
+ * `sameOrigin` (see isSameOriginRequest) marks a request whose Origin is the
217
+ * request's OWN origin. Browsers send Origin on every same-origin POST/PUT/
218
+ * PATCH/DELETE, and such a request needs no CORS headers at all, so it is not
219
+ * a CORS event: it is never warned about (tina4-python#139). It is handled by
220
+ * the configured policy exactly like any other request - the flag silences a
221
+ * warning and never grants anything, so a spoofed Host gains nothing.
222
+ *
223
+ * A warning never advises '*': it names the one origin the operator could add.
213
224
  */
214
- headersFor(requestOrigin: string, isPreflight: boolean): Record<string, string>;
225
+ headersFor(requestOrigin: string, isPreflight: boolean, sameOrigin?: boolean): Record<string, string>;
215
226
  }
227
+ /**
228
+ * Is the request SAME-origin: does its Origin equal the request's own origin?
229
+ *
230
+ * The own origin is the request's scheme (first hop of X-Forwarded-Proto, else
231
+ * the native TLS socket) and host (first hop of X-Forwarded-Host, else Host).
232
+ * Both sides go through the WHATWG URL parser, so scheme and host compare
233
+ * case-insensitively and http:80 / https:443 equal the port-less form. An
234
+ * unparseable Origin (including the opaque "null") is never same-origin.
235
+ *
236
+ * Proxy headers are honoured without a trusted-proxy check on purpose: the
237
+ * answer only decides whether a CORS WARNING is logged, never what the policy
238
+ * grants, so a forged header gains the sender nothing.
239
+ */
240
+ export declare function isSameOriginRequest(req: {
241
+ headers: Record<string, string | string[] | undefined>;
242
+ socket?: unknown;
243
+ }): boolean;
216
244
  /**
217
245
  * Built-in CORS middleware (function form).
218
246
  *
@@ -302,6 +330,22 @@ export declare class RequestLogger {
302
330
  * Router.use(SecurityHeadersMiddleware);
303
331
  */
304
332
  export declare class SecurityHeadersMiddleware {
333
+ /**
334
+ * Run BEFORE route matching (ADR-0012), so EVERY response gets the headers,
335
+ * not only a matched route's. Static files, the SPA index.html at "/",
336
+ * template pages, 404s and 405s are all answered by the not-found fallback
337
+ * chain, which only the pre-match pass reaches. As a post-match middleware
338
+ * this left an SPA's front door with no CSP and frameable (tina4-python#137).
339
+ * The headers read no route metadata, so nothing is lost by running early.
340
+ */
341
+ static preMatch: boolean;
342
+ /**
343
+ * The canonical security header set (SECHDR-DEC-01) with its environment
344
+ * overrides, WITHOUT Strict-Transport-Security, which depends on the
345
+ * request's scheme. Also what a transport rejection carries (ADR-0068
346
+ * section 4), written before the scheme is known.
347
+ */
348
+ static canonicalHeaders(): Record<string, string>;
305
349
  static beforeSecurity(req: Tina4Request, res: Tina4Response): [Tina4Request, Tina4Response];
306
350
  /** Warn-once ledger for the default-CSP heads-up (per process). */
307
351
  private static cspDefaultWarned;
@@ -1,36 +1,3 @@
1
- /**
2
- * Zero-dependency MQTT 3.1.1 client — the protocol every broker and every IoT
3
- * device already speaks.
4
- *
5
- * Built on Node's `node:net` and `node:tls` stdlib modules only: no npm package,
6
- * so an app that talks to Mosquitto / EMQX / HiveMQ / AWS IoT adds nothing to its
7
- * dependency tree. Shaped like the Queue on purpose — publish / subscribe /
8
- * consume:
9
- *
10
- * import { Mqtt } from "@tina4stack/tina4-node";
11
- *
12
- * const mqtt = new Mqtt({ url: "mqtt://broker:1883" }); // TINA4_MQTT_URL
13
- * await mqtt.connect();
14
- * await mqtt.publish("fleet/meter-42/telemetry", '{"kwh":12.5}', 1);
15
- *
16
- * for await (const message of mqtt.consume("fleet/+/telemetry", 1)) {
17
- * if (message.isDuplicate()) continue; // QoS 1 is at-least-once
18
- * store(message.topic, message.payload);
19
- * }
20
- *
21
- * Environment: TINA4_MQTT_URL (default mqtt://127.0.0.1:1883),
22
- * TINA4_MQTT_CLIENT_ID, TINA4_MQTT_KEEPALIVE (seconds, default 60),
23
- * TINA4_MQTT_CA_FILE, TINA4_MQTT_TLS_VERIFY.
24
- *
25
- * Node has no synchronous blocking socket read, so connect()/publish()/
26
- * subscribe()/receive() are async and consume() is an async generator — the same
27
- * idiom as the Queue. No background task runs by default; opt in to the
28
- * cooperative keepalive with startKeepalive(), which registers a background()
29
- * task exactly like the queue consumers do.
30
- *
31
- * Single reader: like every MQTT client the socket has ONE network reader. Call
32
- * receive()/consume() from one place.
33
- */
34
1
  import { MqttMessage } from "./mqttMessage.js";
35
2
  /** Any MQTT protocol / connection failure. */
36
3
  export declare class MqttError extends Error {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Optional peer packages: the database drivers and service clients an
3
+ * APPLICATION installs, never the framework (ADR-0067).
4
+ *
5
+ * tina4-nodejs installs as exactly one package. `pg`, `mongodb`, `redis` and
6
+ * the `@aws-sdk` S3 client are optional peerDependencies, which npm does not
7
+ * install, so for most apps "missing" is the normal state. Every feature that
8
+ * reaches for one must therefore fail at the point of use with the exact
9
+ * command that fixes it - never a bare MODULE_NOT_FOUND, never a silent no-op.
10
+ */
11
+ /** The actionable "install it" error, one wording for every optional peer. */
12
+ export declare function optionalPackageMissing(feature: string, packageNames: string[]): Error;
13
+ /**
14
+ * Resolve an optional peer from the FRAMEWORK's location and return its module
15
+ * URL, or throw the actionable error. Synchronous, so a constructor can refuse
16
+ * immediately (parity with the Python master, which raises ImportError in the
17
+ * constructor) instead of leaving a rejected promise for later.
18
+ *
19
+ * Resolving from here rather than from process.cwd() is what finds the peer an
20
+ * app installed, wherever the server was started from.
21
+ *
22
+ * @param installTogether packages the feature needs as a set (S3 needs the
23
+ * client AND the presigner), all named in the one install command.
24
+ */
25
+ export declare function resolveOptionalPackage(packageName: string, feature: string, installTogether?: string[]): string;
@@ -1,13 +1,3 @@
1
- /**
2
- * Project plan management — persistent, human-readable task state.
3
- *
4
- * Ported from tina4_python/dev_admin/plan.py (master reference).
5
- * A plan is a markdown file under plan/ at the project root with sections
6
- * for title, goal, steps (checkboxes), and notes. Exactly one plan is
7
- * "current" at a time, recorded in plan/.current.
8
- *
9
- * Byte-for-byte compatible with Python's plan/*.md storage.
10
- */
11
1
  export interface PlanStep {
12
2
  text: string;
13
3
  done: boolean;
@@ -1,13 +1,3 @@
1
- /**
2
- * Project index — lightweight, persistent "where is what" map.
3
- *
4
- * Ported from tina4_python/dev_admin/project_index.py (master reference).
5
- *
6
- * Storage: .tina4/project_index.json at the project root.
7
- * Freshness: lazy-refreshes on read via mtime compare — no watchers.
8
- * Extractors: per-language symbol extraction (TS/JS, Twig/HTML, SQL, Markdown,
9
- * Python) using regex. No LLM involvement — pure static analysis.
10
- */
11
1
  export interface FileRoute {
12
2
  method: string;
13
3
  path: string;
@@ -11,6 +11,11 @@ export interface PushOptions {
11
11
  privateKey?: string;
12
12
  ttl?: number;
13
13
  urgency?: "very-low" | "low" | "normal" | "high";
14
+ /**
15
+ * SSRF guard (ADR-0084): an explicit allow-list of hosts / host:port / CIDRs
16
+ * that bypass the private-endpoint refusal even with the opt-out off.
17
+ */
18
+ allowHosts?: string[];
14
19
  }
15
20
  export interface PushResult {
16
21
  ok: boolean;
@@ -59,7 +59,7 @@ export interface ConsumeOptions {
59
59
  export interface QueueBackendInterface {
60
60
  push(queue: string, payload: unknown, delay?: number, priority?: number): string;
61
61
  pop(queue: string): QueueJob | null;
62
- size(queue: string): number;
62
+ size(queue: string, status?: string): number;
63
63
  clear(queue: string): void;
64
64
  /**
65
65
  * Release whatever connection the backend holds, and be safe to call twice.
@@ -74,6 +74,7 @@ export interface QueueBackendInterface {
74
74
  close(): void;
75
75
  complete?(queue: string, id: string): void;
76
76
  fail?(queue: string, id: string, error: string, maxRetries: number, retryBackoff: number): void;
77
+ reject?(queue: string, id: string, reason: string, maxRetries: number): void;
77
78
  /**
78
79
  * Explicit manual re-queue: returns true if the id was found and revived,
79
80
  * false otherwise (parity with Python's backend.retry_job()). A backend may
@@ -289,6 +290,11 @@ export declare class Queue {
289
290
  * after retryBackoff seconds) or dead-letter (attempts >= maxRetries).
290
291
  */
291
292
  _failJob(queue: string, job: QueueJob, error: string, maxRetries: number): void;
293
+ /**
294
+ * Dead-letter a job immediately — no retry (ADR-0023 reject()). Distinct from
295
+ * _failJob, which retries until maxRetries is spent.
296
+ */
297
+ _rejectJob(queue: string, job: QueueJob, reason: string, maxRetries: number): void;
292
298
  /**
293
299
  * Re-queue a job back to the main queue directory with incremented attempts.
294
300
  */
@@ -130,5 +130,14 @@ export declare class LiteBackend {
130
130
  * dead-letter file -- two spellings of the same intent that previously
131
131
  * diverged.
132
132
  */
133
+ /**
134
+ * Reject a job permanently — dead-letter it NOW, no retry (ADR-0023).
135
+ *
136
+ * Unlike failJob(), which requeues until maxRetries is spent, rejectJob()
137
+ * moves a known-poison job straight to the dead-letter store. attempts is
138
+ * floored at maxRetries so deadLetters() (attempts >= maxRetries) returns it
139
+ * and size("dead") agrees.
140
+ */
141
+ rejectJob(queue: string, job: QueueJob, reason: string, maxRetries: number): void;
133
142
  retryJob(queue: string, job: QueueJob, delaySeconds?: number): void;
134
143
  }
@@ -42,6 +42,8 @@ export declare class MongoBackend implements QueueBackend {
42
42
  private collection;
43
43
  private visibilityTimeout;
44
44
  private maxRetries;
45
+ /** Module URL of the app-installed `mongodb` driver, resolved once. */
46
+ private driverUrl;
45
47
  constructor(config?: MongoConfig);
46
48
  /**
47
49
  * Resolved connection config — exposed for testing/introspection.
@@ -74,7 +76,14 @@ export declare class MongoBackend implements QueueBackend {
74
76
  popById(queue: string, id: string): QueueJob | null;
75
77
  push(queue: string, payload: unknown, delay?: number, priority?: number): string;
76
78
  pop(queue: string): QueueJob | null;
77
- size(queue: string): number;
79
+ size(queue: string, status?: string): number;
80
+ /**
81
+ * Dead-letter a job immediately — no retry (ADR-0023 reject()). Distinct from
82
+ * fail(): fail() requeues until maxRetries is spent. attempts is floored at
83
+ * maxRetries so deadLetters()/size("dead") (which filter/scope to the
84
+ * dead-letter store) see it.
85
+ */
86
+ reject(queue: string, id: string, reason: string, maxRetries: number): void;
78
87
  clear(queue: string): void;
79
88
  /**
80
89
  * Acknowledge a completed job — drop its reservation so the reclaim never
@@ -11,6 +11,18 @@ import type { Tina4Request, UploadedFile } from "./types.js";
11
11
  */
12
12
  export declare function makeCaseInsensitiveHeaders(raw: IncomingHttpHeaders): IncomingHttpHeaders;
13
13
  export declare function createRequest(req: IncomingMessage): Tina4Request;
14
+ export declare const DEFAULT_MAX_UPLOAD_SIZE = 10485760;
15
+ /**
16
+ * Read a numeric limit from the environment. A value that is not a usable
17
+ * number falls back to the default with a warning naming the variable (the
18
+ * same rule as PHP's Server::resolveLimit) - a typo must never be what
19
+ * disables a DoS guard.
20
+ */
21
+ export declare function resolveLimit(name: string, fallback: number, zeroAllowed?: boolean): number;
22
+ /** TINA4_MAX_UPLOAD_SIZE, read when it is needed so a value from .env counts. */
23
+ export declare function maxUploadSize(): number;
24
+ /** The one 413 wording (ADR-0068 section 4). */
25
+ export declare function bodyTooLargeMessage(bytes: number | bigint | string, limit: number): string;
14
26
  export declare class PayloadTooLargeError extends Error {
15
27
  statusCode: number;
16
28
  constructor(actual: number, limit: number);
@@ -1,5 +1,18 @@
1
1
  import type { ServerResponse } from "node:http";
2
2
  import type { Tina4Response } from "./types.js";
3
+ /**
4
+ * The caller's name as a JSON string literal with non-ASCII escaped, byte-
5
+ * identical to Python's json.dumps (ADR-0068), so a name that is itself broken
6
+ * cannot break the error message or the log line carrying it.
7
+ */
8
+ export declare function quoteHeaderName(name: string): string;
9
+ /**
10
+ * Refuse a response header whose name is not an HTTP token or whose value
11
+ * carries CR, LF or NUL, with the exact ADR-0068 wording. node:http refuses
12
+ * these too, but names a broken header raw (a CR/LF in the name reaches the
13
+ * log line) - this is the ADR's form, checked first.
14
+ */
15
+ export declare function assertSafeHeader(name: string, value: unknown): void;
3
16
  /**
4
17
  * Set the default templates directory for render().
5
18
  * Called by server.ts during startup.
@@ -88,6 +88,25 @@ export declare function _checkLegacyEnvVars(): void;
88
88
  * of killing an innocent process.
89
89
  */
90
90
  export declare function killPort(port: number): void;
91
+ /**
92
+ * Variables that mark a CI run, in ADR-0070's order (ci_env_vars). A run is CI
93
+ * when any of them holds a value that, trimmed and lower-cased, is non-empty
94
+ * and not in CI_NOT_SET_VALUES.
95
+ */
96
+ export declare const CI_ENVIRONMENT_VARIABLES: string[];
97
+ /** Values that say "not under CI" (ADR-0070 ci_not_set_values), compared trimmed and lower-cased. */
98
+ export declare const CI_NOT_SET_VALUES: string[];
99
+ /**
100
+ * Whether the server may open a browser (ADR-0070): only for a developer at a
101
+ * desk. Every rule is a veto; nothing forces a browser open past one.
102
+ *
103
+ * 1. development only - the caller's resolved debug mode, and never with
104
+ * TINA4_PRODUCTION truthy or in a cluster worker (the primary decides);
105
+ * 2. never when TINA4_NO_BROWSER is truthy (true/1/yes/on);
106
+ * 3. never with --no-browser / a programmatic noBrowser (it can only veto);
107
+ * 4. never under CI.
108
+ */
109
+ export declare function shouldOpenBrowser(isDevelopment: boolean, noBrowser?: boolean): boolean;
91
110
  /**
92
111
  * Resolve port and host with priority: explicit config > ENV var > default.
93
112
  * Exported for testability.
@@ -1,18 +1,3 @@
1
- /**
2
- * Tina4 session handlers — turn a failed `execFileSync` child into a readable cause.
3
- *
4
- * The session-handler interface is synchronous but every backend client is async,
5
- * so each command runs in a short-lived `node -e` child. When that child fails,
6
- * `execFileSync` throws an error whose `.message` begins "Command failed:" and
7
- * then embeds THE ENTIRE GENERATED SCRIPT — kilobytes of source with the real
8
- * reason nowhere in it. Every handler used to throw exactly that, so an
9
- * operator debugging a Redis outage got a wall of JavaScript instead of
10
- * "connect ECONNREFUSED 127.0.0.1:6379".
11
- *
12
- * The children already write the real reason to stderr; `execFileSync` captures
13
- * it on `err.stderr`. This module is the ONE place that prefers it, so the three
14
- * call sites (respClient, mongoClient, redisHandler's npm path) cannot drift.
15
- */
16
1
  /**
17
2
  * Extract the most useful one-line cause from a thrown `execFileSync` error.
18
3
  *
@@ -86,9 +86,33 @@ export declare class DatabaseSessionHandler implements SessionHandler {
86
86
  /** Run a statement that returns nothing. */
87
87
  private exec;
88
88
  /**
89
- * Ensure the session table exists (called once on first use).
89
+ * Ensure the session table exists (called once on first use) - safely when
90
+ * several processes race to create it.
91
+ *
92
+ * Every per-engine statement above is check-then-act, so two processes can
93
+ * both find the table absent and both CREATE; the loser errors, and every
94
+ * engine spells that differently (a pg_type unique violation on PostgreSQL,
95
+ * Msg 2714 on SQL Server, an RDB$RELATIONS unique violation on Firebird). So
96
+ * a failure is judged by RE-CHECKING whether the table now exists, never by
97
+ * parsing the message - the rescue the Firebird note above has always
98
+ * assumed, and which was not here. MEASURED on the lab before it: six real
99
+ * processes racing lost one to five of them on PostgreSQL, SQLite and
100
+ * Firebird.
101
+ *
102
+ * The re-check is RETRIED because the loser can fail BEFORE the winner has
103
+ * committed. On MySQL, CREATE TABLE IF NOT EXISTS takes a shared metadata
104
+ * lock, checks, then upgrades it to exclusive; two upgrades deadlock, and when
105
+ * MySQL cannot back the victim off silently it answers 1213 "Deadlock found"
106
+ * while the winner's table is still invisible (tina4-php CI run 35972320442).
107
+ * Bounded, so a genuine failure (no CREATE grant) still throws. Same attempts
108
+ * and delays in all four frameworks.
90
109
  */
91
110
  private ensureTable;
111
+ /**
112
+ * Whether tina4_session exists, asked the one way every engine answers alike:
113
+ * a query against it that returns no rows succeeds only if it is there.
114
+ */
115
+ private tableExists;
92
116
  read(sessionId: string): SessionData | null;
93
117
  write(sessionId: string, data: SessionData, ttl: number): void;
94
118
  destroy(sessionId: string): void;
@@ -2,6 +2,7 @@ import type { Router } from "./router.js";
2
2
  export declare class SsoError extends Error {
3
3
  }
4
4
  type Json = Record<string, any>;
5
+ export { liveSessionIdentity } from "./ssoIdentity.js";
5
6
  export interface SsoOptions {
6
7
  issuer?: string;
7
8
  clientId?: string;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The stored SSO identity, only while it is live (ADR-0079 s5). `stored` is the
3
+ * reserved _tina4_sso session value. The identity needs an issuer and a
4
+ * subject, and a numeric expires_at that is 0 (the provider gave no lifetime)
5
+ * or still in the future.
6
+ *
7
+ * Its own dependency-free module, so the auth gate can use it without
8
+ * importing the OIDC client (and everything the client imports).
9
+ */
10
+ export declare function liveSessionIdentity(stored: unknown): Record<string, any> | null;
@@ -0,0 +1,19 @@
1
+ export declare const ALLOW_PRIVATE_ENV = "TINA4_ALLOW_PRIVATE_REQUESTS";
2
+ /** Raised when the SSRF guard refuses an outbound request. */
3
+ export declare class SsrfError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ /** True when TINA4_ALLOW_PRIVATE_REQUESTS opts out of the guard. */
7
+ export declare function allowPrivateRequests(): boolean;
8
+ /**
9
+ * Classify one IP string: true = private/internal, refuse it. An IPv4-mapped
10
+ * IPv6 address is classified by its embedded IPv4. An unparseable value is
11
+ * treated as blocked - the guard refuses what it cannot classify.
12
+ */
13
+ export declare function isBlockedAddress(ip: string): boolean;
14
+ /**
15
+ * Refuse `url` when it targets a private/internal address. Throws SsrfError for a
16
+ * non-http(s) scheme, an unresolvable host, or any resolved address in the
17
+ * blocked space. `allowHosts` is a list of hosts / host:port / CIDRs to permit.
18
+ */
19
+ export declare function guardUrl(url: string, allowHosts?: string[]): Promise<void>;
@@ -0,0 +1,28 @@
1
+ import type { IncomingMessage, ServerResponse, Server } from "node:http";
2
+ /**
3
+ * Answer through a ServerResponse with the rejection shape and close. Every
4
+ * header already on the response is dropped: the shape is exact. Node closes
5
+ * the connection once the answer is flushed (Connection: close), so the rest
6
+ * of a refused upload is never read.
7
+ */
8
+ export declare function sendTransportRejection(res: ServerResponse, status: number, message: string, end?: {
9
+ (cb?: () => void): ServerResponse<IncomingMessage>;
10
+ (chunk: any, cb?: () => void): ServerResponse<IncomingMessage>;
11
+ (chunk: any, encoding: BufferEncoding, cb?: () => void): ServerResponse<IncomingMessage>;
12
+ }): void;
13
+ /**
14
+ * ADR-0068 section 2 for Node. node:http refuses an unsafe header where it is
15
+ * set (setHeader / appendHeader / writeHead). A header set straight on the raw
16
+ * response - past Tina4's own call-site check - used to surface as a route
17
+ * error page. Here the refusal is recorded instead, and whatever the handler
18
+ * sends next becomes the fixture's 500 {"error":"Invalid response header"},
19
+ * logged by header name. Tina4's own Response keeps refusing at the call site
20
+ * (its check runs before this layer is reached).
21
+ */
22
+ export declare function guardUnsafeHeaders(res: ServerResponse): void;
23
+ /**
24
+ * Create the HTTP server Tina4 listens with - the main port, its loopback
25
+ * siblings, the AI port and every cluster worker - so every one of them keeps
26
+ * the same limits and answers.
27
+ */
28
+ export declare function createTina4HttpServer(handler: (req: IncomingMessage, res: ServerResponse) => unknown): Server;
@@ -1,19 +1,3 @@
1
- /**
2
- * Which upstream hops are allowed to speak for a client (ADR-0019).
3
- *
4
- * X-Forwarded-For is written by whoever sends it. Reading it unconditionally
5
- * lets any client choose its own rate-limit bucket, and - worse - choose
6
- * SOMEONE ELSE'S, which is a starvation primitive against a third party. So
7
- * the forwarding headers are believed only when the raw socket peer is a proxy
8
- * the operator has explicitly declared.
9
- *
10
- * Configured by TINA4_TRUSTED_PROXIES: comma-separated exact addresses and/or
11
- * CIDR ranges, IPv4 and IPv6, e.g. "10.0.0.0/8, 192.168.1.5, ::1, fd00::/8".
12
- * Empty or unset means trust NOTHING, which is the secure default.
13
- *
14
- * Zero-dependency: node: has no CIDR matcher, so the packing and prefix
15
- * comparison are done here over plain byte arrays.
16
- */
17
1
  /** [packed network address, prefix bits] */
18
2
  type Network = [Uint8Array, number];
19
3
  /**
@@ -198,6 +198,11 @@ export interface Tina4Config {
198
198
  * and staticDir are resolved relative to this path instead of process.cwd(). */
199
199
  basePath?: string;
200
200
  routesDir?: string;
201
+ /**
202
+ * Never open a browser on start (ADR-0070 rule 3, the programmatic
203
+ * --no-browser). It can only veto: false does not force one open.
204
+ */
205
+ noBrowser?: boolean;
201
206
  modelsDir?: string;
202
207
  templatesDir?: string;
203
208
  staticDir?: string;
@@ -47,6 +47,7 @@ export declare class NATSBackplane implements WebSocketBackplane {
47
47
  private url;
48
48
  private subs;
49
49
  private ready;
50
+ private natsModuleUrl;
50
51
  constructor(url?: string);
51
52
  publish(channel: string, message: string): Promise<void>;
52
53
  subscribe(channel: string, callback: (message: string) => void): Promise<void>;
@@ -1,23 +1,3 @@
1
- /**
2
- * Tina4 WSDL/SOAP — SOAP 1.1 / WSDL 1.1 service base class.
3
- *
4
- * Auto-generates WSDL definitions and handles SOAP XML requests.
5
- * Zero external dependencies — uses simple string parsing for XML.
6
- *
7
- * Matches the PHP reference implementation (Tina4\WSDL).
8
- *
9
- * import { WSDLService, WSDLOperation } from "@tina4/core";
10
- *
11
- * class Calculator extends WSDLService {
12
- * serviceName = "Calculator";
13
- * serviceUrl = "/api/calculator";
14
- *
15
- * @WSDLOperation({ output: { Result: "int" } })
16
- * async Add(a: number, b: number): Promise<Record<string, unknown>> {
17
- * return { Result: a + b };
18
- * }
19
- * }
20
- */
21
1
  export interface WSDLOperationMeta {
22
2
  name: string;
23
3
  description?: string;
@@ -34,8 +14,14 @@ interface WSDLOperationConfig {
34
14
  *
35
15
  * @WSDLOperation({ description: "Add two numbers", input: { a: "int", b: "int" }, output: { Result: "int" } })
36
16
  * async Add(a: number, b: number): Promise<Record<string, unknown>> { ... }
17
+ *
18
+ * Works under both decorator flavours: TC39 standard decorators (what tsx and
19
+ * esbuild compile by default: `(method, context)`) and TypeScript's
20
+ * experimentalDecorators (`(prototype, name, descriptor)`).
37
21
  */
38
- export declare function WSDLOperation(config?: WSDLOperationConfig): (_target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
22
+ export declare function WSDLOperation(config?: WSDLOperationConfig): (target: unknown, context: string | symbol | {
23
+ name: string | symbol;
24
+ }, descriptor?: PropertyDescriptor) => PropertyDescriptor | undefined;
39
25
  export declare abstract class WSDLService {
40
26
  abstract serviceName: string;
41
27
  abstract serviceUrl: string;
@@ -72,7 +58,7 @@ export declare abstract class WSDLService {
72
58
  /**
73
59
  * Handle incoming SOAP request (parse XML, dispatch to method, return SOAP response).
74
60
  */
75
- handle(soapXml?: string): Promise<string>;
61
+ handle(soapXml?: string | Uint8Array): Promise<string>;
76
62
  /**
77
63
  * Register this service's routes on a router.
78
64
  * GET /service-url?wsdl -> WSDL XML
@@ -241,7 +241,13 @@ export declare class Frond {
241
241
  */
242
242
  private applyFastFilter;
243
243
  private applyRenderedFilter;
244
- private variablePermitted;
244
+ /**
245
+ * Sandbox gate for a WHOLE expression (F6/ADR-0077): refuse a dunder walk or
246
+ * a root variable outside the allow-list. Applied to output, if conditions,
247
+ * set right-hand sides and for iterables so none can smuggle a blocked
248
+ * variable past the per-output gate.
249
+ */
250
+ private sandboxExprOk;
245
251
  private resolveConcatenation;
246
252
  private applyRenderedFilters;
247
253
  private evalVarInner;
@@ -98,6 +98,12 @@ export declare class FirebirdAdapter implements DatabaseAdapter {
98
98
  /** Connect to Firebird. Must be called before using the adapter. */
99
99
  /** ADR-0044 required adapter capability. */
100
100
  getDatabaseType(): string;
101
+ /**
102
+ * Quote a name the ORM emits the way Firebird stores it: UPPER CASE (see
103
+ * fbQuote). An already-quoted name passes through - the escape hatch for a
104
+ * genuinely case-sensitive table. Python master: FirebirdAdapter.quote_identifier.
105
+ */
106
+ quoteIdentifier(name: string): string;
101
107
  /** ADR-0044: readable/writable native boolean. */
102
108
  autocommit: boolean;
103
109
  /**
@@ -186,6 +192,12 @@ export declare class FirebirdAdapter implements DatabaseAdapter {
186
192
  lastId?: number | bigint;
187
193
  }>;
188
194
  executeAsync(sql: string, params?: unknown[]): Promise<unknown>;
195
+ /**
196
+ * Run a row-producing statement ONCE and return its rows, for
197
+ * Database.execute(). node-firebird's execute() returns no rows, so a SELECT
198
+ * or an `INSERT ... RETURNING` through execute() used to lose them.
199
+ */
200
+ executeRowsAsync<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
189
201
  query<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[];
190
202
  queryAsync<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
191
203
  /**
@@ -45,8 +45,19 @@ export declare class MssqlAdapter implements DatabaseAdapter {
45
45
  * `startAt` lets a caller that has already consumed N placeholders (an UPDATE
46
46
  * whose SET values are @p0..@p{N-1}) continue the numbering into a raw WHERE
47
47
  * fragment instead of restarting at @p0.
48
+ *
49
+ * Only REAL placeholders are rewritten: a `?` inside a string literal, a
50
+ * quoted identifier or a comment is left alone (tina4-python#138).
48
51
  */
49
52
  private convertPlaceholders;
53
+ /**
54
+ * The SQL the driver receives. With NO parameters it is sent EXACTLY as
55
+ * written (the cross-framework contract): there is nothing to bind.
56
+ *
57
+ * tina4: with parameters every `?` outside a literal, quoted identifier or
58
+ * comment is a placeholder.
59
+ */
60
+ private bindable;
50
61
  execute(sql: string, params?: unknown[]): unknown;
51
62
  executeMany(sql: string, paramsList: unknown[][]): {
52
63
  totalAffected: number;
@@ -23,6 +23,8 @@ export declare class MysqlAdapter implements DatabaseAdapter {
23
23
  /** Connect to MySQL. Must be called before using the adapter. */
24
24
  /** ADR-0044 required adapter capability. */
25
25
  getDatabaseType(): string;
26
+ /** MySQL identifiers are backtick-quoted; a double-quoted token is a string. */
27
+ quoteIdentifier(name: string): string;
26
28
  /** ADR-0044: readable/writable native boolean. */
27
29
  autocommit: boolean;
28
30
  /**