tina4-nodejs 3.13.97 → 3.13.99

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 (96) hide show
  1. package/CLAUDE.md +60 -25
  2. package/package.json +1 -2
  3. package/packages/cli/dist/bin.js +20620 -18995
  4. package/packages/cli/src/bin.ts +28 -71
  5. package/packages/cli/src/commands/migrate.ts +36 -75
  6. package/packages/cli/src/commands/migrateRollback.ts +10 -1
  7. package/packages/cli/src/commands/test.ts +92 -21
  8. package/packages/core/dist/index.js +20459 -18815
  9. package/packages/core/public/js/tina4-dev-admin.min.js +23 -19
  10. package/packages/core/src/ai.ts +28 -12
  11. package/packages/core/src/api.ts +13 -5
  12. package/packages/core/src/background.ts +9 -3
  13. package/packages/core/src/devAdmin.ts +135 -20
  14. package/packages/core/src/dispatchPipeline.ts +185 -1
  15. package/packages/core/src/docs.ts +33 -5
  16. package/packages/core/src/env.ts +1 -1
  17. package/packages/core/src/errorOverlay.ts +39 -48
  18. package/packages/core/src/fakeData.ts +15 -0
  19. package/packages/core/src/index.ts +17 -6
  20. package/packages/core/src/logger.ts +892 -572
  21. package/packages/core/src/mcp.ts +9 -1
  22. package/packages/core/src/messenger.ts +31 -4
  23. package/packages/core/src/middleware.ts +169 -43
  24. package/packages/core/src/portTakeover.ts +232 -0
  25. package/packages/core/src/request.ts +57 -8
  26. package/packages/core/src/response.ts +67 -0
  27. package/packages/core/src/router.ts +35 -7
  28. package/packages/core/src/server.ts +450 -190
  29. package/packages/core/src/static.ts +81 -12
  30. package/packages/core/src/testClient.ts +126 -137
  31. package/packages/core/src/testing.ts +16 -12
  32. package/packages/core/src/types.ts +21 -9
  33. package/packages/core/src/version.ts +66 -0
  34. package/packages/core/src/websocket.ts +2 -2
  35. package/packages/core/src/websocketBackplane.ts +2 -2
  36. package/packages/frond/dist/index.js +31 -13
  37. package/packages/frond/src/engine.ts +39 -7
  38. package/packages/orm/dist/index.js +10879 -9258
  39. package/packages/orm/src/adapters/firebird.ts +200 -27
  40. package/packages/orm/src/adapters/mongodb.ts +160 -10
  41. package/packages/orm/src/adapters/mssql.ts +38 -11
  42. package/packages/orm/src/adapters/mysql.ts +24 -1
  43. package/packages/orm/src/adapters/odbc.ts +127 -29
  44. package/packages/orm/src/adapters/postgres.ts +18 -0
  45. package/packages/orm/src/adapters/sqlite.ts +93 -14
  46. package/packages/orm/src/autoCrud.ts +72 -8
  47. package/packages/orm/src/baseModel.ts +323 -71
  48. package/packages/orm/src/cachedDatabase.ts +48 -1
  49. package/packages/orm/src/database.ts +162 -59
  50. package/packages/orm/src/fakeData.ts +6 -2
  51. package/packages/orm/src/index.ts +4 -1
  52. package/packages/orm/src/migration.ts +95 -52
  53. package/packages/orm/src/query.ts +16 -4
  54. package/packages/orm/src/seeder.ts +43 -25
  55. package/packages/orm/src/sqlTranslator.ts +104 -19
  56. package/packages/orm/src/types.ts +97 -21
  57. package/packages/orm/src/validation.ts +5 -1
  58. package/packages/swagger/dist/index.js +3 -2
  59. package/packages/swagger/src/generator.ts +19 -4
  60. package/packages/swagger/src/ui.ts +6 -4
  61. package/types/cli/src/bin.d.ts +0 -22
  62. package/types/core/src/api.d.ts +11 -4
  63. package/types/core/src/background.d.ts +5 -2
  64. package/types/core/src/devAdmin.d.ts +35 -0
  65. package/types/core/src/dispatchPipeline.d.ts +41 -1
  66. package/types/core/src/errorOverlay.d.ts +13 -13
  67. package/types/core/src/index.d.ts +9 -6
  68. package/types/core/src/logger.d.ts +111 -185
  69. package/types/core/src/middleware.d.ts +40 -5
  70. package/types/core/src/portTakeover.d.ts +50 -0
  71. package/types/core/src/request.d.ts +15 -0
  72. package/types/core/src/response.d.ts +29 -0
  73. package/types/core/src/server.d.ts +92 -0
  74. package/types/core/src/testClient.d.ts +29 -3
  75. package/types/core/src/testing.d.ts +16 -12
  76. package/types/core/src/types.d.ts +21 -9
  77. package/types/core/src/version.d.ts +11 -0
  78. package/types/core/src/websocketBackplane.d.ts +1 -1
  79. package/types/frond/src/engine.d.ts +10 -0
  80. package/types/orm/src/adapters/firebird.d.ts +61 -2
  81. package/types/orm/src/adapters/mongodb.d.ts +20 -0
  82. package/types/orm/src/adapters/mssql.d.ts +11 -0
  83. package/types/orm/src/adapters/mysql.d.ts +11 -0
  84. package/types/orm/src/adapters/odbc.d.ts +35 -4
  85. package/types/orm/src/adapters/postgres.d.ts +11 -0
  86. package/types/orm/src/adapters/sqlite.d.ts +23 -4
  87. package/types/orm/src/baseModel.d.ts +45 -25
  88. package/types/orm/src/cachedDatabase.d.ts +27 -1
  89. package/types/orm/src/database.d.ts +56 -6
  90. package/types/orm/src/index.d.ts +3 -2
  91. package/types/orm/src/migration.d.ts +23 -5
  92. package/types/orm/src/query.d.ts +3 -0
  93. package/types/orm/src/seeder.d.ts +15 -2
  94. package/types/orm/src/sqlTranslator.d.ts +17 -4
  95. package/types/orm/src/types.d.ts +75 -16
  96. package/packages/core/src/errorOverlay.test.ts +0 -122
package/CLAUDE.md CHANGED
@@ -1,10 +1,10 @@
1
- # CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.97)
1
+ # CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.99)
2
2
 
3
3
  > This file helps AI assistants (Claude, Copilot, Cursor, etc.) understand and work on this codebase effectively.
4
4
 
5
5
  ## What This Project Is
6
6
 
7
- Tina4 for Node.js/TypeScript v3.13.97 - The Intelligent Native Application 4ramework. A convention-over-configuration structural paradigm. The developer writes TypeScript; Tina4 is invisible infrastructure.
7
+ Tina4 for Node.js/TypeScript v3.13.99 - The Intelligent Native Application 4ramework. A convention-over-configuration structural paradigm. The developer writes TypeScript; Tina4 is invisible infrastructure.
8
8
 
9
9
  The philosophy: zero ceremony, batteries included, file system as source of truth.
10
10
 
@@ -110,9 +110,9 @@ The HTTP foundation. Handles request/response lifecycle, route matching, middlew
110
110
  - `types.ts` — All shared type definitions (`Tina4Request`, `Tina4Response`, `RouteHandler`, etc.)
111
111
  - `events.ts` — Observer-pattern event system (`Events.on`, `emit`, `once`, `off`, `clear`)
112
112
  - `ai.ts` — AI coding tool context installer (`AI_TOOLS`, `isInstalled`, `showMenu`, `installSelected`, `installAll`, `generateContext`)
113
- - `errorOverlay.ts` — Rich debug error page for dev mode (`renderErrorOverlay`, `renderProductionError`, `isDebugMode`)
113
+ - `errorOverlay.ts` — Rich debug error page for dev mode (`renderErrorOverlay`, `isDebugMode`)
114
114
  - `htmlElement.ts` — Programmatic HTML builder (`HtmlElement`, `htmlElement`, `addHtmlHelpers`)
115
- - `testing.ts` — Inline testing framework (`tests`, `assertEqual`, `assertRaises`, `runAll`)
115
+ - `testing.ts` — Inline testing framework (`tests`, `expectEqual`, `expectRaises`, `runAll`)
116
116
  - `fakeData.ts` — Core fake data generator (names, emails, addresses, UUIDs, etc.)
117
117
  - `constants.ts` — HTTP status codes (`HTTP_OK`, `HTTP_NOT_FOUND`, etc.) and content types (`APPLICATION_JSON`, `TEXT_HTML`, etc.)
118
118
  - `devAdmin.ts` — Dev toolbar (fixed bottom bar injected into HTML pages) and admin dashboard at `/_dev/`
@@ -157,7 +157,7 @@ Database layer with auto-CRUD generation, seeding, fake data, and SQL translatio
157
157
  - `sqlTranslator.ts` — Cross-engine SQL translator (`SQLTranslator`) and TTL query cache (`QueryCache`)
158
158
  - **Instance methods:** `save(): this|false` (fluent, false on failure), `delete()`, `forceDelete()`, `restore()`, `load(sql, params?, include?): boolean`, `validate(): string[]`, `toDict(include?)`, `toAssoc(include?)`, `toObject()`, `toArray(): unknown[]`, `toList()`, `toJson(include?)`, `hasOne(class, fk)`, `hasMany(class, fk, limit?, offset?)`, `belongsTo(class, fk)`
159
159
  - **Static methods:** `find(id, include?)`, `findById(id, include?)`, `findOrFail(id)`, `create(data)`, `all(limit=100, offset=0, include?, orderBy?)`, `select(sql, params?, limit=100, offset=0)`, `selectOne(sql, params?, include?)`, `where(conditions, params?, limit=100, offset=0, include?, orderBy?)`, `count(conditions?, params?)`, `withTrashed(conditions?, params?, limit=100, offset=0)`, `scope(name, filterSql, params?)` (registers reusable method), `createTable()`, `query()`, `_processForeignKeys()`, `_applyFkRegistry()`
160
- - **Foreign key auto-wire:** Declare a field with `type: "foreignKey"` and `references: "ModelName"` to auto-wire both `belongsTo` on the declaring model and `hasMany` on the referenced model. Optional `relatedName` overrides the has-many key. Models must be registered via `BaseModel.registerModel(name, class)` for name-based resolution. Example: `user_id: { type: "foreignKey", references: "User" }` `post.belongsTo(User, "user_id")` and `user.hasMany(Post, "user_id")` both resolve without extra wiring.
160
+ - **Foreign key auto-wire (declarative, read-side-only):** Declare a field with `type: "foreignKey"` and `references: "ModelName"` to auto-wire both `belongsTo` on the declaring model and `hasMany` on the referenced model. Optional `relatedName` overrides the has-many key. Models must be registered via `BaseModel.registerModel(name, class)` for name-based resolution. Example: `author_id: { type: "foreignKey", references: "Author", relatedName: "posts" }` attaches LAZY accessors on both sides — `await post.author` (belongsTo) and `await author.posts` (hasMany) resolve on attribute access (async, cached), and `include: ["posts"]` on `find`/`all`/`where` eager-loads them in ONE query per relation. A soft-deleted child is excluded from traversal, and the has-many read is uncapped. The auto-wire emits NO DB-level FK / ON DELETE clause (REL-DEC-01, read-side-only): referential integrity is the migration/DDL's job, so deleting a parent does not cascade to children at the engine level. (Before 3.13.99 the declarative accessors did not attach and lazy load did not exist — only the imperative `post.belongsTo(Author, "author_id")` / `author.hasMany(Post, "author_id")` worked.)
161
161
  - QueryBuilder supports `toMongo()` for generating MongoDB query documents from the same fluent API
162
162
  - `getNextId(table: string, pkColumn?: string, generatorName?: string): Promise<number>` — Race-safe ID generation using atomic sequence table (`tina4_sequences`). SQLite/MySQL/MSSQL use `tina4_sequences` with atomic UPDATE+SELECT. PostgreSQL auto-creates sequences if missing. Firebird uses existing generators (unchanged).
163
163
 
@@ -321,7 +321,7 @@ Auto-generates OpenAPI 3.0.3 docs.
321
321
  **Environment (read by `generator.ts` / `ui.ts`):**
322
322
  - `TINA4_SWAGGER_ENABLED` - turns the `/swagger` UI + `/swagger/openapi.json` endpoints on/off (`ui.ts`). Explicit `true`/`false` wins; unset falls back to `TINA4_DEBUG`. Set `false` to DISABLE swagger in ANY environment (including dev); set `true` to expose it in production. This is the documented production on/off switch (wired for real in 3.13.40 - previously ignored). **This is how you disable swagger.**
323
323
  - `TINA4_SWAGGER_SERVERS` - comma-separated list of server URLs for the OpenAPI `servers[]` block (multi-server / multi-environment). Falls back to `SWAGGER_DEV_URL`, else the framework default.
324
- - `TINA4_SWAGGER_UI_CDN` - base URL for the Swagger UI assets (`swagger-ui.css` + `swagger-ui-bundle.js`). Defaults to the public CDN (`https://unpkg.com/swagger-ui-dist@5`); point it at a self-hosted mirror for air-gapped deployments.
324
+ - `TINA4_SWAGGER_UI_CDN` - base URL for the Swagger UI assets (`swagger-ui.css` + `swagger-ui-bundle.js`). Defaults to the public CDN (`https://cdn.jsdelivr.net/npm/swagger-ui-dist@5`, matching python/php/ruby); point it at a self-hosted mirror for air-gapped deployments.
325
325
  - Info block: `TINA4_SWAGGER_TITLE`, `TINA4_SWAGGER_VERSION`, `TINA4_SWAGGER_DESCRIPTION`, `TINA4_SWAGGER_CONTACT_EMAIL`, `TINA4_SWAGGER_CONTACT_TEAM`, `TINA4_SWAGGER_CONTACT_URL`, `TINA4_SWAGGER_LICENSE`.
326
326
 
327
327
  **Configurability (v3.13.42):**
@@ -409,21 +409,20 @@ const doc = generateContext("cursor");
409
409
  Rich HTML error page for development mode. Uses Catppuccin Mocha colour palette, shows syntax-highlighted source context around the error line, stack trace with source preview, request details, and environment info. Controlled by `TINA4_DEBUG` env var.
410
410
 
411
411
  ```typescript
412
- import { renderErrorOverlay, renderProductionError, isDebugMode } from "@tina4/core";
412
+ import { renderErrorOverlay, isDebugMode } from "@tina4/core";
413
413
 
414
- // In a route error handler:
414
+ // In a route error handler — dev only:
415
415
  try {
416
416
  await handler(req, res);
417
417
  } catch (err) {
418
- const html = isDebugMode()
419
- ? renderErrorOverlay(err as Error, req) // full debug overlay
420
- : renderProductionError(500, "Internal Server Error"); // safe production page
421
- res.html(html, 500);
418
+ if (isDebugMode()) res.html(renderErrorOverlay(err as Error, req), 500);
422
419
  }
423
420
 
424
421
  // isDebugMode() returns true when TINA4_DEBUG is "true"
425
422
  ```
426
423
 
424
+ The overlay is dev-only (gated on `isDebugMode()`/`TINA4_DEBUG`). The production 500 is NOT rendered here — the server dispatch renders `errors/500.twig` with an empty `error_message` (CWE-209), so the exception detail stays in the server log only. Sensitive request fields (Authorization / Cookie / Set-Cookie headers and password-like body/param keys) are redacted even in the overlay, the frame count is capped, and the dispatch guards the render.
425
+
427
426
  ## Module: HtmlElement (`packages/core/src/htmlElement.ts`)
428
427
 
429
428
  Programmatic HTML builder that avoids string concatenation. Three usage patterns: direct construction, builder-pattern functions, and helper injection.
@@ -461,16 +460,16 @@ Void tags (`br`, `hr`, `img`, `input`, `meta`, etc.) render without closing tags
461
460
 
462
461
  ## Module: Inline Testing (`packages/core/src/testing.ts`)
463
462
 
464
- Attach test assertions directly to functions. Tests are registered globally and run with `runAll()`. No external test runner needed.
463
+ Attach inline test expectations directly to functions with the `expect*` DESCRIPTOR builders — named apart from the xUnit `assert*` on `Tina4Test` (test.ts) so the two surfaces never collide. `npx tsx packages/cli/src/bin.ts test` (or `npx tina4nodejs test`) discovers `tests()`-decorated functions under `src/` and runs them with a real exit code (non-zero on any failure), then runs the file-based `test/` suite.
465
464
 
466
465
  ```typescript
467
- import { tests, assertEqual, assertRaises, assertTrue, assertFalse, runAll, reset } from "@tina4/core";
466
+ import { tests, expectEqual, expectRaises, expectTrue, expectFalse, runAll, reset } from "@tina4/core";
468
467
 
469
- // Decorate a function with inline tests
468
+ // Decorate a function with inline expectations
470
469
  const add = tests(
471
- assertEqual([5, 3], 8), // add(5, 3) === 8
472
- assertEqual([0, 0], 0), // add(0, 0) === 0
473
- assertRaises(Error, [null]), // add(null) throws Error
470
+ expectEqual([5, 3], 8), // add(5, 3) === 8
471
+ expectEqual([0, 0], 0), // add(0, 0) === 0
472
+ expectRaises(Error, [null]), // add(null) throws Error
474
473
  )(function add(a: number, b: number | null = null): number {
475
474
  if (b === null) throw new Error("b required");
476
475
  return a + b;
@@ -483,9 +482,9 @@ add(2, 3); // 5
483
482
  const results = runAll({ quiet: false, failfast: false });
484
483
  // → { passed: 3, failed: 0, errors: 0, details: [...] }
485
484
 
486
- // Additional assertion types
487
- assertTrue([someArgs]); // result is truthy
488
- assertFalse([someArgs]); // result is falsy
485
+ // Additional expectation types
486
+ expectTrue([someArgs]); // result is truthy
487
+ expectFalse([someArgs]); // result is falsy
489
488
 
490
489
  // Reset registry between test runs
491
490
  reset();
@@ -823,7 +822,7 @@ bindDatabase(await createAdapterFromUrl("postgres://localhost:5432/analytics"),
823
822
  // silently falling back to the default. (initDatabase / the internal setAdapter are unchanged.)
824
823
  ```
825
824
 
826
- **Soft delete:** set `static softDelete = true`. Server boot (`syncModels()`) adds the `is_deleted` INTEGER column (0/1) but **`Model.createTable()` does not**, so declare it there yourself. `delete()` flips the flag, `forceDelete()` removes the row, `restore()` clears it.
825
+ **Soft delete:** set `static softDelete = true`. Server boot (`syncModels()`) AND `Model.createTable()` both add the `is_deleted` INTEGER column (0/1) automatically for a soft-delete model that does not declare it (SOFTDEL-DEC-02, 3.13.99). `delete()` flips the flag, `forceDelete()` removes the row, `restore()` clears it.
827
826
 
828
827
  ## Module: QueryBuilder (`packages/orm/src/queryBuilder.ts`)
829
828
 
@@ -887,7 +886,7 @@ syncModels(discoveredModels); // auto-create tables / add columns
887
886
  - Files are applied in **numeric-prefix order** (`9_` before `10_` — a plain lexical sort misorders unpadded prefixes because `"10" < "9"`). A file with no numeric/timestamp prefix sorts **after** the numbered ones (lexically) and logs a `Log.warning` — its order is undefined.
888
887
  - State is tracked in the `tina4_migration` table (auto-created per engine, canonical columns `id, migration_name VARCHAR(500) NOT NULL UNIQUE, description VARCHAR(500), batch INTEGER NOT NULL DEFAULT 1, executed_at VARCHAR(50) NOT NULL, passed INTEGER NOT NULL DEFAULT 1` - identical across all four frameworks). A migration is **applied** when a row exists for it with `passed = 1` (the applied-read is `WHERE passed = 1`). `migrate()` writes **only `passed = 1` rows**, and it does so **delete-before-insert**: on success it DELETEs any existing row for that `migration_name` and then INSERTs the fresh `passed = 1` row (the shared `recordApplied()` helper, mirroring the Python master's `_record_applied()`), so the table holds **at most one row per `migration_name`** - latest state wins. A FAILED migration file is rolled back and **no row is written** for it (it is NOT recorded as `passed = 0`; the record step is never reached), and the run STOPS (the `migrate()` summary's `failed[]` carries the failure). The public `recordMigration(name, batch, passed)` API can write a `passed = 0` row (and one may be carried over from an older table); any `passed = 0` row is treated as **not applied**. Because the success path deletes any existing row for the `migration_name` before the `passed = 1` INSERT, a leftover `passed = 0` row **re-applies cleanly** on the next `migrate()` - the stale row is superseded rather than colliding on the UNIQUE `migration_name` (that collision previously wedged a re-run). Fix the bad file and re-run.
889
888
  - **Each migration FILE is wrapped in its own transaction.** On a failure the file rolls back and `migrate()` **STOPS** — later files are never applied on top of a missing earlier one (parity with Python/PHP/Ruby). Already-applied files stay applied. The explicit `tina4 migrate` CLI surfaces a non-empty `failed[]` as a non-zero exit; startup auto-migration logs it and the service still boots (see `TINA4_AUTO_MIGRATE` above).
890
- - **Atomicity caveat:** per-file transactions are truly atomic only on engines with **transactional DDL (PostgreSQL)**. MySQL, Firebird, and SQLite auto-commit DDL, so a multi-statement migration that fails midway on those engines leaves earlier statements applied — keep one logical change per file. `CREATE TABLE` and `ALTER TABLE ... ADD` are made idempotent on Firebird/MSSQL (existence-checked via `RDB$RELATION_FIELDS` / `tableExists`) so a re-run with a raw `CREATE`/`ADD` does not error "object already exists"; SQLite/MySQL/PostgreSQL support `IF NOT EXISTS` and are left to the engine. Only a genuine already-exists is skipped — every other error still raises.
889
+ - **Atomicity caveat:** per-file transactions are truly atomic on engines with **transactional DDL (PostgreSQL, and SQLite)**. SQLite's DDL is transactional too (autocommit is off inside `adapterStartTransaction`), so a multi-statement migration that fails midway on SQLite rolls back cleanly, including any `CREATE TABLE` that already ran earlier in the same file — proven by `test/migrationContract.test.ts`. MySQL and Firebird **auto-commit DDL**, so the same failure on those two engines leaves earlier statements applied — keep one logical change per file there. `CREATE TABLE` and `ALTER TABLE ... ADD` are made idempotent on Firebird/MSSQL (existence-checked via `RDB$RELATION_FIELDS` / `tableExists`) so a re-run with a raw `CREATE`/`ADD` does not error "object already exists"; SQLite/MySQL/PostgreSQL support `IF NOT EXISTS` and are left to the engine. Only a genuine already-exists is skipped — every other error still raises.
891
890
  - The stored-proc block delimiters (`$$ … $$` / `// … //`) are extracted before splitting, but a `//` preceded by a colon is **not** treated as a delimiter, so a URL (`https://…`) or any `://` literal inside a migration is never swallowed as an opaque block.
892
891
 
893
892
  Schema sync (`syncModels`) runs alongside SQL migrations on boot.
@@ -1204,7 +1203,7 @@ import { Router } from "./router.js"; // .js even though the file is .ts
1204
1203
  1. **Native `node:http`** — No framework dependency. Zero overhead.
1205
1204
  2. **`tsx` for dev** — No build step needed during development. TypeScript runs directly.
1206
1205
  3. **Convention-based models** — `static fields = {}` over decorators. No special TypeScript config needed.
1207
- 4. **CDN for Swagger UI** — Keeps install under 8MB. Single HTML file loads from unpkg.com.
1206
+ 4. **CDN for Swagger UI** — Keeps install under 8MB. Single HTML file loads from jsdelivr.net (the same default across all four frameworks).
1208
1207
  5. **Browser reload, not process restart** — The `tina4` Rust CLI watches `src/`, `migrations/`, `.env` and POSTs `/__dev/api/reload` to the running server. The server stays up; only the browser reloads (via WS on `/__dev_reload`, polling fallback on `GET /__dev/api/mtime`). No ESM HMR gymnastics, no server restart, no framework-side watcher.
1209
1208
  6. **SQLite default** — `node:sqlite` is synchronous and fast. Full adapters for Postgres, MySQL, MSSQL/SQL Server, and Firebird.
1210
1209
  7. **CLI named `tina4nodejs`** (primary) with `tina4` as alias — So `npx tina4nodejs init` or `npx tina4 init` both work.
@@ -1385,7 +1384,7 @@ When adding new features, add a corresponding `test/<feature>.test.ts` file.
1385
1384
  ## v3 Features Summary
1386
1385
 
1387
1386
  - **98 built-in features**, zero third-party dependencies
1388
- - **7,537 tests** passing, 0 failed, **0 skipped** across 262 files (typecheck exit 0) - measured 2026-08-06 on the lab host (Ubuntu 24.04.4 LTS x86_64, Node v24.18.0) against live services with TINA4_REQUIRE_SERVICES=1. **Firebird is NOT excluded** - `test/firebird*.test.ts` runs against a REAL Firebird 5. The last six skips are closed, each by giving it the environment its condition needs rather than by relaxing what it asserts: the four missing-driver cases run in a child process that genuinely cannot resolve the driver (the source is copied out of the repo to a tree with no `node_modules` above it and run with plain `node` - `test/_driverlessTree.ts`, no resolver shim); the strict-mode write failure drops the effective uid so a 0400 file really denies root; and the RabbitMQ default-config round-trip clears the per-framework isolation overrides for that one case so "default" means the default. Every one was proven able to FAIL by mutating the code it guards.
1387
+ - **7,537 tests** passing, 0 failed, **0 skipped** across 262 files (typecheck exit 0) - measured 2026-08-06 on the lab host (Ubuntu 24.04.4 LTS x86_64, Node v24.18.0) against live services with TINA4_REQUIRE_SERVICES=1. **Firebird runs on the lab but IS excluded from the require-services gate** - `test/_serviceGate.ts` lists `firebird` in `EXCLUDED_KEYWORDS` because GitHub CI provisions no Firebird, so a Firebird skip has to stay green there; the lab provisions a live Firebird 5 (`TINA4_TEST_FIREBIRD_URL`) and `test/firebird*.test.ts` (plus the feature-12 `test/firebirdProviderContract.test.ts`) runs against it. Those are two different things: real-Firebird coverage is enforced on the LAB, not by CI (FB-GATE-EXCLUDED). `node-firebird` is an optionalDependency of `@tina4/orm`, not a devDependency. The last six skips are closed, each by giving it the environment its condition needs rather than by relaxing what it asserts: the four missing-driver cases run in a child process that genuinely cannot resolve the driver (the source is copied out of the repo to a tree with no `node_modules` above it and run with plain `node` - `test/_driverlessTree.ts`, no resolver shim); the strict-mode write failure drops the effective uid so a 0400 file really denies root; and the RabbitMQ default-config round-trip clears the per-framework isolation overrides for that one case so "default" means the default. Every one was proven able to FAIL by mutating the code it guards.
1389
1388
  - **Race-safe `getNextId()`** with atomic sequence table (`tina4_sequences`) for SQLite/MySQL/MSSQL; PostgreSQL auto-creates sequences
1390
1389
  - **Frond template engine optimizations**: pre-compiled regexes, lazy loop context (copy-on-write), filter chain caching, path split caching, inline common filters (11-15% speedup)
1391
1390
  - **Production server auto-detect**: `npx tina4nodejs serve --production` auto-uses cluster mode
@@ -1426,6 +1425,42 @@ Always read and follow the instructions in .claude/skills/tina4-developer-nodejs
1426
1425
  ## Tina4-js Frontend Skill
1427
1426
  Always read and follow the instructions in .claude/skills/tina4-js/SKILL.md when working with tina4-js frontend code. Read its referenced files in .claude/skills/tina4-js/references/ as needed.
1428
1427
 
1428
+ ## The Uniform Plan (cross-framework audit + consolidations)
1429
+
1430
+ Tina4 is one framework in four languages, so the feature-by-feature audit and the
1431
+ contract-fixture consolidations live in ONE framework-agnostic place, not per
1432
+ repo: **`tina4-documentation/plan/v3/`** (the `tina4-documentation` repo, `main`
1433
+ branch). Cite plan docs by repo-prefixed path.
1434
+
1435
+ - `plan/v3/98-feature-audit.md` - the master audit tracker: audit every feature,
1436
+ pick the best implementation (ADR-0004 "best implementation prevails"), park a
1437
+ plan. Planning first; implementation follows per feature.
1438
+ - `plan/v3/features/NNN-*.md` - one parked plan per feature: the chosen pattern,
1439
+ the methodology, and the tests to write.
1440
+ - `plan/v3/fixtures/*_contract.json` + `plan/v3/CONTRACT-MAP.md` - the executable
1441
+ consolidations. The SAME bytes drive the contract runners in all four
1442
+ frameworks, mapped feature -> fixture -> ADR -> proven/owed.
1443
+ - `plan/v3/DECISIONS.md` + `plan/v3/decisions/` - the ADR log. Consult it before
1444
+ changing any cross-framework contract, and supersede an ADR explicitly rather
1445
+ than silently. `MASTER-SPEC.md` is the feature source-of-truth, but its NUMBERS
1446
+ are stale; the fixtures, `CONTRACT-MAP.md`, and
1447
+ `scripts/audit-contract-fixtures.py` carry current truth.
1448
+
1449
+ This repo's own `plan/` holds LANGUAGE-SPECIFIC task plans (`PARITY.md`,
1450
+ `SCAFFOLDING.md`, `TESTS.md`, `AI-CONTEXT.md`, and per-task files). The
1451
+ CROSS-framework audit and consolidation work is the central plan above.
1452
+
1453
+ **To advance a feature (the update loop):** pick the next feature from the parity
1454
+ backlog -> MEASURE all four side by side (assume no parity; no mocks, real
1455
+ services on the lab) -> DECIDE the best implementation, writing or superseding an
1456
+ ADR when the contract changes -> write the executable
1457
+ `fixtures/<feature>_contract.json` and fix the divergences in ALL FOUR with named
1458
+ positive and negative regressions -> flip owed->proven, run
1459
+ `scripts/audit-contract-fixtures.py`, and update the `CONTRACT-MAP.md` row from
1460
+ those counts -> verify yourself at HEAD on the lab -> ship `feature/release<ver>`
1461
+ -> `v3` -> tag. Pushing plan edits into tina4-documentation is a MERGE, never a
1462
+ rebase (the subtree graft + the PDF-sync bot).
1463
+
1429
1464
  ## First Principle: Documentation Matches Code Reality
1430
1465
 
1431
1466
  **This rule overrides everything else in this file.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tina4-nodejs",
3
- "version": "3.13.97",
3
+ "version": "3.13.99",
4
4
  "type": "module",
5
5
  "description": "Tina4 for Node.js/TypeScript - 54 built-in features, zero dependencies",
6
6
  "keywords": [
@@ -99,7 +99,6 @@
99
99
  "@types/pg": "^8.20.0",
100
100
  "esbuild": "^0.24.0",
101
101
  "mongodb": "^6.0.0",
102
- "node-firebird": "^2.14.3",
103
102
  "tsx": "^4.19.0",
104
103
  "typescript": "^5.7.0",
105
104
  "vitest": "^4.1.9"