@donkeylabs/cli 2.1.0 → 2.2.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.
package/package.json
CHANGED
|
@@ -18,7 +18,8 @@ export async function generateClientCommand(
|
|
|
18
18
|
// Extract routes from server
|
|
19
19
|
const entryPath = config.entry || "./src/index.ts";
|
|
20
20
|
console.log(pc.dim(`Extracting routes from ${entryPath}...`));
|
|
21
|
-
const
|
|
21
|
+
const serverOutput = await extractRoutesFromServer(entryPath);
|
|
22
|
+
const routes = serverOutput.routes;
|
|
22
23
|
|
|
23
24
|
if (routes.length === 0) {
|
|
24
25
|
console.warn(pc.yellow("No routes found - generating empty client"));
|
|
@@ -36,15 +36,26 @@ export interface RouteInfo {
|
|
|
36
36
|
eventsSource?: Record<string, string>;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
export interface ProcessInfo {
|
|
40
|
+
name: string;
|
|
41
|
+
events?: Record<string, string>;
|
|
42
|
+
commands?: Record<string, string>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ServerOutput {
|
|
46
|
+
routes: RouteInfo[];
|
|
47
|
+
processes: ProcessInfo[];
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
/**
|
|
40
51
|
* Run the server entry file with DONKEYLABS_GENERATE=1 to get typed route metadata
|
|
41
52
|
*/
|
|
42
|
-
export async function extractRoutesFromServer(entryPath: string): Promise<
|
|
53
|
+
export async function extractRoutesFromServer(entryPath: string): Promise<ServerOutput> {
|
|
43
54
|
const fullPath = join(process.cwd(), entryPath);
|
|
44
55
|
|
|
45
56
|
if (!existsSync(fullPath)) {
|
|
46
57
|
console.warn(pc.yellow(`Entry file not found: ${entryPath}`));
|
|
47
|
-
return [];
|
|
58
|
+
return { routes: [], processes: [] };
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
const TIMEOUT_MS = 10000; // 10 second timeout
|
|
@@ -66,7 +77,7 @@ export async function extractRoutesFromServer(entryPath: string): Promise<RouteI
|
|
|
66
77
|
child.kill("SIGTERM");
|
|
67
78
|
console.warn(pc.yellow(`Route extraction timed out after ${TIMEOUT_MS / 1000}s`));
|
|
68
79
|
console.warn(pc.dim("Make sure routes are registered with server.use() before any blocking operations"));
|
|
69
|
-
resolve([]);
|
|
80
|
+
resolve({ routes: [], processes: [] });
|
|
70
81
|
}, TIMEOUT_MS);
|
|
71
82
|
|
|
72
83
|
child.stdout?.on("data", (data) => {
|
|
@@ -84,7 +95,7 @@ export async function extractRoutesFromServer(entryPath: string): Promise<RouteI
|
|
|
84
95
|
if (code !== 0) {
|
|
85
96
|
console.warn(pc.yellow(`Failed to extract routes from server (exit code ${code})`));
|
|
86
97
|
if (stderr) console.warn(pc.dim(stderr));
|
|
87
|
-
resolve([]);
|
|
98
|
+
resolve({ routes: [], processes: [] });
|
|
88
99
|
return;
|
|
89
100
|
}
|
|
90
101
|
|
|
@@ -105,17 +116,25 @@ export async function extractRoutesFromServer(entryPath: string): Promise<RouteI
|
|
|
105
116
|
eventsSource: r.eventsType,
|
|
106
117
|
};
|
|
107
118
|
});
|
|
108
|
-
|
|
119
|
+
|
|
120
|
+
// Parse process definitions
|
|
121
|
+
const processes: ProcessInfo[] = (result.processes || []).map((p: any) => ({
|
|
122
|
+
name: p.name,
|
|
123
|
+
events: p.events,
|
|
124
|
+
commands: p.commands,
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
resolve({ routes, processes });
|
|
109
128
|
} catch (e) {
|
|
110
129
|
console.warn(pc.yellow("Failed to parse route data from server"));
|
|
111
|
-
resolve([]);
|
|
130
|
+
resolve({ routes: [], processes: [] });
|
|
112
131
|
}
|
|
113
132
|
});
|
|
114
133
|
|
|
115
134
|
child.on("error", (err) => {
|
|
116
135
|
clearTimeout(timeout);
|
|
117
136
|
console.warn(pc.yellow(`Failed to run entry file: ${err.message}`));
|
|
118
|
-
resolve([]);
|
|
137
|
+
resolve({ routes: [], processes: [] });
|
|
119
138
|
});
|
|
120
139
|
});
|
|
121
140
|
}
|
package/src/commands/generate.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { BunSqliteDialect } from "kysely-bun-sqlite";
|
|
|
7
7
|
import { Database } from "bun:sqlite";
|
|
8
8
|
import { generate, KyselyBunSqliteDialect } from "kysely-codegen";
|
|
9
9
|
import * as ts from "typescript";
|
|
10
|
-
import { loadConfig, extractRoutesFromServer, type DonkeylabsConfig, type RouteInfo } from "./generate-utils";
|
|
10
|
+
import { loadConfig, extractRoutesFromServer, type DonkeylabsConfig, type RouteInfo, type ProcessInfo } from "./generate-utils";
|
|
11
11
|
|
|
12
12
|
async function getPluginExportName(pluginPath: string): Promise<string | null> {
|
|
13
13
|
try {
|
|
@@ -918,9 +918,11 @@ export async function generateCommand(_args: string[]): Promise<void> {
|
|
|
918
918
|
);
|
|
919
919
|
}
|
|
920
920
|
|
|
921
|
-
// Extract routes by running the server with DONKEYLABS_GENERATE=1
|
|
921
|
+
// Extract routes and processes by running the server with DONKEYLABS_GENERATE=1
|
|
922
922
|
const entryPath = config.entry || "./src/index.ts";
|
|
923
|
-
const
|
|
923
|
+
const serverOutput = await extractRoutesFromServer(entryPath);
|
|
924
|
+
const serverRoutes = serverOutput.routes;
|
|
925
|
+
const serverProcesses = serverOutput.processes;
|
|
924
926
|
|
|
925
927
|
// Find custom service definitions
|
|
926
928
|
const services = await findServiceDefinitions(entryPath);
|
|
@@ -940,13 +942,22 @@ export async function generateCommand(_args: string[]): Promise<void> {
|
|
|
940
942
|
);
|
|
941
943
|
}
|
|
942
944
|
|
|
945
|
+
// Log process definitions found
|
|
946
|
+
if (serverProcesses.length > 0) {
|
|
947
|
+
console.log(
|
|
948
|
+
pc.green("Found processes:"),
|
|
949
|
+
serverProcesses.map((p) => pc.dim(p.name)).join(", ")
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
|
|
943
953
|
// Generate all files
|
|
944
954
|
await generateRegistry(plugins, outPath);
|
|
945
955
|
await generateContext(plugins, services, outPath);
|
|
946
956
|
await generateRouteTypes(fileRoutes, outPath);
|
|
947
957
|
await generateEventTypes(serverEvents, outPath);
|
|
958
|
+
await generateProcessTypes(serverProcesses, outPath);
|
|
948
959
|
|
|
949
|
-
const generated = ["registry", "context", "routes", "events"];
|
|
960
|
+
const generated = ["registry", "context", "routes", "events", "processes"];
|
|
950
961
|
|
|
951
962
|
// Determine client output path
|
|
952
963
|
const clientOutput = config.client?.output || join(outPath, "client.ts");
|
|
@@ -1454,3 +1465,99 @@ ${eventRegistryEntries.join("\n")}
|
|
|
1454
1465
|
|
|
1455
1466
|
await writeFile(join(outPath, "events.ts"), content);
|
|
1456
1467
|
}
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* Generate processes.ts with typed events, commands, and ProcessRegistry augmentation.
|
|
1471
|
+
*/
|
|
1472
|
+
async function generateProcessTypes(processes: ProcessInfo[], outPath: string): Promise<void> {
|
|
1473
|
+
if (processes.length === 0) {
|
|
1474
|
+
const content = `// Auto-generated by donkeylabs generate
|
|
1475
|
+
// Processes - import as: import { type ProcessEventMap, type ProcessCommandMap } from ".@donkeylabs/server/processes";
|
|
1476
|
+
|
|
1477
|
+
/** Map of all process event names to their data types */
|
|
1478
|
+
export interface ProcessEventMap {}
|
|
1479
|
+
|
|
1480
|
+
/** Map of all process command names to their data types */
|
|
1481
|
+
export interface ProcessCommandMap {}
|
|
1482
|
+
|
|
1483
|
+
// Augment ProcessRegistry for typed send() (empty when no processes defined)
|
|
1484
|
+
declare module "@donkeylabs/server" {
|
|
1485
|
+
interface ProcessRegistry {}
|
|
1486
|
+
}
|
|
1487
|
+
`;
|
|
1488
|
+
await writeFile(join(outPath, "processes.ts"), content);
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
const namespaceBlocks: string[] = [];
|
|
1493
|
+
const eventMapEntries: string[] = [];
|
|
1494
|
+
const commandMapEntries: string[] = [];
|
|
1495
|
+
const registryEntries: string[] = [];
|
|
1496
|
+
|
|
1497
|
+
for (const proc of processes) {
|
|
1498
|
+
const pascalName = toPascalCase(proc.name);
|
|
1499
|
+
|
|
1500
|
+
const eventDecls: string[] = [];
|
|
1501
|
+
const commandDecls: string[] = [];
|
|
1502
|
+
|
|
1503
|
+
// Generate event types
|
|
1504
|
+
if (proc.events) {
|
|
1505
|
+
for (const [eventName, tsType] of Object.entries(proc.events)) {
|
|
1506
|
+
const pascalEvent = toPascalCase(eventName);
|
|
1507
|
+
eventDecls.push(` export type ${pascalEvent} = ${tsType};`);
|
|
1508
|
+
eventMapEntries.push(` "${proc.name}.${eventName}": ${pascalName}.Events.${pascalEvent};`);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
// Generate command types
|
|
1513
|
+
if (proc.commands) {
|
|
1514
|
+
for (const [cmdName, tsType] of Object.entries(proc.commands)) {
|
|
1515
|
+
const pascalCmd = toPascalCase(cmdName);
|
|
1516
|
+
commandDecls.push(` export type ${pascalCmd} = ${tsType};`);
|
|
1517
|
+
commandMapEntries.push(` "${proc.name}.${cmdName}": ${pascalName}.Commands.${pascalCmd};`);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// Build namespace
|
|
1522
|
+
const eventsNs = eventDecls.length > 0
|
|
1523
|
+
? ` export namespace Events {\n${eventDecls.join("\n")}\n }`
|
|
1524
|
+
: ` export namespace Events {}`;
|
|
1525
|
+
const commandsNs = commandDecls.length > 0
|
|
1526
|
+
? ` export namespace Commands {\n${commandDecls.join("\n")}\n }`
|
|
1527
|
+
: ` export namespace Commands {}`;
|
|
1528
|
+
|
|
1529
|
+
namespaceBlocks.push(`export namespace ${pascalName} {\n${eventsNs}\n${commandsNs}\n}`);
|
|
1530
|
+
|
|
1531
|
+
// Registry entry
|
|
1532
|
+
registryEntries.push(` "${proc.name}": {
|
|
1533
|
+
events: ${pascalName}.Events;
|
|
1534
|
+
commands: ${pascalName}.Commands;
|
|
1535
|
+
};`);
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
const content = `// Auto-generated by donkeylabs generate
|
|
1539
|
+
// Processes - import as: import { type ProcessEventMap, type ProcessCommandMap } from ".@donkeylabs/server/processes";
|
|
1540
|
+
|
|
1541
|
+
// Process type namespaces
|
|
1542
|
+
${namespaceBlocks.join("\n\n")}
|
|
1543
|
+
|
|
1544
|
+
/** Map of all process event names to their data types */
|
|
1545
|
+
export interface ProcessEventMap {
|
|
1546
|
+
${eventMapEntries.join("\n")}
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
/** Map of all process command names to their data types */
|
|
1550
|
+
export interface ProcessCommandMap {
|
|
1551
|
+
${commandMapEntries.join("\n")}
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
// Augment ProcessRegistry for typed send()
|
|
1555
|
+
declare module "@donkeylabs/server" {
|
|
1556
|
+
interface ProcessRegistry {
|
|
1557
|
+
${registryEntries.join("\n")}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
`;
|
|
1561
|
+
|
|
1562
|
+
await writeFile(join(outPath, "processes.ts"), content);
|
|
1563
|
+
}
|