bonescript-compiler 0.2.1 → 0.4.0

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 (167) hide show
  1. package/LICENSE +21 -21
  2. package/dist/algorithm_catalog.js +166 -166
  3. package/dist/cli.d.ts +2 -1
  4. package/dist/cli.js +75 -543
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/check.d.ts +5 -0
  7. package/dist/commands/check.js +34 -0
  8. package/dist/commands/check.js.map +1 -0
  9. package/dist/commands/compile.d.ts +5 -0
  10. package/dist/commands/compile.js +215 -0
  11. package/dist/commands/compile.js.map +1 -0
  12. package/dist/commands/debug.d.ts +5 -0
  13. package/dist/commands/debug.js +59 -0
  14. package/dist/commands/debug.js.map +1 -0
  15. package/dist/commands/diff.d.ts +5 -0
  16. package/dist/commands/diff.js +125 -0
  17. package/dist/commands/diff.js.map +1 -0
  18. package/dist/commands/fmt.d.ts +5 -0
  19. package/dist/commands/fmt.js +49 -0
  20. package/dist/commands/fmt.js.map +1 -0
  21. package/dist/commands/init.d.ts +5 -0
  22. package/dist/commands/init.js +96 -0
  23. package/dist/commands/init.js.map +1 -0
  24. package/dist/commands/ir.d.ts +5 -0
  25. package/dist/commands/ir.js +27 -0
  26. package/dist/commands/ir.js.map +1 -0
  27. package/dist/commands/lex.d.ts +5 -0
  28. package/dist/commands/lex.js +21 -0
  29. package/dist/commands/lex.js.map +1 -0
  30. package/dist/commands/parse.d.ts +5 -0
  31. package/dist/commands/parse.js +30 -0
  32. package/dist/commands/parse.js.map +1 -0
  33. package/dist/commands/test.d.ts +5 -0
  34. package/dist/commands/test.js +61 -0
  35. package/dist/commands/test.js.map +1 -0
  36. package/dist/commands/verify_determinism.d.ts +5 -0
  37. package/dist/commands/verify_determinism.js +64 -0
  38. package/dist/commands/verify_determinism.js.map +1 -0
  39. package/dist/commands/watch.d.ts +5 -0
  40. package/dist/commands/watch.js +50 -0
  41. package/dist/commands/watch.js.map +1 -0
  42. package/dist/emit_auth.d.ts +6 -0
  43. package/dist/emit_auth.js +69 -0
  44. package/dist/emit_auth.js.map +1 -0
  45. package/dist/emit_capability.d.ts +13 -0
  46. package/dist/emit_capability.js +292 -128
  47. package/dist/emit_capability.js.map +1 -1
  48. package/dist/emit_composition.js +37 -3
  49. package/dist/emit_composition.js.map +1 -1
  50. package/dist/emit_database.d.ts +7 -0
  51. package/dist/emit_database.js +74 -0
  52. package/dist/emit_database.js.map +1 -0
  53. package/dist/emit_deploy.js +162 -162
  54. package/dist/emit_events.d.ts +1 -0
  55. package/dist/emit_events.js +342 -275
  56. package/dist/emit_events.js.map +1 -1
  57. package/dist/emit_full.js +135 -95
  58. package/dist/emit_full.js.map +1 -1
  59. package/dist/emit_index.d.ts +6 -0
  60. package/dist/emit_index.js +157 -0
  61. package/dist/emit_index.js.map +1 -0
  62. package/dist/emit_maintenance.js +249 -249
  63. package/dist/emit_models.d.ts +12 -0
  64. package/dist/emit_models.js +171 -0
  65. package/dist/emit_models.js.map +1 -0
  66. package/dist/emit_openapi.d.ts +9 -0
  67. package/dist/emit_openapi.js +308 -0
  68. package/dist/emit_openapi.js.map +1 -0
  69. package/dist/emit_package.d.ts +7 -0
  70. package/dist/emit_package.js +70 -0
  71. package/dist/emit_package.js.map +1 -0
  72. package/dist/emit_router.d.ts +12 -0
  73. package/dist/emit_router.js +390 -0
  74. package/dist/emit_router.js.map +1 -0
  75. package/dist/emit_runtime.d.ts +17 -11
  76. package/dist/emit_runtime.js +29 -686
  77. package/dist/emit_runtime.js.map +1 -1
  78. package/dist/emit_sourcemap.js +66 -66
  79. package/dist/emit_tests.js +37 -0
  80. package/dist/emit_tests.js.map +1 -1
  81. package/dist/emitter.js +34 -5
  82. package/dist/emitter.js.map +1 -1
  83. package/dist/extension_manager.d.ts +2 -2
  84. package/dist/extension_manager.js +6 -3
  85. package/dist/extension_manager.js.map +1 -1
  86. package/dist/lowering.d.ts +5 -14
  87. package/dist/lowering.js +47 -417
  88. package/dist/lowering.js.map +1 -1
  89. package/dist/lowering_channels.d.ts +11 -0
  90. package/dist/lowering_channels.js +102 -0
  91. package/dist/lowering_channels.js.map +1 -0
  92. package/dist/lowering_entities.d.ts +11 -0
  93. package/dist/lowering_entities.js +222 -0
  94. package/dist/lowering_entities.js.map +1 -0
  95. package/dist/lowering_helpers.d.ts +13 -0
  96. package/dist/lowering_helpers.js +76 -0
  97. package/dist/lowering_helpers.js.map +1 -0
  98. package/dist/module_loader.d.ts +2 -2
  99. package/dist/module_loader.js +20 -23
  100. package/dist/module_loader.js.map +1 -1
  101. package/dist/scaffold.d.ts +2 -2
  102. package/dist/scaffold.js +316 -319
  103. package/dist/scaffold.js.map +1 -1
  104. package/dist/typechecker.js +32 -13
  105. package/dist/typechecker.js.map +1 -1
  106. package/dist/verifier.d.ts +5 -0
  107. package/dist/verifier.js +140 -2
  108. package/dist/verifier.js.map +1 -1
  109. package/package.json +62 -52
  110. package/src/algorithm_catalog.ts +345 -345
  111. package/src/ast.ts +334 -334
  112. package/src/cli.ts +98 -624
  113. package/src/commands/check.ts +33 -0
  114. package/src/commands/compile.ts +191 -0
  115. package/src/commands/debug.ts +33 -0
  116. package/src/commands/diff.ts +108 -0
  117. package/src/commands/fmt.ts +22 -0
  118. package/src/commands/init.ts +72 -0
  119. package/src/commands/ir.ts +23 -0
  120. package/src/commands/lex.ts +17 -0
  121. package/src/commands/parse.ts +24 -0
  122. package/src/commands/test.ts +36 -0
  123. package/src/commands/verify_determinism.ts +66 -0
  124. package/src/commands/watch.ts +25 -0
  125. package/src/emit_auth.ts +67 -0
  126. package/src/emit_batch.ts +140 -140
  127. package/src/emit_capability.ts +617 -436
  128. package/src/emit_composition.ts +229 -196
  129. package/src/emit_database.ts +75 -0
  130. package/src/emit_deploy.ts +190 -190
  131. package/src/emit_events.ts +377 -307
  132. package/src/emit_extras.ts +240 -240
  133. package/src/emit_full.ts +351 -309
  134. package/src/emit_index.ts +161 -0
  135. package/src/emit_maintenance.ts +459 -459
  136. package/src/emit_models.ts +176 -0
  137. package/src/emit_openapi.ts +318 -0
  138. package/src/emit_package.ts +69 -0
  139. package/src/emit_router.ts +409 -0
  140. package/src/emit_runtime.ts +17 -728
  141. package/src/emit_sourcemap.ts +140 -140
  142. package/src/emit_tests.ts +246 -205
  143. package/src/emit_websocket.ts +229 -229
  144. package/src/emitter.ts +31 -5
  145. package/src/extension_manager.ts +189 -187
  146. package/src/formatter.ts +297 -297
  147. package/src/index.ts +88 -88
  148. package/src/ir.ts +215 -215
  149. package/src/lexer.ts +630 -630
  150. package/src/lowering.ts +142 -556
  151. package/src/lowering_channels.ts +107 -0
  152. package/src/lowering_entities.ts +248 -0
  153. package/src/lowering_helpers.ts +75 -0
  154. package/src/module_loader.ts +112 -114
  155. package/src/optimizer.ts +196 -196
  156. package/src/parse_decls.ts +409 -409
  157. package/src/parse_decls2.ts +244 -244
  158. package/src/parse_expr.ts +197 -197
  159. package/src/parse_types.ts +54 -54
  160. package/src/parser.ts +1 -1
  161. package/src/parser_base.ts +57 -57
  162. package/src/parser_recovery.ts +153 -153
  163. package/src/scaffold.ts +372 -375
  164. package/src/solver.ts +330 -330
  165. package/src/typechecker.ts +30 -15
  166. package/src/types.ts +122 -122
  167. package/src/verifier.ts +151 -4
@@ -0,0 +1,67 @@
1
+ /**
2
+ * BoneScript Auth Emitter
3
+ * Generates auth.ts — JWT middleware with production safety checks.
4
+ */
5
+
6
+ import * as IR from "./ir";
7
+
8
+ export function emitAuthMiddleware(_system: IR.IRSystem): string {
9
+ return `// Generated by BoneScript compiler. DO NOT EDIT.
10
+ import { Request, Response, NextFunction } from "express";
11
+ import jwt from "jsonwebtoken";
12
+
13
+ // JWT_SECRET must be set in production. The server will refuse to start without it
14
+ // when NODE_ENV is "production" to prevent accidental use of a weak fallback.
15
+ const JWT_SECRET = (() => {
16
+ const secret = process.env.JWT_SECRET;
17
+ if (!secret) {
18
+ if (process.env.NODE_ENV === "production") {
19
+ console.error("[FATAL] JWT_SECRET environment variable is not set. Refusing to start in production.");
20
+ process.exit(1);
21
+ }
22
+ console.warn("[WARN] JWT_SECRET is not set. Using insecure default — do not use in production.");
23
+ return "bonescript-dev-secret-do-not-use-in-production";
24
+ }
25
+ if (secret.length < 32) {
26
+ console.warn("[WARN] JWT_SECRET is shorter than 32 characters. Use a longer secret in production.");
27
+ }
28
+ return secret;
29
+ })();
30
+
31
+ export interface AuthContext {
32
+ authenticated: boolean;
33
+ actor_id: string | null;
34
+ trace_id: string;
35
+ }
36
+
37
+ export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
38
+ const header = req.headers.authorization;
39
+ if (!header || !header.startsWith("Bearer ")) {
40
+ (req as any).auth = { authenticated: false, actor_id: null, trace_id: req.headers["x-trace-id"] as string || "" };
41
+ next();
42
+ return;
43
+ }
44
+ try {
45
+ const token = header.slice(7);
46
+ const decoded = jwt.verify(token, JWT_SECRET) as { sub: string };
47
+ (req as any).auth = {
48
+ authenticated: true,
49
+ actor_id: decoded.sub,
50
+ trace_id: req.headers["x-trace-id"] as string || "",
51
+ };
52
+ } catch {
53
+ (req as any).auth = { authenticated: false, actor_id: null, trace_id: req.headers["x-trace-id"] as string || "" };
54
+ }
55
+ next();
56
+ }
57
+
58
+ export function requireAuth(req: Request, res: Response, next: NextFunction): void {
59
+ const auth: AuthContext = (req as any).auth;
60
+ if (!auth || !auth.authenticated) {
61
+ res.status(401).json({ error: { code: "UNAUTHORIZED", message: "Authentication required" } });
62
+ return;
63
+ }
64
+ next();
65
+ }
66
+ `;
67
+ }
package/src/emit_batch.ts CHANGED
@@ -1,140 +1,140 @@
1
- /**
2
- * BoneScript Batch Executor Emitter
3
- * Generates a batch processing module for sync: batch capabilities.
4
- * Batch capabilities are queued and processed in configurable intervals.
5
- */
6
-
7
- import * as IR from "./ir";
8
-
9
- export function emitBatchExecutor(system: IR.IRSystem): string {
10
- // Collect all batch capabilities
11
- const batchMethods: { modName: string; method: IR.IRMethod }[] = [];
12
- for (const mod of system.modules) {
13
- for (const iface of mod.interfaces) {
14
- for (const method of iface.methods) {
15
- if (method.sync === "batch") {
16
- batchMethods.push({ modName: mod.name, method });
17
- }
18
- }
19
- }
20
- }
21
-
22
- if (batchMethods.length === 0) return "";
23
-
24
- const lines: string[] = [];
25
- lines.push(`// Generated by BoneScript compiler. DO NOT EDIT.`);
26
- lines.push(`// Batch executor for sync: batch capabilities.`);
27
- lines.push(`// Set BATCH_INTERVAL_MS to control flush frequency (default: 5000ms).`);
28
- lines.push(``);
29
- lines.push(`import { query } from "./db";`);
30
- lines.push(`import { logger } from "./logger";`);
31
- lines.push(`import { counter, histogram } from "./metrics";`);
32
- lines.push(``);
33
-
34
- // Batch queue type
35
- lines.push(`interface BatchItem {`);
36
- lines.push(` capability: string;`);
37
- lines.push(` payload: Record<string, unknown>;`);
38
- lines.push(` enqueuedAt: Date;`);
39
- lines.push(` resolve: (result: any) => void;`);
40
- lines.push(` reject: (err: Error) => void;`);
41
- lines.push(`}`);
42
- lines.push(``);
43
-
44
- // Per-capability queues
45
- lines.push(`const queues: Map<string, BatchItem[]> = new Map([`);
46
- for (const { modName, method } of batchMethods) {
47
- lines.push(` ["${modName}.${method.name}", []],`);
48
- }
49
- lines.push(`]);`);
50
- lines.push(``);
51
-
52
- // Enqueue function
53
- lines.push(`export function enqueueBatch(capability: string, payload: Record<string, unknown>): Promise<any> {`);
54
- lines.push(` return new Promise((resolve, reject) => {`);
55
- lines.push(` const queue = queues.get(capability);`);
56
- lines.push(` if (!queue) {`);
57
- lines.push(` reject(new Error(\`Unknown batch capability: \${capability}\`));`);
58
- lines.push(` return;`);
59
- lines.push(` }`);
60
- lines.push(` queue.push({ capability, payload, enqueuedAt: new Date(), resolve, reject });`);
61
- lines.push(` counter("batch.enqueued", { capability });`);
62
- lines.push(` });`);
63
- lines.push(`}`);
64
- lines.push(``);
65
-
66
- // Flush function — processes all queued items
67
- lines.push(`async function flushBatch(capability: string, items: BatchItem[]): Promise<void> {`);
68
- lines.push(` if (items.length === 0) return;`);
69
- lines.push(` const start = Date.now();`);
70
- lines.push(` logger.info("batch_flush_started", { event: capability, metadata: { count: items.length } });`);
71
- lines.push(``);
72
- lines.push(` // Process items in a single DB transaction`);
73
- lines.push(` const { pool } = require("./db");`);
74
- lines.push(` const client = await pool.connect();`);
75
- lines.push(` try {`);
76
- lines.push(` await client.query("BEGIN");`);
77
- lines.push(` for (const item of items) {`);
78
- lines.push(` try {`);
79
- lines.push(` // Execute the batch item`);
80
- lines.push(` // Each capability's batch handler is registered below`);
81
- lines.push(` const handler = batchHandlers.get(capability);`);
82
- lines.push(` if (handler) {`);
83
- lines.push(` const result = await handler(item.payload, client);`);
84
- lines.push(` item.resolve(result);`);
85
- lines.push(` } else {`);
86
- lines.push(` item.reject(new Error(\`No batch handler for \${capability}\`));`);
87
- lines.push(` }`);
88
- lines.push(` } catch (e: any) {`);
89
- lines.push(` item.reject(e);`);
90
- lines.push(` }`);
91
- lines.push(` }`);
92
- lines.push(` await client.query("COMMIT");`);
93
- lines.push(` histogram("batch.duration_ms", Date.now() - start, { capability });`);
94
- lines.push(` counter("batch.processed", { capability, count: String(items.length) });`);
95
- lines.push(` logger.info("batch_flush_completed", { event: capability, metadata: { count: items.length, duration_ms: Date.now() - start } });`);
96
- lines.push(` } catch (e: any) {`);
97
- lines.push(` await client.query("ROLLBACK");`);
98
- lines.push(` for (const item of items) item.reject(e);`);
99
- lines.push(` logger.error("batch_flush_failed", { event: capability, metadata: { error: e.message } });`);
100
- lines.push(` } finally {`);
101
- lines.push(` client.release();`);
102
- lines.push(` }`);
103
- lines.push(`}`);
104
- lines.push(``);
105
-
106
- // Batch handler registry
107
- lines.push(`type BatchHandler = (payload: Record<string, unknown>, client: any) => Promise<any>;`);
108
- lines.push(`const batchHandlers: Map<string, BatchHandler> = new Map();`);
109
- lines.push(``);
110
- lines.push(`export function registerBatchHandler(capability: string, handler: BatchHandler): void {`);
111
- lines.push(` batchHandlers.set(capability, handler);`);
112
- lines.push(`}`);
113
- lines.push(``);
114
-
115
- // Batch worker — flushes all queues on interval
116
- lines.push(`export function startBatchWorker(intervalMs: number = parseInt(process.env.BATCH_INTERVAL_MS || "5000")): NodeJS.Timeout {`);
117
- lines.push(` logger.info("batch_worker_started", { event: "startup", metadata: { interval_ms: intervalMs } });`);
118
- lines.push(` return setInterval(async () => {`);
119
- lines.push(` for (const [capability, queue] of queues) {`);
120
- lines.push(` if (queue.length === 0) continue;`);
121
- lines.push(` const batch = queue.splice(0, queue.length); // drain queue atomically`);
122
- lines.push(` await flushBatch(capability, batch).catch(e => {`);
123
- lines.push(` logger.error("batch_worker_error", { event: capability, metadata: { error: e.message } });`);
124
- lines.push(` });`);
125
- lines.push(` }`);
126
- lines.push(` }, intervalMs);`);
127
- lines.push(`}`);
128
- lines.push(``);
129
-
130
- // Capability-specific stub handlers
131
- for (const { modName, method } of batchMethods) {
132
- const key = `${modName}.${method.name}`;
133
- lines.push(`// Batch handler for ${key}`);
134
- lines.push(`// Register your implementation:`);
135
- lines.push(`// registerBatchHandler("${key}", async (payload, client) => { ... });`);
136
- lines.push(``);
137
- }
138
-
139
- return lines.join("\n");
140
- }
1
+ /**
2
+ * BoneScript Batch Executor Emitter
3
+ * Generates a batch processing module for sync: batch capabilities.
4
+ * Batch capabilities are queued and processed in configurable intervals.
5
+ */
6
+
7
+ import * as IR from "./ir";
8
+
9
+ export function emitBatchExecutor(system: IR.IRSystem): string {
10
+ // Collect all batch capabilities
11
+ const batchMethods: { modName: string; method: IR.IRMethod }[] = [];
12
+ for (const mod of system.modules) {
13
+ for (const iface of mod.interfaces) {
14
+ for (const method of iface.methods) {
15
+ if (method.sync === "batch") {
16
+ batchMethods.push({ modName: mod.name, method });
17
+ }
18
+ }
19
+ }
20
+ }
21
+
22
+ if (batchMethods.length === 0) return "";
23
+
24
+ const lines: string[] = [];
25
+ lines.push(`// Generated by BoneScript compiler. DO NOT EDIT.`);
26
+ lines.push(`// Batch executor for sync: batch capabilities.`);
27
+ lines.push(`// Set BATCH_INTERVAL_MS to control flush frequency (default: 5000ms).`);
28
+ lines.push(``);
29
+ lines.push(`import { query } from "./db";`);
30
+ lines.push(`import { logger } from "./logger";`);
31
+ lines.push(`import { counter, histogram } from "./metrics";`);
32
+ lines.push(``);
33
+
34
+ // Batch queue type
35
+ lines.push(`interface BatchItem {`);
36
+ lines.push(` capability: string;`);
37
+ lines.push(` payload: Record<string, unknown>;`);
38
+ lines.push(` enqueuedAt: Date;`);
39
+ lines.push(` resolve: (result: any) => void;`);
40
+ lines.push(` reject: (err: Error) => void;`);
41
+ lines.push(`}`);
42
+ lines.push(``);
43
+
44
+ // Per-capability queues
45
+ lines.push(`const queues: Map<string, BatchItem[]> = new Map([`);
46
+ for (const { modName, method } of batchMethods) {
47
+ lines.push(` ["${modName}.${method.name}", []],`);
48
+ }
49
+ lines.push(`]);`);
50
+ lines.push(``);
51
+
52
+ // Enqueue function
53
+ lines.push(`export function enqueueBatch(capability: string, payload: Record<string, unknown>): Promise<any> {`);
54
+ lines.push(` return new Promise((resolve, reject) => {`);
55
+ lines.push(` const queue = queues.get(capability);`);
56
+ lines.push(` if (!queue) {`);
57
+ lines.push(` reject(new Error(\`Unknown batch capability: \${capability}\`));`);
58
+ lines.push(` return;`);
59
+ lines.push(` }`);
60
+ lines.push(` queue.push({ capability, payload, enqueuedAt: new Date(), resolve, reject });`);
61
+ lines.push(` counter("batch.enqueued", { capability });`);
62
+ lines.push(` });`);
63
+ lines.push(`}`);
64
+ lines.push(``);
65
+
66
+ // Flush function — processes all queued items
67
+ lines.push(`async function flushBatch(capability: string, items: BatchItem[]): Promise<void> {`);
68
+ lines.push(` if (items.length === 0) return;`);
69
+ lines.push(` const start = Date.now();`);
70
+ lines.push(` logger.info("batch_flush_started", { event: capability, metadata: { count: items.length } });`);
71
+ lines.push(``);
72
+ lines.push(` // Process items in a single DB transaction`);
73
+ lines.push(` const { pool } = require("./db");`);
74
+ lines.push(` const client = await pool.connect();`);
75
+ lines.push(` try {`);
76
+ lines.push(` await client.query("BEGIN");`);
77
+ lines.push(` for (const item of items) {`);
78
+ lines.push(` try {`);
79
+ lines.push(` // Execute the batch item`);
80
+ lines.push(` // Each capability's batch handler is registered below`);
81
+ lines.push(` const handler = batchHandlers.get(capability);`);
82
+ lines.push(` if (handler) {`);
83
+ lines.push(` const result = await handler(item.payload, client);`);
84
+ lines.push(` item.resolve(result);`);
85
+ lines.push(` } else {`);
86
+ lines.push(` item.reject(new Error(\`No batch handler for \${capability}\`));`);
87
+ lines.push(` }`);
88
+ lines.push(` } catch (e: any) {`);
89
+ lines.push(` item.reject(e);`);
90
+ lines.push(` }`);
91
+ lines.push(` }`);
92
+ lines.push(` await client.query("COMMIT");`);
93
+ lines.push(` histogram("batch.duration_ms", Date.now() - start, { capability });`);
94
+ lines.push(` counter("batch.processed", { capability, count: String(items.length) });`);
95
+ lines.push(` logger.info("batch_flush_completed", { event: capability, metadata: { count: items.length, duration_ms: Date.now() - start } });`);
96
+ lines.push(` } catch (e: any) {`);
97
+ lines.push(` await client.query("ROLLBACK");`);
98
+ lines.push(` for (const item of items) item.reject(e);`);
99
+ lines.push(` logger.error("batch_flush_failed", { event: capability, metadata: { error: e.message } });`);
100
+ lines.push(` } finally {`);
101
+ lines.push(` client.release();`);
102
+ lines.push(` }`);
103
+ lines.push(`}`);
104
+ lines.push(``);
105
+
106
+ // Batch handler registry
107
+ lines.push(`type BatchHandler = (payload: Record<string, unknown>, client: any) => Promise<any>;`);
108
+ lines.push(`const batchHandlers: Map<string, BatchHandler> = new Map();`);
109
+ lines.push(``);
110
+ lines.push(`export function registerBatchHandler(capability: string, handler: BatchHandler): void {`);
111
+ lines.push(` batchHandlers.set(capability, handler);`);
112
+ lines.push(`}`);
113
+ lines.push(``);
114
+
115
+ // Batch worker — flushes all queues on interval
116
+ lines.push(`export function startBatchWorker(intervalMs: number = parseInt(process.env.BATCH_INTERVAL_MS || "5000")): NodeJS.Timeout {`);
117
+ lines.push(` logger.info("batch_worker_started", { event: "startup", metadata: { interval_ms: intervalMs } });`);
118
+ lines.push(` return setInterval(async () => {`);
119
+ lines.push(` for (const [capability, queue] of queues) {`);
120
+ lines.push(` if (queue.length === 0) continue;`);
121
+ lines.push(` const batch = queue.splice(0, queue.length); // drain queue atomically`);
122
+ lines.push(` await flushBatch(capability, batch).catch(e => {`);
123
+ lines.push(` logger.error("batch_worker_error", { event: capability, metadata: { error: e.message } });`);
124
+ lines.push(` });`);
125
+ lines.push(` }`);
126
+ lines.push(` }, intervalMs);`);
127
+ lines.push(`}`);
128
+ lines.push(``);
129
+
130
+ // Capability-specific stub handlers
131
+ for (const { modName, method } of batchMethods) {
132
+ const key = `${modName}.${method.name}`;
133
+ lines.push(`// Batch handler for ${key}`);
134
+ lines.push(`// Register your implementation:`);
135
+ lines.push(`// registerBatchHandler("${key}", async (payload, client) => { ... });`);
136
+ lines.push(``);
137
+ }
138
+
139
+ return lines.join("\n");
140
+ }