tina4-nodejs 3.13.91 → 3.13.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/CLAUDE.md +16 -3
  2. package/README.md +1 -1
  3. package/package.json +12 -9
  4. package/packages/cli/dist/bin.js +1312 -987
  5. package/packages/core/dist/index.js +1312 -987
  6. package/packages/core/src/auth.ts +141 -21
  7. package/packages/core/src/devMailbox.ts +20 -44
  8. package/packages/core/src/index.ts +2 -2
  9. package/packages/core/src/messenger.ts +72 -0
  10. package/packages/core/src/queueBackends/kafkaBackend.ts +108 -12
  11. package/packages/core/src/sessionHandlers/childError.ts +72 -0
  12. package/packages/core/src/sessionHandlers/mongoClient.ts +9 -3
  13. package/packages/core/src/sessionHandlers/redisHandler.ts +18 -5
  14. package/packages/core/src/sessionHandlers/respClient.ts +5 -1
  15. package/packages/frond/dist/index.js +74 -31
  16. package/packages/frond/src/engine.ts +99 -33
  17. package/packages/orm/dist/index.js +3102 -2777
  18. package/packages/orm/src/adapters/sqlite.ts +4 -1
  19. package/packages/orm/src/database.ts +108 -8
  20. package/types/cli/src/bin.d.ts +92 -0
  21. package/types/cli/src/commands/build.d.ts +2 -0
  22. package/types/cli/src/commands/generate.d.ts +47 -0
  23. package/types/cli/src/commands/init.d.ts +1 -0
  24. package/types/cli/src/commands/metrics.d.ts +6 -0
  25. package/types/cli/src/commands/migrate.d.ts +1 -0
  26. package/types/cli/src/commands/migrateCreate.d.ts +1 -0
  27. package/types/cli/src/commands/migrateRollback.d.ts +1 -0
  28. package/types/cli/src/commands/migrateStatus.d.ts +1 -0
  29. package/types/cli/src/commands/queue.d.ts +20 -0
  30. package/types/cli/src/commands/routes.d.ts +1 -0
  31. package/types/cli/src/commands/seed.d.ts +1 -0
  32. package/types/cli/src/commands/serve.d.ts +6 -0
  33. package/types/cli/src/commands/test.d.ts +1 -0
  34. package/types/core/src/ai.d.ts +64 -0
  35. package/types/core/src/api.d.ts +262 -0
  36. package/types/core/src/auth.d.ts +154 -0
  37. package/types/core/src/authGate.d.ts +20 -0
  38. package/types/core/src/background.d.ts +34 -0
  39. package/types/core/src/cache.d.ts +160 -0
  40. package/types/core/src/constants.d.ts +38 -0
  41. package/types/core/src/container.d.ts +44 -0
  42. package/types/core/src/context/chunker.d.ts +31 -0
  43. package/types/core/src/context/index.d.ts +93 -0
  44. package/types/core/src/devAdmin.d.ts +179 -0
  45. package/types/core/src/devMailbox.d.ts +54 -0
  46. package/types/core/src/docs.d.ts +141 -0
  47. package/types/core/src/docsAutoDiscovery.d.ts +6 -0
  48. package/types/core/src/dotenv.d.ts +65 -0
  49. package/types/core/src/env.d.ts +28 -0
  50. package/types/core/src/errorOverlay.d.ts +36 -0
  51. package/types/core/src/events.d.ts +75 -0
  52. package/types/core/src/fakeData.d.ts +55 -0
  53. package/types/core/src/feedback.d.ts +90 -0
  54. package/types/core/src/graphql.d.ts +207 -0
  55. package/types/core/src/health.d.ts +22 -0
  56. package/types/core/src/htmlElement.d.ts +75 -0
  57. package/types/core/src/i18n.d.ts +37 -0
  58. package/types/core/src/index.d.ts +93 -0
  59. package/types/core/src/job.d.ts +39 -0
  60. package/types/core/src/logger.d.ts +123 -0
  61. package/types/core/src/mcp.d.ts +248 -0
  62. package/types/core/src/messenger.d.ts +191 -0
  63. package/types/core/src/metrics.d.ts +77 -0
  64. package/types/core/src/middleware.d.ts +207 -0
  65. package/types/core/src/mqtt.d.ts +257 -0
  66. package/types/core/src/mqttMessage.d.ts +67 -0
  67. package/types/core/src/plan.d.ts +96 -0
  68. package/types/core/src/projectIndex.d.ts +56 -0
  69. package/types/core/src/queue.d.ts +219 -0
  70. package/types/core/src/queueBackends/kafkaBackend.d.ts +117 -0
  71. package/types/core/src/queueBackends/liteBackend.d.ts +119 -0
  72. package/types/core/src/queueBackends/mongoBackend.d.ts +97 -0
  73. package/types/core/src/queueBackends/rabbitmqBackend.d.ts +55 -0
  74. package/types/core/src/rateLimiter.d.ts +49 -0
  75. package/types/core/src/request.d.ts +25 -0
  76. package/types/core/src/response.d.ts +28 -0
  77. package/types/core/src/routeDiscovery.d.ts +12 -0
  78. package/types/core/src/router.d.ts +355 -0
  79. package/types/core/src/scss.d.ts +19 -0
  80. package/types/core/src/server.d.ts +131 -0
  81. package/types/core/src/service.d.ts +115 -0
  82. package/types/core/src/session.d.ts +256 -0
  83. package/types/core/src/sessionHandlers/childError.d.ts +34 -0
  84. package/types/core/src/sessionHandlers/databaseHandler.d.ts +42 -0
  85. package/types/core/src/sessionHandlers/mongoClient.d.ts +24 -0
  86. package/types/core/src/sessionHandlers/mongoHandler.d.ts +61 -0
  87. package/types/core/src/sessionHandlers/redisHandler.d.ts +60 -0
  88. package/types/core/src/sessionHandlers/respClient.d.ts +22 -0
  89. package/types/core/src/sessionHandlers/valkeyHandler.d.ts +65 -0
  90. package/types/core/src/static.d.ts +2 -0
  91. package/types/core/src/test.d.ts +94 -0
  92. package/types/core/src/testClient.d.ts +36 -0
  93. package/types/core/src/testing.d.ts +58 -0
  94. package/types/core/src/types.d.ts +219 -0
  95. package/types/core/src/validator.d.ts +52 -0
  96. package/types/core/src/websocket.d.ts +376 -0
  97. package/types/core/src/websocketBackplane.d.ts +166 -0
  98. package/types/core/src/websocketConnection.d.ts +54 -0
  99. package/types/core/src/wsdl.d.ts +101 -0
  100. package/types/frond/src/engine.d.ts +263 -0
  101. package/types/frond/src/index.d.ts +2 -0
  102. package/types/orm/src/adapters/firebird.d.ts +138 -0
  103. package/types/orm/src/adapters/mongodb.d.ts +81 -0
  104. package/types/orm/src/adapters/mssql.d.ts +70 -0
  105. package/types/orm/src/adapters/mysql.d.ts +66 -0
  106. package/types/orm/src/adapters/odbc.d.ts +97 -0
  107. package/types/orm/src/adapters/postgres.d.ts +85 -0
  108. package/types/orm/src/adapters/sqlite.d.ts +56 -0
  109. package/types/orm/src/autoCrud.d.ts +73 -0
  110. package/types/orm/src/baseModel.d.ts +391 -0
  111. package/types/orm/src/cachedDatabase.d.ts +177 -0
  112. package/types/orm/src/database.d.ts +609 -0
  113. package/types/orm/src/databaseResult.d.ts +85 -0
  114. package/types/orm/src/docstore.d.ts +182 -0
  115. package/types/orm/src/fakeData.d.ts +22 -0
  116. package/types/orm/src/index.d.ts +40 -0
  117. package/types/orm/src/migration.d.ts +275 -0
  118. package/types/orm/src/model.d.ts +7 -0
  119. package/types/orm/src/query.d.ts +14 -0
  120. package/types/orm/src/queryBuilder.d.ts +173 -0
  121. package/types/orm/src/realtime/index.d.ts +7 -0
  122. package/types/orm/src/realtime/models/attachment.d.ts +43 -0
  123. package/types/orm/src/realtime/models/channel.d.ts +32 -0
  124. package/types/orm/src/realtime/models/channelMember.d.ts +32 -0
  125. package/types/orm/src/realtime/models/message.d.ts +36 -0
  126. package/types/orm/src/realtime/models/workspace.d.ts +26 -0
  127. package/types/orm/src/realtime/realtime.d.ts +24 -0
  128. package/types/orm/src/realtime/storage.d.ts +61 -0
  129. package/types/orm/src/seeder.d.ts +118 -0
  130. package/types/orm/src/sqlTranslator.d.ts +134 -0
  131. package/types/orm/src/types.d.ts +138 -0
  132. package/types/orm/src/validation.d.ts +6 -0
  133. package/types/swagger/src/generator.d.ts +46 -0
  134. package/types/swagger/src/index.d.ts +2 -0
  135. package/types/swagger/src/ui.d.ts +11 -0
@@ -279,7 +279,10 @@ export class SQLiteAdapter implements DatabaseAdapter {
279
279
  name: string; type: string; notnull: number; dflt_value: unknown; pk: number;
280
280
  }>;
281
281
  return rows.map((r) => ({
282
- name: r.name, type: r.type, nullable: r.notnull === 0, default: r.dflt_value, primaryKey: r.pk === 1,
282
+ // PRAGMA table_info reports `pk` as the 1-BASED POSITION within the primary
283
+ // key, not a boolean: a composite key gives pk=1, pk=2, ... Testing `=== 1`
284
+ // reported only the first column of a composite key.
285
+ name: r.name, type: r.type, nullable: r.notnull === 0, default: r.dflt_value, primaryKey: Number(r.pk) > 0,
283
286
  }));
284
287
  }
285
288
 
@@ -484,6 +484,9 @@ export class Database {
484
484
  /** Factory for creating new adapters (used by pool) */
485
485
  private adapterFactory: (() => Promise<DatabaseAdapter>) | null = null;
486
486
 
487
+ /** table -> primary-key column name (or null), introspected once */
488
+ private _pkCache: Map<string, string[]> = new Map();
489
+
487
490
  /**
488
491
  * Whether a standalone write auto-commits. ON by default — a write made
489
492
  * outside an explicit transaction commits on its own connection before
@@ -787,28 +790,125 @@ export class Database {
787
790
  return result;
788
791
  }
789
792
 
790
- /** Update rows in a table matching filter. */
793
+ /**
794
+ * The table's primary-key column, introspected once and cached.
795
+ *
796
+ * Uses the cross-engine getColumns() contract (v3.13.14, #48), which reports
797
+ * primaryKey per column on every adapter. Resolves to null when the table has
798
+ * no primary key or cannot be introspected.
799
+ */
800
+ async primaryKey(table: string): Promise<string[]> {
801
+ if (!this._pkCache.has(table)) {
802
+ let pk: string[] = [];
803
+ try {
804
+ const columns = await this.getColumns(table);
805
+ pk = columns.filter((c) => c.primaryKey).map((c) => c.name);
806
+ } catch {
807
+ pk = [];
808
+ }
809
+ this._pkCache.set(table, pk);
810
+ }
811
+ return this._pkCache.get(table) ?? [];
812
+ }
813
+
814
+ /**
815
+ * A failed write must be loud.
816
+ *
817
+ * The adapters catch a SQL error and return { success: false, affectedRows: 0 },
818
+ * so a filterless update produced invalid SQL ("... WHERE ") and reported
819
+ * nothing rather than raising. A caller who does not inspect the result
820
+ * believes the write landed (audit feature 4, P1).
821
+ */
822
+ private static assertWrote(result: DatabaseWriteResult, verb: string, table: string): DatabaseWriteResult {
823
+ if (result && (result as any).success === false) {
824
+ throw new Error(
825
+ `${verb} failed on ${table}: ${(result as any).error ?? "unknown error"}`,
826
+ );
827
+ }
828
+ return result;
829
+ }
830
+
831
+ /**
832
+ * Update rows. A write with no filter is an error, not a full-table write.
833
+ *
834
+ * With no explicit filter the primary key is taken out of `data` and used as
835
+ * the WHERE clause. With neither a filter nor a primary key in `data` this
836
+ * throws rather than silently changing nothing (audit feature 4, P1).
837
+ */
791
838
  async update(table: string, data: Record<string, unknown>, filter?: Record<string, unknown>, params?: unknown[]): Promise<DatabaseWriteResult> {
839
+ let effectiveFilter = filter ?? {};
840
+ let effectiveData = data;
841
+
842
+ if (Object.keys(effectiveFilter).length === 0) {
843
+ const pkColumns = await this.primaryKey(table);
844
+ const missing = pkColumns.filter((c) => !(c in data));
845
+ if (pkColumns.length === 0 || missing.length > 0) {
846
+ throw new Error(
847
+ `update requires a filter or the complete primary key in the data; pass ` +
848
+ `filter explicitly to update multiple rows (table=${table}, ` +
849
+ `primary key=[${pkColumns.join(", ")}], missing from data=[${missing.join(", ")}]). ` +
850
+ `To empty a table use truncate(${table}).`,
851
+ );
852
+ }
853
+ // EVERY key column goes into the WHERE. A composite key built from only its
854
+ // first column would match every row sharing that value - the data-loss bug
855
+ // this method exists to prevent, reintroduced.
856
+ effectiveData = { ...data };
857
+ const keyed: Record<string, unknown> = {};
858
+ for (const col of pkColumns) {
859
+ keyed[col] = effectiveData[col];
860
+ delete effectiveData[col];
861
+ }
862
+ if (Object.keys(effectiveData).length === 0) {
863
+ throw new Error(
864
+ `update was given only the primary key [${pkColumns.join(", ")}] and no ` +
865
+ `columns to set (table=${table})`,
866
+ );
867
+ }
868
+ effectiveFilter = keyed;
869
+ }
870
+
792
871
  const adapter = this.getNextAdapter();
793
872
  const result = (adapter as any).updateAsync
794
- ? await (adapter as any).updateAsync(table, data, filter ?? {}, params)
795
- : adapter.update(table, data, filter ?? {}, params);
873
+ ? await (adapter as any).updateAsync(table, effectiveData, effectiveFilter, params)
874
+ : adapter.update(table, effectiveData, effectiveFilter, params);
796
875
  if (this.autoCommit && !this.inExplicitTransaction()) {
797
876
  try { await adapterCommit(adapter); } catch { /* no active transaction */ }
798
877
  }
799
- return result;
878
+ return Database.assertWrote(result, "update", table);
800
879
  }
801
880
 
802
- /** Delete rows from a table matching filter. */
881
+ /** Delete rows. A filterless delete throws; use truncate() to empty a table. */
803
882
  async delete(table: string, filter?: Record<string, unknown>, params?: unknown[]): Promise<DatabaseWriteResult> {
883
+ const effectiveFilter = filter ?? {};
884
+ if (!Array.isArray(effectiveFilter) && typeof effectiveFilter !== "string"
885
+ && Object.keys(effectiveFilter).length === 0) {
886
+ throw new Error(
887
+ `delete requires a filter (table=${table}). To remove every row use truncate(${table}).`,
888
+ );
889
+ }
890
+
804
891
  const adapter = this.getNextAdapter();
805
892
  const result = (adapter as any).deleteAsync
806
- ? await (adapter as any).deleteAsync(table, filter ?? {}, params)
807
- : adapter.delete(table, filter ?? {}, params);
893
+ ? await (adapter as any).deleteAsync(table, effectiveFilter, params)
894
+ : adapter.delete(table, effectiveFilter, params);
808
895
  if (this.autoCommit && !this.inExplicitTransaction()) {
809
896
  try { await adapterCommit(adapter); } catch { /* no active transaction */ }
810
897
  }
811
- return result;
898
+ return Database.assertWrote(result, "delete", table);
899
+ }
900
+
901
+ /** Remove every row. The explicit spelling of a whole-table delete. */
902
+ async truncate(table: string): Promise<DatabaseWriteResult> {
903
+ const adapter = this.getNextAdapter();
904
+ // The adapters' delete() already accepts a raw string WHERE clause.
905
+ const result = (adapter as any).deleteAsync
906
+ ? await (adapter as any).deleteAsync(table, "1 = 1", [])
907
+ : adapter.delete(table, "1 = 1" as any, []);
908
+ if (this.autoCommit && !this.inExplicitTransaction()) {
909
+ try { await adapterCommit(adapter); } catch { /* no active transaction */ }
910
+ }
911
+ return Database.assertWrote(result, "truncate", table);
812
912
  }
813
913
 
814
914
  /** Close all database connections (pool or single). */
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Kill any process listening on `port`. Returns true if anything was killed.
3
+ *
4
+ * Every PID is validated first. `parseInt` on a non-numeric lsof field yields
5
+ * 1, and SIGTERM to PID 1 is the container's own init -- which is exactly how a
6
+ * production container logged "Killed existing process on port 7148 (PID: 1
7
+ * ...)" and then exited 143, killing itself on startup.
8
+ */
9
+ /**
10
+ * The PIDs from `lsof -ti` output that are safe to signal.
11
+ *
12
+ * Pure so the safety rule can be tested directly. An unvalidated parse is a
13
+ * footgun with real teeth: where lsof prints a different shape than -ti
14
+ * implies, a non-numeric field becomes 0, and signalling PID 0 hits EVERY
15
+ * process in the caller's own process group -- the server kills itself. That
16
+ * is what produced "Killed existing process on port 7148 (PID: 1 ...)" in a
17
+ * real image, where the container then exited 143.
18
+ *
19
+ * Accepts only all-digit tokens; never PID 0 (our group), PID 1 (init),
20
+ * ourselves, or our own process group.
21
+ */
22
+ export declare function selectablePids(lsofOutput: string, me: number, myGroup?: number): number[];
23
+ export interface CommandManifestEntry {
24
+ name: string;
25
+ summary: string;
26
+ args?: string[];
27
+ subcommands?: string[];
28
+ /** True when the tina4 client implements this command, not the framework. */
29
+ delegated?: boolean;
30
+ }
31
+ export interface CommandManifest {
32
+ framework: string;
33
+ version: string;
34
+ commands: CommandManifestEntry[];
35
+ }
36
+ /**
37
+ * Build the machine-readable manifest of the CLI's command surface.
38
+ *
39
+ * Pure data: reads the module-level COMMANDS and DELEGATED registries plus the
40
+ * framework version — no bootstrap, no database, no migrations, no app imports.
41
+ * This is exactly what `commands --json` serialises and what the tina4 Rust
42
+ * client consumes to discover which commands this framework supports.
43
+ *
44
+ * Commands handed to the `tina4` client carry `delegated: true`, so the manifest
45
+ * describes the WHOLE surface the CLI accepts while still saying who implements
46
+ * each one. The client needs no change: its help renderer already drops manifest
47
+ * names that clash with its own natives.
48
+ *
49
+ * Shape (identical keys to the Python master):
50
+ * { framework: "nodejs", version: "<x.y.z>",
51
+ * commands: [{ name, summary, args?, subcommands?, delegated? }, ...] }
52
+ */
53
+ export declare function buildCommandManifest(): CommandManifest;
54
+ /**
55
+ * Emit the CLI's own command surface — the self-describing manifest.
56
+ *
57
+ * tina4nodejs commands human-readable list
58
+ * tina4nodejs commands --json machine-readable manifest (for the tina4 CLI)
59
+ *
60
+ * CHEAP + side-effect-free by contract: it only prints the static COMMANDS
61
+ * registry plus the framework version. It MUST NOT bootstrap the framework,
62
+ * open a database, run migrations, or import app modules — the Rust client
63
+ * calls this on `tina4 --help`, in any directory, so it must be instant and
64
+ * safe to run anywhere.
65
+ */
66
+ export declare function runCommands(args?: string[]): void;
67
+ export interface CommandSpec {
68
+ handler: (cmdArgs: string[]) => void | Promise<void>;
69
+ summary: string;
70
+ usage?: string;
71
+ args?: string[];
72
+ subcommands?: string[];
73
+ }
74
+ export declare const COMMANDS: Record<string, CommandSpec>;
75
+ export interface DelegatedSpec {
76
+ summary: string;
77
+ usage?: string;
78
+ args?: string[];
79
+ }
80
+ export declare const DELEGATED: Record<string, DelegatedSpec>;
81
+ export declare const CLIENT_BINARY = "tina4";
82
+ export declare const DELEGATION_GUARD_ENV = "TINA4_CLI_DELEGATED";
83
+ export declare const EXIT_CLIENT_UNAVAILABLE = 127;
84
+ export declare const EXIT_UNKNOWN_COMMAND = 1;
85
+ /**
86
+ * Run `tina4 <command> <args...>`, returning the client's exit code.
87
+ *
88
+ * Returns EXIT_CLIENT_UNAVAILABLE (127) with an actionable message when the
89
+ * client is not on PATH, or when the re-entry guard shows the resolved `tina4`
90
+ * came back to a framework CLI (a delegation loop).
91
+ */
92
+ export declare function delegateToClient(command: string, args: string[]): number;
@@ -0,0 +1,2 @@
1
+ /** Build the deployable Docker image. Fails loud (exit 1+) on missing prerequisites. */
2
+ export declare function buildImage(args: string[]): void;
@@ -0,0 +1,47 @@
1
+ export declare function toSnake(name: string): string;
2
+ export declare function toTableName(name: string): string;
3
+ /** slug-of-anything → PascalCase (order-emails → OrderEmails). */
4
+ export declare function toPascal(name: string): string;
5
+ export declare function parseFields(fieldsStr: string): Array<[string, string]>;
6
+ export declare const DEFAULT_FIELDS: ReadonlyArray<[string, string]>;
7
+ /** Parsed --fields, or the default single `name` column when none given. */
8
+ export declare function fieldsOrDefault(fieldsStr: string): Array<[string, string]>;
9
+ export declare function parseCliArgs(args: string[]): {
10
+ flags: Record<string, string | boolean>;
11
+ positional: string[];
12
+ };
13
+ /**
14
+ * Parse a `--every` duration ("5m", "30s", "2h", "1d", or bare seconds) → seconds.
15
+ * Falls back to 60s on an empty/unparseable value so a scaffold always has a
16
+ * valid ServiceRunner interval.
17
+ */
18
+ export declare function parseEvery(every: string | boolean | undefined): number;
19
+ /**
20
+ * The canonical AI-FILL placeholder for a LOGIC-shaped stub — a tight, grounded
21
+ * fill-spec (not a vague `// TODO`) so a coding agent (or dev) completes it
22
+ * correctly. `throw new Error(...)` makes an unfilled scaffold fail LOUD; the
23
+ * greppable `AI-FILL` banner lets a human/agent jump to every gap. `use` names
24
+ * only REAL tina4-nodejs symbols (verified in source).
25
+ */
26
+ export declare function aiFill(fn: string, spec: {
27
+ intent: string;
28
+ given?: string;
29
+ use: string;
30
+ ret?: string;
31
+ ground: string;
32
+ raise: string;
33
+ }, indent?: string): string;
34
+ /**
35
+ * The lighter EXTEND marker for CRUD-shaped WORKING code — no throw (the
36
+ * boilerplate IS the feature); just a greppable hint at the natural extension
37
+ * point (custom validation / business rules / authorization).
38
+ */
39
+ export declare function extend(note: string, hint?: string, indent?: string): string;
40
+ export interface GeneratorSpec {
41
+ handler: (name: string, flags: Record<string, string | boolean>) => void;
42
+ /** Arg/flag hint shown in `tina4nodejs help` (human only). */
43
+ usage: string;
44
+ summary: string;
45
+ }
46
+ export declare const GENERATORS: Record<string, GeneratorSpec>;
47
+ export declare function generate(what: string, name: string, extraArgs?: string[]): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function initProject(name: string): Promise<void>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Run the metrics report. Returns the process exit code; does NOT call
3
+ * process.exit (the bin wrapper does). 0 = ok / below threshold, 1 = gated
4
+ * failure, 2 = bad arguments / analysis error.
5
+ */
6
+ export declare function runMetrics(args?: string[]): number;
@@ -0,0 +1 @@
1
+ export declare function runMigrations(migrationDir?: string): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function createMigration(description?: string): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function migrateRollback(migrationDir?: string): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function migrateStatus(migrationDir?: string): Promise<void>;
@@ -0,0 +1,20 @@
1
+ /** A per-job handler declared by a consumer module (receives the job payload). */
2
+ export type QueueHandler = (payload: unknown) => unknown | Promise<unknown>;
3
+ /**
4
+ * Return the per-job handler that a consumer module declares for `topic`.
5
+ *
6
+ * A consumer module (e.g. the one `generate queue <topic>` scaffolds) exposes a
7
+ * default-export config; when its `topic` matches, `queue work` drives the
8
+ * consumer through that config's per-job `handle` callable — so the worker owns
9
+ * the poll loop (honouring --poll and the bounded --once drain) instead of the
10
+ * consumer's own endless loop. Returns the callable, or null when no consumer in
11
+ * `servicesDir` targets this topic. Mirrors Python's _resolve_queue_handler.
12
+ */
13
+ export declare function resolveQueueHandler(servicesDir: string, topic: string): Promise<QueueHandler | null>;
14
+ /** Subcommand names, in order — surfaced in `commands --json` for the tina4 client. */
15
+ export declare const QUEUE_SUBCOMMAND_NAMES: string[];
16
+ /**
17
+ * Top-level queue command: run workers and manage jobs. Dispatches to the
18
+ * subcommand handlers above; unknown / missing subcommands fail loud (exit 1).
19
+ */
20
+ export declare function queueCommand(args?: string[]): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function listRoutes(): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function runSeeds(seedPath?: string): Promise<void>;
@@ -0,0 +1,6 @@
1
+ export interface ServeOptions {
2
+ port?: number;
3
+ noBrowser?: boolean;
4
+ noReload?: boolean;
5
+ }
6
+ export declare function serveProject(options: ServeOptions): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function runTests(testPath?: string): Promise<void>;
@@ -0,0 +1,64 @@
1
+ export interface AiTool {
2
+ name: string;
3
+ description: string;
4
+ contextFile: string;
5
+ configDir: string | null;
6
+ }
7
+ export declare const AI_TOOLS: AiTool[];
8
+ export declare const DEV_SKILL = "tina4-developer-nodejs";
9
+ /**
10
+ * Install the Tina4 SKILL.md skills into the project AND the global
11
+ * ~/.claude/skills, fetched from the release ref matching this framework
12
+ * version. Returns the skills that were fully installed. Network-dependent —
13
+ * on a fetch failure the skill is skipped, never fatal.
14
+ */
15
+ export declare function installSkills(root?: string, targets?: string[]): string[];
16
+ /**
17
+ * Check if a tool's context file already exists.
18
+ */
19
+ export declare function isInstalled(root: string, tool: AiTool): boolean;
20
+ /**
21
+ * Print the numbered menu and read user input via readline.
22
+ * Returns a promise that resolves to the user's selection string.
23
+ */
24
+ export declare function showMenu(root?: string): Promise<string>;
25
+ /**
26
+ * Install context files for the selected tools.
27
+ *
28
+ * selection: comma-separated numbers like "1,2,3" or "all"
29
+ * Returns list of created/updated file paths.
30
+ */
31
+ export declare function installSelected(root: string, selection: string): string[];
32
+ /**
33
+ * Install context for all AI tools (non-interactive).
34
+ */
35
+ export declare function installAll(root?: string): string[];
36
+ /** Return [start, end] markers for a context file. */
37
+ export declare function markersFor(contextFile: string): [string, string];
38
+ /** Return the marker-bracketed Tina4 skill registration block. */
39
+ export declare function skillBlock(contextFile: string): string;
40
+ /** True iff both start and end markers appear in order. */
41
+ export declare function hasMarkers(existing: string, start: string, end: string): boolean;
42
+ /** Replace the bracketed block in `existing` with `block`. */
43
+ export declare function replaceMarkerBlock(existing: string, block: string, start: string, end: string): string;
44
+ /**
45
+ * True if the file starts with a header the pre-v3.13.9 installer
46
+ * wrote. Used to migrate one-time off the old clobber-style install.
47
+ */
48
+ export declare function looksLikeOldFrameworkInstall(existing: string): boolean;
49
+ /**
50
+ * Write the context file non-destructively. Returns a human-readable
51
+ * action verb for the caller's log line.
52
+ *
53
+ * Four branches:
54
+ * 1. Doesn't exist \u2192 write framework guide + skill block
55
+ * 2. Has markers \u2192 refresh just the skill block (idempotent)
56
+ * 3. Old header \u2192 migrate: replace old dump with new guide + block
57
+ * 4. User content \u2192 append the skill block, preserve everything else
58
+ */
59
+ export declare function writeOrMerge(contextPath: string, contextFile: string, frameworkGuide: string): string;
60
+ /**
61
+ * Generate the Tina4 context document for a specific AI tool.
62
+ */
63
+ export declare function generateContext(toolName?: string): string;
64
+ export { AiTool as AiToolType };