@donkeylabs/cli 2.0.19 → 2.0.21
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 +1 -1
- package/src/commands/generate.ts +169 -7
- package/templates/starter/donkeylabs.config.ts +1 -0
- package/templates/starter/package.json +7 -0
- package/templates/starter/src/index.ts +9 -0
- package/templates/starter/src/routes/health/handlers/ping.ts +35 -0
- package/templates/sveltekit-app/src/server/routes/counter/handlers/decrement.ts +19 -0
- package/templates/sveltekit-app/src/server/routes/counter/handlers/get.ts +19 -0
- package/templates/sveltekit-app/src/server/routes/counter/handlers/increment.ts +19 -0
- package/templates/sveltekit-app/src/server/routes/counter/index.ts +11 -0
package/package.json
CHANGED
package/src/commands/generate.ts
CHANGED
|
@@ -70,19 +70,182 @@ async function extractMiddlewareNames(pluginPath: string): Promise<string[]> {
|
|
|
70
70
|
try {
|
|
71
71
|
const content = await readFile(pluginPath, "utf-8");
|
|
72
72
|
|
|
73
|
+
const names = new Set<string>();
|
|
74
|
+
|
|
73
75
|
// Look for middleware definitions: `name: createMiddleware(...)`
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
// Supports generic config: `name: createMiddleware<Config>(...)`
|
|
77
|
+
for (const match of content.matchAll(/(\w+)\s*:\s*createMiddleware\s*(?:<[^>]+>)?\s*\(/g)) {
|
|
78
|
+
if (match[1]) names.add(match[1]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Look for direct middleware objects: `middleware: { ... }`
|
|
82
|
+
for (const match of content.matchAll(/middleware\s*:\s*\{/g)) {
|
|
83
|
+
const openBracePos = (match.index ?? 0) + match[0].length - 1;
|
|
84
|
+
const block = extractBalancedBlock(content, openBracePos, "{", "}");
|
|
85
|
+
for (const key of extractTopLevelObjectKeys(block)) {
|
|
86
|
+
names.add(key);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Look for middleware factory returning object literal directly:
|
|
91
|
+
// `middleware: (ctx, service) => ({ ... })`
|
|
92
|
+
for (const match of content.matchAll(/middleware\s*:\s*\([^)]*\)\s*=>\s*\(\s*\{/g)) {
|
|
93
|
+
const openBracePos = (match.index ?? 0) + match[0].length - 1;
|
|
94
|
+
const block = extractBalancedBlock(content, openBracePos, "{", "}");
|
|
95
|
+
for (const key of extractTopLevelObjectKeys(block)) {
|
|
96
|
+
names.add(key);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Look for middleware factory with block body:
|
|
101
|
+
// `middleware: (...) => { return { ... } }`
|
|
102
|
+
for (const match of content.matchAll(/middleware\s*:\s*\([^)]*\)\s*=>\s*\{/g)) {
|
|
103
|
+
const openBracePos = (match.index ?? 0) + match[0].length - 1;
|
|
104
|
+
const fnBody = extractBalancedBlock(content, openBracePos, "{", "}");
|
|
105
|
+
if (!fnBody) continue;
|
|
106
|
+
|
|
107
|
+
const returnMatch = fnBody.match(/return\s*\{/);
|
|
108
|
+
if (!returnMatch || returnMatch.index === undefined) continue;
|
|
79
109
|
|
|
80
|
-
|
|
110
|
+
const returnObjStart = returnMatch.index + returnMatch[0].length - 1;
|
|
111
|
+
const returnObj = extractBalancedBlock(fnBody, returnObjStart, "{", "}");
|
|
112
|
+
for (const key of extractTopLevelObjectKeys(returnObj)) {
|
|
113
|
+
names.add(key);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return [...names];
|
|
81
118
|
} catch {
|
|
82
119
|
return [];
|
|
83
120
|
}
|
|
84
121
|
}
|
|
85
122
|
|
|
123
|
+
function extractTopLevelObjectKeys(objectBlock: string): string[] {
|
|
124
|
+
if (!objectBlock || !objectBlock.startsWith("{") || !objectBlock.endsWith("}")) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const keys: string[] = [];
|
|
129
|
+
const src = objectBlock.slice(1, -1);
|
|
130
|
+
const len = src.length;
|
|
131
|
+
|
|
132
|
+
let i = 0;
|
|
133
|
+
let braceDepth = 0;
|
|
134
|
+
let bracketDepth = 0;
|
|
135
|
+
let parenDepth = 0;
|
|
136
|
+
|
|
137
|
+
const isIdentifierStart = (ch: string) => /[A-Za-z_$]/.test(ch);
|
|
138
|
+
const isIdentifierPart = (ch: string) => /[A-Za-z0-9_$]/.test(ch);
|
|
139
|
+
|
|
140
|
+
while (i < len) {
|
|
141
|
+
const ch = src[i]!;
|
|
142
|
+
|
|
143
|
+
// Skip strings
|
|
144
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
145
|
+
const quote = ch;
|
|
146
|
+
i++;
|
|
147
|
+
while (i < len) {
|
|
148
|
+
const c = src[i]!;
|
|
149
|
+
if (c === "\\") {
|
|
150
|
+
i += 2;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (c === quote) {
|
|
154
|
+
i++;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
i++;
|
|
158
|
+
}
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Skip comments
|
|
163
|
+
if (ch === "/" && src[i + 1] === "/") {
|
|
164
|
+
i += 2;
|
|
165
|
+
while (i < len && src[i] !== "\n") i++;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (ch === "/" && src[i + 1] === "*") {
|
|
169
|
+
i += 2;
|
|
170
|
+
while (i + 1 < len && !(src[i] === "*" && src[i + 1] === "/")) i++;
|
|
171
|
+
i += 2;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Track nesting
|
|
176
|
+
if (ch === "{") {
|
|
177
|
+
braceDepth++;
|
|
178
|
+
i++;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (ch === "}") {
|
|
182
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
183
|
+
i++;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (ch === "[") {
|
|
187
|
+
bracketDepth++;
|
|
188
|
+
i++;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (ch === "]") {
|
|
192
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
193
|
+
i++;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (ch === "(") {
|
|
197
|
+
parenDepth++;
|
|
198
|
+
i++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (ch === ")") {
|
|
202
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
203
|
+
i++;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Parse top-level keys only
|
|
208
|
+
if (braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
|
|
209
|
+
// Skip whitespace and commas
|
|
210
|
+
if (/\s|,/.test(ch)) {
|
|
211
|
+
i++;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let key = "";
|
|
216
|
+
const keyStart = i;
|
|
217
|
+
|
|
218
|
+
// Identifier key: authRequired: ... or authRequired(...) { ... }
|
|
219
|
+
if (isIdentifierStart(ch)) {
|
|
220
|
+
i++;
|
|
221
|
+
while (i < len && isIdentifierPart(src[i]!)) i++;
|
|
222
|
+
key = src.slice(keyStart, i);
|
|
223
|
+
} else {
|
|
224
|
+
i++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Skip optional marker and whitespace
|
|
229
|
+
while (i < len && /\s/.test(src[i]!)) i++;
|
|
230
|
+
if (src[i] === "?") {
|
|
231
|
+
i++;
|
|
232
|
+
while (i < len && /\s/.test(src[i]!)) i++;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Object property or method shorthand
|
|
236
|
+
if (src[i] === ":" || src[i] === "(") {
|
|
237
|
+
keys.push(key);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
i++;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return keys;
|
|
247
|
+
}
|
|
248
|
+
|
|
86
249
|
interface ServiceDefinitionInfo {
|
|
87
250
|
name: string;
|
|
88
251
|
exportName: string;
|
|
@@ -1270,4 +1433,3 @@ ${eventRegistryEntries.join("\n")}
|
|
|
1270
1433
|
|
|
1271
1434
|
await writeFile(join(outPath, "events.ts"), content);
|
|
1272
1435
|
}
|
|
1273
|
-
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ServerContext } from "@donkeylabs/server";
|
|
2
|
+
|
|
3
|
+
type AppContext = ServerContext;
|
|
4
|
+
|
|
5
|
+
interface Handler<TInput = any, TOutput = any> {
|
|
6
|
+
handle(input: TInput): TOutput | Promise<TOutput>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type PingInput = {
|
|
10
|
+
name: string;
|
|
11
|
+
cool: number;
|
|
12
|
+
echo?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type PingOutput = {
|
|
16
|
+
status: "ok";
|
|
17
|
+
timestamp: string;
|
|
18
|
+
echo?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class PingHandler implements Handler {
|
|
22
|
+
ctx: AppContext;
|
|
23
|
+
|
|
24
|
+
constructor(ctx: AppContext) {
|
|
25
|
+
this.ctx = ctx;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async handle(input: PingInput): Promise<PingOutput> {
|
|
29
|
+
return {
|
|
30
|
+
status: "ok",
|
|
31
|
+
timestamp: new Date().toISOString(),
|
|
32
|
+
echo: input.echo,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ServerContext } from "@donkeylabs/server";
|
|
2
|
+
|
|
3
|
+
type AppContext = ServerContext;
|
|
4
|
+
|
|
5
|
+
interface Handler<TInput = any, TOutput = any> {
|
|
6
|
+
handle(input: TInput): TOutput | Promise<TOutput>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class CounterDecrementHandler implements Handler {
|
|
10
|
+
ctx: AppContext;
|
|
11
|
+
|
|
12
|
+
constructor(ctx: AppContext) {
|
|
13
|
+
this.ctx = ctx;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async handle(): Promise<{ count: number }> {
|
|
17
|
+
return { count: -1 };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ServerContext } from "@donkeylabs/server";
|
|
2
|
+
|
|
3
|
+
type AppContext = ServerContext;
|
|
4
|
+
|
|
5
|
+
interface Handler<TInput = any, TOutput = any> {
|
|
6
|
+
handle(input: TInput): TOutput | Promise<TOutput>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class CounterGetHandler implements Handler {
|
|
10
|
+
ctx: AppContext;
|
|
11
|
+
|
|
12
|
+
constructor(ctx: AppContext) {
|
|
13
|
+
this.ctx = ctx;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async handle(): Promise<{ count: number }> {
|
|
17
|
+
return { count: 0 };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ServerContext } from "@donkeylabs/server";
|
|
2
|
+
|
|
3
|
+
type AppContext = ServerContext;
|
|
4
|
+
|
|
5
|
+
interface Handler<TInput = any, TOutput = any> {
|
|
6
|
+
handle(input: TInput): TOutput | Promise<TOutput>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class CounterIncrementHandler implements Handler {
|
|
10
|
+
ctx: AppContext;
|
|
11
|
+
|
|
12
|
+
constructor(ctx: AppContext) {
|
|
13
|
+
this.ctx = ctx;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async handle(): Promise<{ count: number }> {
|
|
17
|
+
return { count: 1 };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createRouter } from "@donkeylabs/server";
|
|
2
|
+
import { CounterGetHandler } from "./handlers/get";
|
|
3
|
+
import { CounterIncrementHandler } from "./handlers/increment";
|
|
4
|
+
import { CounterDecrementHandler } from "./handlers/decrement";
|
|
5
|
+
|
|
6
|
+
export const counterRouter = createRouter("counter")
|
|
7
|
+
.route("counter.get").typed({ handle: CounterGetHandler })
|
|
8
|
+
.route("counter.increment").typed({ handle: CounterIncrementHandler })
|
|
9
|
+
.route("counter.decrement").typed({ handle: CounterDecrementHandler });
|
|
10
|
+
|
|
11
|
+
export default counterRouter;
|