@donkeylabs/server 0.3.0 → 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.
- package/LICENSE +1 -1
- package/docs/api-client.md +7 -7
- package/docs/cache.md +1 -74
- package/docs/core-services.md +4 -116
- package/docs/cron.md +1 -1
- package/docs/errors.md +2 -2
- package/docs/events.md +3 -98
- package/docs/handlers.md +13 -48
- package/docs/logger.md +3 -58
- package/docs/middleware.md +2 -2
- package/docs/plugins.md +13 -64
- package/docs/project-structure.md +4 -142
- package/docs/rate-limiter.md +4 -136
- package/docs/router.md +6 -14
- package/docs/sse.md +1 -99
- package/docs/sveltekit-adapter.md +420 -0
- package/package.json +8 -11
- package/registry.d.ts +15 -14
- package/src/core/cache.ts +0 -75
- package/src/core/cron.ts +3 -96
- package/src/core/errors.ts +78 -11
- package/src/core/events.ts +1 -47
- package/src/core/index.ts +0 -4
- package/src/core/jobs.ts +0 -112
- package/src/core/logger.ts +12 -79
- package/src/core/rate-limiter.ts +29 -108
- package/src/core/sse.ts +1 -84
- package/src/core.ts +13 -104
- package/src/generator/index.ts +566 -0
- package/src/generator/zod-to-ts.ts +114 -0
- package/src/handlers.ts +14 -110
- package/src/index.ts +30 -24
- package/src/middleware.ts +2 -5
- package/src/registry.ts +4 -0
- package/src/router.ts +47 -1
- package/src/server.ts +618 -332
- package/README.md +0 -254
- package/cli/commands/dev.ts +0 -134
- package/cli/commands/generate.ts +0 -605
- package/cli/commands/init.ts +0 -205
- package/cli/commands/interactive.ts +0 -417
- package/cli/commands/plugin.ts +0 -192
- package/cli/commands/route.ts +0 -195
- package/cli/donkeylabs +0 -2
- package/cli/index.ts +0 -114
- package/docs/svelte-frontend.md +0 -324
- package/docs/testing.md +0 -438
- package/mcp/donkeylabs-mcp +0 -3238
- package/mcp/server.ts +0 -3238
package/cli/commands/plugin.ts
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import pc from "picocolors";
|
|
5
|
-
import prompts from "prompts";
|
|
6
|
-
|
|
7
|
-
export async function pluginCommand(args: string[]): Promise<void> {
|
|
8
|
-
const subcommand = args[0];
|
|
9
|
-
|
|
10
|
-
switch (subcommand) {
|
|
11
|
-
case "create":
|
|
12
|
-
case "new":
|
|
13
|
-
await createPlugin(args[1]);
|
|
14
|
-
break;
|
|
15
|
-
|
|
16
|
-
case "list":
|
|
17
|
-
await listPlugins();
|
|
18
|
-
break;
|
|
19
|
-
|
|
20
|
-
default:
|
|
21
|
-
console.log(`
|
|
22
|
-
${pc.bold("Plugin Management")}
|
|
23
|
-
|
|
24
|
-
${pc.bold("Commands:")}
|
|
25
|
-
${pc.cyan("create <name>")} Create a new plugin
|
|
26
|
-
${pc.cyan("list")} List installed plugins
|
|
27
|
-
|
|
28
|
-
${pc.bold("Examples:")}
|
|
29
|
-
donkeylabs plugin create auth
|
|
30
|
-
donkeylabs plugin list
|
|
31
|
-
`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function createPlugin(name?: string): Promise<void> {
|
|
36
|
-
if (!name) {
|
|
37
|
-
const response = await prompts({
|
|
38
|
-
type: "text",
|
|
39
|
-
name: "name",
|
|
40
|
-
message: "Plugin name:",
|
|
41
|
-
validate: (v) =>
|
|
42
|
-
/^[a-z][a-z0-9-]*$/.test(v) ||
|
|
43
|
-
"Name must be lowercase alphanumeric with dashes",
|
|
44
|
-
});
|
|
45
|
-
name = response.name;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!name) {
|
|
49
|
-
console.log(pc.yellow("Cancelled."));
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const { hasSchema, hasDependencies } = await prompts([
|
|
54
|
-
{
|
|
55
|
-
type: "confirm",
|
|
56
|
-
name: "hasSchema",
|
|
57
|
-
message: "Does this plugin need a database schema?",
|
|
58
|
-
initial: false,
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
type: "confirm",
|
|
62
|
-
name: "hasDependencies",
|
|
63
|
-
message: "Does this plugin depend on other plugins?",
|
|
64
|
-
initial: false,
|
|
65
|
-
},
|
|
66
|
-
]);
|
|
67
|
-
|
|
68
|
-
let dependencies: string[] = [];
|
|
69
|
-
if (hasDependencies) {
|
|
70
|
-
const { deps } = await prompts({
|
|
71
|
-
type: "list",
|
|
72
|
-
name: "deps",
|
|
73
|
-
message: "Enter dependency names (comma-separated):",
|
|
74
|
-
separator: ",",
|
|
75
|
-
});
|
|
76
|
-
dependencies = deps?.filter(Boolean) || [];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const pluginDir = join(process.cwd(), "src/plugins", name);
|
|
80
|
-
|
|
81
|
-
if (existsSync(pluginDir)) {
|
|
82
|
-
console.log(pc.red(`Plugin directory already exists: ${pluginDir}`));
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
await mkdir(pluginDir, { recursive: true });
|
|
87
|
-
|
|
88
|
-
const camelName = name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
89
|
-
const pascalName = camelName.charAt(0).toUpperCase() + camelName.slice(1);
|
|
90
|
-
|
|
91
|
-
const schemaImport = hasSchema
|
|
92
|
-
? `import type { DB as ${pascalName}Schema } from "./schema";\n`
|
|
93
|
-
: "";
|
|
94
|
-
|
|
95
|
-
const schemaType = hasSchema ? `<${pascalName}Schema>()` : "";
|
|
96
|
-
|
|
97
|
-
const depsLine =
|
|
98
|
-
dependencies.length > 0
|
|
99
|
-
? ` dependencies: [${dependencies.map((d) => `"${d}"`).join(", ")}] as const,\n`
|
|
100
|
-
: "";
|
|
101
|
-
|
|
102
|
-
const pluginContent = `import { createPlugin } from "@donkeylabs/server";
|
|
103
|
-
${schemaImport}
|
|
104
|
-
export interface ${pascalName}Service {
|
|
105
|
-
// Define your service interface here
|
|
106
|
-
getData(): Promise<string>;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export const ${camelName}Plugin = createPlugin${hasSchema ? `\n .withSchema${schemaType}` : ""}
|
|
110
|
-
.define({
|
|
111
|
-
name: "${name}",
|
|
112
|
-
version: "1.0.0",
|
|
113
|
-
${depsLine}
|
|
114
|
-
service: async (ctx): Promise<${pascalName}Service> => {
|
|
115
|
-
console.log("[${pascalName}Plugin] Initializing...");
|
|
116
|
-
${
|
|
117
|
-
dependencies.length > 0
|
|
118
|
-
? dependencies.map((d) => ` // Access ${d} via: ctx.deps.${d}`).join("\n") + "\n"
|
|
119
|
-
: ""
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
getData: async () => {
|
|
123
|
-
return "Hello from ${name} plugin!";
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
`;
|
|
129
|
-
|
|
130
|
-
await writeFile(join(pluginDir, "index.ts"), pluginContent);
|
|
131
|
-
console.log(pc.green(" Created:"), `src/plugins/${name}/index.ts`);
|
|
132
|
-
|
|
133
|
-
if (hasSchema) {
|
|
134
|
-
const schemaContent = `// Database schema types for ${name} plugin
|
|
135
|
-
// Run \`bun run gen:types\` to regenerate from database
|
|
136
|
-
|
|
137
|
-
export interface DB {
|
|
138
|
-
// Define your table interfaces here
|
|
139
|
-
// Example:
|
|
140
|
-
// ${name}: {
|
|
141
|
-
// id: Generated<number>;
|
|
142
|
-
// name: string;
|
|
143
|
-
// created_at: Generated<string>;
|
|
144
|
-
// };
|
|
145
|
-
}
|
|
146
|
-
`;
|
|
147
|
-
await writeFile(join(pluginDir, "schema.ts"), schemaContent);
|
|
148
|
-
console.log(pc.green(" Created:"), `src/plugins/${name}/schema.ts`);
|
|
149
|
-
|
|
150
|
-
await mkdir(join(pluginDir, "migrations"), { recursive: true });
|
|
151
|
-
|
|
152
|
-
const migrationContent = `import { Kysely } from "kysely";
|
|
153
|
-
|
|
154
|
-
export async function up(db: Kysely<any>) {
|
|
155
|
-
// Create your tables here
|
|
156
|
-
// await db.schema
|
|
157
|
-
// .createTable("${name}")
|
|
158
|
-
// .ifNotExists()
|
|
159
|
-
// .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
160
|
-
// .addColumn("name", "text", (col) => col.notNull())
|
|
161
|
-
// .addColumn("created_at", "text", (col) => col.defaultTo(sql\`CURRENT_TIMESTAMP\`))
|
|
162
|
-
// .execute();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export async function down(db: Kysely<any>) {
|
|
166
|
-
// Drop your tables here
|
|
167
|
-
// await db.schema.dropTable("${name}").ifExists().execute();
|
|
168
|
-
}
|
|
169
|
-
`;
|
|
170
|
-
await writeFile(
|
|
171
|
-
join(pluginDir, "migrations", "001_initial.ts"),
|
|
172
|
-
migrationContent
|
|
173
|
-
);
|
|
174
|
-
console.log(
|
|
175
|
-
pc.green(" Created:"),
|
|
176
|
-
`src/plugins/${name}/migrations/001_initial.ts`
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
console.log(`
|
|
181
|
-
${pc.bold(pc.green("Plugin created!"))}
|
|
182
|
-
|
|
183
|
-
${pc.bold("Next steps:")}
|
|
184
|
-
1. Edit your plugin at ${pc.cyan(`src/plugins/${name}/index.ts`)}
|
|
185
|
-
${hasSchema ? ` 2. Define your schema in ${pc.cyan(`src/plugins/${name}/schema.ts`)}\n 3. Add migrations in ${pc.cyan(`src/plugins/${name}/migrations/`)}\n` : ""} ${hasSchema ? "4" : "2"}. Regenerate types: ${pc.cyan("donkeylabs generate")}
|
|
186
|
-
`);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async function listPlugins() {
|
|
190
|
-
console.log(pc.yellow("Plugin listing not yet implemented."));
|
|
191
|
-
console.log("Run 'donkeylabs generate' to see discovered plugins.");
|
|
192
|
-
}
|
package/cli/commands/route.ts
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route Command
|
|
3
|
-
*
|
|
4
|
-
* Create new routes from the command line
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* donkeylabs route <router> <route-name>
|
|
8
|
-
* donkeylabs route users create
|
|
9
|
-
* donkeylabs route orders get-by-id
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { writeFile, mkdir } from "node:fs/promises";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
import { existsSync } from "node:fs";
|
|
15
|
-
import pc from "picocolors";
|
|
16
|
-
|
|
17
|
-
function toPascalCase(str: string): string {
|
|
18
|
-
return str
|
|
19
|
-
.replace(/-([a-z])/g, (_, c) => c.toUpperCase())
|
|
20
|
-
.replace(/^./, (c) => c.toUpperCase());
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function routeCommand(args: string[]): Promise<void> {
|
|
24
|
-
const routerName = args[0];
|
|
25
|
-
const routeName = args[1];
|
|
26
|
-
|
|
27
|
-
if (!routerName || !routeName) {
|
|
28
|
-
console.log(`
|
|
29
|
-
${pc.bold("Usage:")}
|
|
30
|
-
donkeylabs route <router> <route-name>
|
|
31
|
-
|
|
32
|
-
${pc.bold("Examples:")}
|
|
33
|
-
donkeylabs route users create
|
|
34
|
-
donkeylabs route orders get-by-id
|
|
35
|
-
donkeylabs route health check
|
|
36
|
-
|
|
37
|
-
${pc.bold("This creates:")}
|
|
38
|
-
src/routes/<router>/<route-name>/
|
|
39
|
-
├── index.ts # Route definition
|
|
40
|
-
├── models/model.ts # Schema + Model class
|
|
41
|
-
└── tests/
|
|
42
|
-
├── unit.test.ts
|
|
43
|
-
└── integ.test.ts
|
|
44
|
-
`);
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Validate names
|
|
49
|
-
if (!/^[a-z][a-z0-9-]*$/.test(routerName)) {
|
|
50
|
-
console.error(pc.red("Router name must use lowercase letters, numbers, and hyphens"));
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!/^[a-z][a-z0-9-]*$/.test(routeName)) {
|
|
55
|
-
console.error(pc.red("Route name must use lowercase letters, numbers, and hyphens"));
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const routesDir = join(process.cwd(), "src/routes");
|
|
60
|
-
const routePath = join(routesDir, routerName, routeName);
|
|
61
|
-
|
|
62
|
-
if (existsSync(routePath)) {
|
|
63
|
-
console.error(pc.red(`Route already exists: src/routes/${routerName}/${routeName}`));
|
|
64
|
-
process.exit(1);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Create directory structure
|
|
68
|
-
await mkdir(join(routePath, "models"), { recursive: true });
|
|
69
|
-
await mkdir(join(routePath, "tests"), { recursive: true });
|
|
70
|
-
|
|
71
|
-
// Generate PascalCase and camelCase names
|
|
72
|
-
const pascalRouter = toPascalCase(routerName);
|
|
73
|
-
const pascalRoute = toPascalCase(routeName);
|
|
74
|
-
const camelRoute = routeName.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
|
|
75
|
-
|
|
76
|
-
// Create model.ts with Model class
|
|
77
|
-
const modelContent = `import { z } from "zod";
|
|
78
|
-
|
|
79
|
-
// After running \`donkeylabs generate\`, use typed Handler:
|
|
80
|
-
// import type { Handler } from "@donkeylabs/server";
|
|
81
|
-
// import type { ${pascalRouter} } from "$server/routes";
|
|
82
|
-
// export class ${pascalRoute}Model implements Handler<${pascalRouter}.${pascalRoute}> { ... }
|
|
83
|
-
|
|
84
|
-
// Input/Output schemas
|
|
85
|
-
export const Input = z.object({
|
|
86
|
-
// Define your input schema here
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
export const Output = z.object({
|
|
90
|
-
success: z.boolean(),
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
export type Input = z.infer<typeof Input>;
|
|
94
|
-
export type Output = z.infer<typeof Output>;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Model class with handler logic.
|
|
98
|
-
* After gen:types, implement Handler<${pascalRouter}.${pascalRoute}> for full typing.
|
|
99
|
-
*/
|
|
100
|
-
export class ${pascalRoute}Model {
|
|
101
|
-
constructor(private ctx: any) {}
|
|
102
|
-
|
|
103
|
-
handle(input: Input): Output {
|
|
104
|
-
return {
|
|
105
|
-
success: true,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
`;
|
|
110
|
-
await writeFile(join(routePath, "models/model.ts"), modelContent);
|
|
111
|
-
|
|
112
|
-
// Create index.ts (route definition)
|
|
113
|
-
const routeIndexContent = `// Route definition
|
|
114
|
-
// After gen:types, use: Route<${pascalRouter}.${pascalRoute}> for full typing
|
|
115
|
-
import type { AppRoute } from "@donkeylabs/server";
|
|
116
|
-
import { Input, Output, ${pascalRoute}Model } from "./models/model";
|
|
117
|
-
|
|
118
|
-
export const ${camelRoute}Route: AppRoute = {
|
|
119
|
-
input: Input,
|
|
120
|
-
output: Output,
|
|
121
|
-
handle: async (input, ctx) => {
|
|
122
|
-
const model = new ${pascalRoute}Model(ctx);
|
|
123
|
-
return model.handle(input);
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
`;
|
|
127
|
-
await writeFile(join(routePath, "index.ts"), routeIndexContent);
|
|
128
|
-
|
|
129
|
-
// Create unit.test.ts
|
|
130
|
-
const unitTestContent = `import { describe, it, expect } from "bun:test";
|
|
131
|
-
import { ${pascalRoute}Model, Input } from "../models/model";
|
|
132
|
-
|
|
133
|
-
describe("${routerName}.${routeName} model", () => {
|
|
134
|
-
it("should return success", () => {
|
|
135
|
-
const input = Input.parse({});
|
|
136
|
-
const ctx = {} as any;
|
|
137
|
-
const model = new ${pascalRoute}Model(ctx);
|
|
138
|
-
const result = model.handle(input);
|
|
139
|
-
expect(result.success).toBe(true);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
`;
|
|
143
|
-
await writeFile(join(routePath, "tests/unit.test.ts"), unitTestContent);
|
|
144
|
-
|
|
145
|
-
// Create integ.test.ts
|
|
146
|
-
const integTestContent = `import { describe, it, expect } from "bun:test";
|
|
147
|
-
|
|
148
|
-
const BASE_URL = process.env.TEST_SERVER_URL || "http://localhost:3000";
|
|
149
|
-
|
|
150
|
-
describe("${routerName}.${routeName} (integration)", () => {
|
|
151
|
-
// Server must be running for integration tests
|
|
152
|
-
it("POST /${routerName}.${routeName} returns success", async () => {
|
|
153
|
-
try {
|
|
154
|
-
const res = await fetch(\`\${BASE_URL}/${routerName}.${routeName}\`, {
|
|
155
|
-
method: "POST",
|
|
156
|
-
headers: { "Content-Type": "application/json" },
|
|
157
|
-
body: JSON.stringify({}),
|
|
158
|
-
});
|
|
159
|
-
expect(res.ok).toBe(true);
|
|
160
|
-
} catch (e: any) {
|
|
161
|
-
if (e.code === "ConnectionRefused") {
|
|
162
|
-
console.log("Skipping: Server not running");
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
throw e;
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
`;
|
|
170
|
-
await writeFile(join(routePath, "tests/integ.test.ts"), integTestContent);
|
|
171
|
-
|
|
172
|
-
// Check if router index exists, if not create it
|
|
173
|
-
const routerIndexPath = join(routesDir, routerName, "index.ts");
|
|
174
|
-
if (!existsSync(routerIndexPath)) {
|
|
175
|
-
const routerIndexContent = `import { createRouter } from "@donkeylabs/server";
|
|
176
|
-
import { ${camelRoute}Route } from "./${routeName}";
|
|
177
|
-
|
|
178
|
-
export const ${camelRoute}Router = createRouter("${routerName}")
|
|
179
|
-
.route("${routeName}").typed(${camelRoute}Route);
|
|
180
|
-
`;
|
|
181
|
-
await writeFile(routerIndexPath, routerIndexContent);
|
|
182
|
-
console.log(pc.green(`Created router: src/routes/${routerName}/index.ts`));
|
|
183
|
-
} else {
|
|
184
|
-
console.log(pc.yellow(`\nNote: Add the route to src/routes/${routerName}/index.ts:`));
|
|
185
|
-
console.log(pc.gray(` import { ${camelRoute}Route } from "./${routeName}";`));
|
|
186
|
-
console.log(pc.gray(` .route("${routeName}").typed(${camelRoute}Route)`));
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
console.log(pc.green(`\nCreated route: src/routes/${routerName}/${routeName}/`));
|
|
190
|
-
console.log(pc.gray(` - index.ts`));
|
|
191
|
-
console.log(pc.gray(` - models/model.ts`));
|
|
192
|
-
console.log(pc.gray(` - tests/unit.test.ts`));
|
|
193
|
-
console.log(pc.gray(` - tests/integ.test.ts`));
|
|
194
|
-
console.log(pc.cyan(`\nRun ${pc.bold("donkeylabs generate")} to update types.`));
|
|
195
|
-
}
|
package/cli/donkeylabs
DELETED
package/cli/index.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* @donkeylabs/server CLI
|
|
4
|
-
*
|
|
5
|
-
* Commands:
|
|
6
|
-
* init Initialize a new project
|
|
7
|
-
* generate Generate types (registry, context, client)
|
|
8
|
-
* plugin Plugin management (create, list)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { parseArgs } from "node:util";
|
|
12
|
-
import pc from "picocolors";
|
|
13
|
-
|
|
14
|
-
const { positionals, values } = parseArgs({
|
|
15
|
-
args: process.argv.slice(2),
|
|
16
|
-
options: {
|
|
17
|
-
help: { type: "boolean", short: "h" },
|
|
18
|
-
version: { type: "boolean", short: "v" },
|
|
19
|
-
},
|
|
20
|
-
allowPositionals: true,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const command = positionals[0];
|
|
24
|
-
|
|
25
|
-
function printHelp() {
|
|
26
|
-
console.log(`
|
|
27
|
-
${pc.bold("@donkeylabs/server")} - Type-safe plugin system for Bun
|
|
28
|
-
|
|
29
|
-
${pc.bold("Usage:")}
|
|
30
|
-
donkeylabs Interactive menu
|
|
31
|
-
donkeylabs <command> [options]
|
|
32
|
-
|
|
33
|
-
${pc.bold("Commands:")}
|
|
34
|
-
${pc.cyan("init")} Initialize a new project
|
|
35
|
-
${pc.cyan("generate")} Generate types (registry, context, client)
|
|
36
|
-
${pc.cyan("route")} Create a new route
|
|
37
|
-
${pc.cyan("dev")} Start dev server with auto-regeneration
|
|
38
|
-
${pc.cyan("plugin")} Plugin management
|
|
39
|
-
|
|
40
|
-
${pc.bold("Options:")}
|
|
41
|
-
-h, --help Show this help message
|
|
42
|
-
-v, --version Show version number
|
|
43
|
-
|
|
44
|
-
${pc.bold("Examples:")}
|
|
45
|
-
donkeylabs # Interactive menu
|
|
46
|
-
donkeylabs init
|
|
47
|
-
donkeylabs generate
|
|
48
|
-
donkeylabs route users create
|
|
49
|
-
donkeylabs dev
|
|
50
|
-
donkeylabs plugin create myPlugin
|
|
51
|
-
`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function printVersion() {
|
|
55
|
-
const pkg = await Bun.file(new URL("../package.json", import.meta.url)).json();
|
|
56
|
-
console.log(pkg.version);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function main() {
|
|
60
|
-
if (values.help) {
|
|
61
|
-
printHelp();
|
|
62
|
-
process.exit(0);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (values.version) {
|
|
66
|
-
await printVersion();
|
|
67
|
-
process.exit(0);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// No command provided - launch interactive mode
|
|
71
|
-
if (!command) {
|
|
72
|
-
const { interactiveCommand } = await import("./commands/interactive");
|
|
73
|
-
await interactiveCommand();
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
switch (command) {
|
|
78
|
-
case "init":
|
|
79
|
-
const { initCommand } = await import("./commands/init");
|
|
80
|
-
await initCommand(positionals.slice(1));
|
|
81
|
-
break;
|
|
82
|
-
|
|
83
|
-
case "generate":
|
|
84
|
-
case "gen":
|
|
85
|
-
const { generateCommand } = await import("./commands/generate");
|
|
86
|
-
await generateCommand(positionals.slice(1));
|
|
87
|
-
break;
|
|
88
|
-
|
|
89
|
-
case "plugin":
|
|
90
|
-
const { pluginCommand } = await import("./commands/plugin");
|
|
91
|
-
await pluginCommand(positionals.slice(1));
|
|
92
|
-
break;
|
|
93
|
-
|
|
94
|
-
case "route":
|
|
95
|
-
const { routeCommand } = await import("./commands/route");
|
|
96
|
-
await routeCommand(positionals.slice(1));
|
|
97
|
-
break;
|
|
98
|
-
|
|
99
|
-
case "dev":
|
|
100
|
-
const { devCommand } = await import("./commands/dev");
|
|
101
|
-
await devCommand(positionals.slice(1));
|
|
102
|
-
break;
|
|
103
|
-
|
|
104
|
-
default:
|
|
105
|
-
console.error(pc.red(`Unknown command: ${command}`));
|
|
106
|
-
console.log(`Run ${pc.cyan("donkeylabs --help")} for available commands.`);
|
|
107
|
-
process.exit(1);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
main().catch((error) => {
|
|
112
|
-
console.error(pc.red("Error:"), error.message);
|
|
113
|
-
process.exit(1);
|
|
114
|
-
});
|