@donkeylabs/adapter-sveltekit 2.0.14 → 2.0.16
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/dist/client/index.d.ts +231 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +501 -0
- package/dist/generator/index.d.ts +17 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +330 -0
- package/dist/hooks/index.d.ts +53 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +96 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +51 -120
- package/dist/vite.d.ts +71 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +611 -0
- package/package.json +16 -14
- package/src/client/index.ts +0 -659
- package/src/generator/index.ts +0 -401
- package/src/hooks/index.ts +0 -124
- package/src/vite.ts +0 -729
package/src/generator/index.ts
DELETED
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SvelteKit-specific client generator
|
|
3
|
-
*
|
|
4
|
-
* This generator extends the core @donkeylabs/server generator
|
|
5
|
-
* to produce clients that work with both SSR (direct calls) and browser (HTTP).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
9
|
-
import { dirname } from "node:path";
|
|
10
|
-
import {
|
|
11
|
-
generateClientCode,
|
|
12
|
-
zodToTypeScript,
|
|
13
|
-
toPascalCase,
|
|
14
|
-
toCamelCase,
|
|
15
|
-
type RouteInfo,
|
|
16
|
-
type ExtractedRoute,
|
|
17
|
-
type ClientGeneratorOptions,
|
|
18
|
-
} from "@donkeylabs/server/generator";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Type guard to check if a route is a full RouteInfo (with prefix and routeName)
|
|
22
|
-
*/
|
|
23
|
-
function isRouteInfo(route: RouteInfo | ExtractedRoute): route is RouteInfo {
|
|
24
|
-
return (
|
|
25
|
-
typeof route === "object" &&
|
|
26
|
-
route !== null &&
|
|
27
|
-
"prefix" in route &&
|
|
28
|
-
"routeName" in route &&
|
|
29
|
-
typeof (route as RouteInfo).prefix === "string"
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** SvelteKit-specific generator options */
|
|
34
|
-
export const svelteKitGeneratorOptions: ClientGeneratorOptions = {
|
|
35
|
-
baseImport:
|
|
36
|
-
'import { UnifiedApiClientBase, SSEConnection, type ClientOptions, type RequestOptions, type SSEConnectionOptions } from "@donkeylabs/adapter-sveltekit/client";',
|
|
37
|
-
baseClass: "UnifiedApiClientBase",
|
|
38
|
-
constructorSignature: "options?: ClientOptions",
|
|
39
|
-
constructorBody: "super(options);",
|
|
40
|
-
factoryFunction: `/**
|
|
41
|
-
* Create an API client instance
|
|
42
|
-
*
|
|
43
|
-
* @param options.locals - Pass SvelteKit locals for SSR direct calls (no HTTP overhead)
|
|
44
|
-
* @param options.baseUrl - Override the base URL for HTTP calls
|
|
45
|
-
*
|
|
46
|
-
* @example SSR usage in +page.server.ts:
|
|
47
|
-
* \`\`\`ts
|
|
48
|
-
* export const load = async ({ locals }) => {
|
|
49
|
-
* const api = createApi({ locals });
|
|
50
|
-
* const data = await api.myRoute.get({}); // Direct call, no HTTP!
|
|
51
|
-
* return { data };
|
|
52
|
-
* };
|
|
53
|
-
* \`\`\`
|
|
54
|
-
*
|
|
55
|
-
* @example Browser usage in +page.svelte:
|
|
56
|
-
* \`\`\`svelte
|
|
57
|
-
* <script>
|
|
58
|
-
* import { createApi } from '$lib/api';
|
|
59
|
-
* const api = createApi(); // HTTP calls
|
|
60
|
-
* let data = $state(null);
|
|
61
|
-
* async function load() {
|
|
62
|
-
* data = await api.myRoute.get({});
|
|
63
|
-
* }
|
|
64
|
-
* </script>
|
|
65
|
-
* \`\`\`
|
|
66
|
-
*/
|
|
67
|
-
export function createApi(options?: ClientOptions) {
|
|
68
|
-
return new ApiClient(options);
|
|
69
|
-
}`,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Namespace tree node for building nested client structure
|
|
74
|
-
*/
|
|
75
|
-
interface NamespaceTreeNode {
|
|
76
|
-
methods: { methodDef: string; typeDef: string }[];
|
|
77
|
-
children: Map<string, NamespaceTreeNode>;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Build a nested tree structure from routes
|
|
82
|
-
* e.g., routes "api.counter.get", "api.cache.set" become:
|
|
83
|
-
* api -> { counter -> { get }, cache -> { set } }
|
|
84
|
-
*/
|
|
85
|
-
function buildRouteTree(routes: RouteInfo[], commonPrefix: string): Map<string, NamespaceTreeNode> {
|
|
86
|
-
const tree = new Map<string, NamespaceTreeNode>();
|
|
87
|
-
|
|
88
|
-
for (const route of routes) {
|
|
89
|
-
// Get the path parts for nesting (e.g., "api.counter.get" -> ["api", "counter", "get"])
|
|
90
|
-
const parts = route.name.split(".");
|
|
91
|
-
const methodName = parts[parts.length - 1]!; // Last part is the method
|
|
92
|
-
const namespaceParts = parts.slice(0, -1); // Everything before is namespace path
|
|
93
|
-
|
|
94
|
-
if (namespaceParts.length === 0) {
|
|
95
|
-
// Root level method
|
|
96
|
-
if (!tree.has("_root")) {
|
|
97
|
-
tree.set("_root", { methods: [], children: new Map() });
|
|
98
|
-
}
|
|
99
|
-
tree.get("_root")!.methods.push(generateMethodAndType(route, methodName, "Root", commonPrefix));
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Navigate/create the tree path
|
|
104
|
-
let current = tree;
|
|
105
|
-
for (let i = 0; i < namespaceParts.length; i++) {
|
|
106
|
-
const part = namespaceParts[i]!;
|
|
107
|
-
if (!current.has(part)) {
|
|
108
|
-
current.set(part, { methods: [], children: new Map() });
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (i === namespaceParts.length - 1) {
|
|
112
|
-
// At the final namespace level - add the method here
|
|
113
|
-
const pascalNs = toPascalCase(namespaceParts.join("."));
|
|
114
|
-
current.get(part)!.methods.push(generateMethodAndType(route, methodName, pascalNs, commonPrefix));
|
|
115
|
-
} else {
|
|
116
|
-
// Continue traversing
|
|
117
|
-
current = current.get(part)!.children;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return tree;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Generate method definition and type definition for a route
|
|
127
|
-
*/
|
|
128
|
-
function generateMethodAndType(
|
|
129
|
-
route: RouteInfo,
|
|
130
|
-
methodName: string,
|
|
131
|
-
pascalNs: string,
|
|
132
|
-
commonPrefix: string
|
|
133
|
-
): { methodDef: string; typeDef: string } {
|
|
134
|
-
const camelMethod = toCamelCase(methodName);
|
|
135
|
-
const pascalRoute = toPascalCase(methodName);
|
|
136
|
-
const fullRouteName = route.name; // Already includes full path
|
|
137
|
-
|
|
138
|
-
// Generate input type
|
|
139
|
-
const inputType = route.inputSource
|
|
140
|
-
? (route.inputSource.trim().startsWith("z.") ? zodToTypeScript(route.inputSource) : route.inputSource)
|
|
141
|
-
: "Record<string, never>";
|
|
142
|
-
|
|
143
|
-
// Generate type definition
|
|
144
|
-
let typeDef = "";
|
|
145
|
-
let methodDef = "";
|
|
146
|
-
|
|
147
|
-
if (route.handler === "stream" || route.handler === "html") {
|
|
148
|
-
typeDef = ` export namespace ${pascalRoute} {
|
|
149
|
-
export type Input = Expand<${inputType}>;
|
|
150
|
-
}
|
|
151
|
-
export type ${pascalRoute} = { Input: ${pascalRoute}.Input };`;
|
|
152
|
-
|
|
153
|
-
if (route.handler === "stream") {
|
|
154
|
-
methodDef = `${camelMethod}: {
|
|
155
|
-
/** POST request with JSON body (programmatic) */
|
|
156
|
-
fetch: (input: Routes.${pascalNs}.${pascalRoute}.Input, options?: RequestOptions): Promise<Response> => this.streamRequest("${fullRouteName}", input, options),
|
|
157
|
-
/** GET URL for browser src attributes (video, img, download links) */
|
|
158
|
-
url: (input: Routes.${pascalNs}.${pascalRoute}.Input): string => this.streamUrl("${fullRouteName}", input),
|
|
159
|
-
/** GET request with query params */
|
|
160
|
-
get: (input: Routes.${pascalNs}.${pascalRoute}.Input, options?: RequestOptions): Promise<Response> => this.streamGet("${fullRouteName}", input, options),
|
|
161
|
-
}`;
|
|
162
|
-
} else {
|
|
163
|
-
const hasInput = route.inputSource;
|
|
164
|
-
methodDef = `${camelMethod}: (${hasInput ? `input: Routes.${pascalNs}.${pascalRoute}.Input` : ""}): Promise<string> => this.htmlRequest("${fullRouteName}"${hasInput ? ", input" : ""})`;
|
|
165
|
-
}
|
|
166
|
-
} else if (route.handler === "sse") {
|
|
167
|
-
const eventsEntries = route.eventsSource
|
|
168
|
-
? Object.entries(route.eventsSource).map(([eventName, eventSchema]) => {
|
|
169
|
-
const eventType = eventSchema.trim().startsWith("z.")
|
|
170
|
-
? zodToTypeScript(eventSchema)
|
|
171
|
-
: eventSchema;
|
|
172
|
-
return ` "${eventName}": Expand<${eventType}>;`;
|
|
173
|
-
})
|
|
174
|
-
: [];
|
|
175
|
-
const eventsType = eventsEntries.length > 0
|
|
176
|
-
? `{\n${eventsEntries.join("\n")}\n }`
|
|
177
|
-
: "Record<string, unknown>";
|
|
178
|
-
|
|
179
|
-
typeDef = ` export namespace ${pascalRoute} {
|
|
180
|
-
export type Input = Expand<${inputType}>;
|
|
181
|
-
export type Events = ${eventsType};
|
|
182
|
-
}
|
|
183
|
-
export type ${pascalRoute} = { Input: ${pascalRoute}.Input; Events: ${pascalRoute}.Events };`;
|
|
184
|
-
|
|
185
|
-
const hasInput = route.inputSource;
|
|
186
|
-
if (hasInput) {
|
|
187
|
-
methodDef = `${camelMethod}: (input: Routes.${pascalNs}.${pascalRoute}.Input, options?: SSEConnectionOptions): SSEConnection<Routes.${pascalNs}.${pascalRoute}.Events> => this.sseConnect("${fullRouteName}", input, options)`;
|
|
188
|
-
} else {
|
|
189
|
-
methodDef = `${camelMethod}: (options?: SSEConnectionOptions): SSEConnection<Routes.${pascalNs}.${pascalRoute}.Events> => this.sseConnect("${fullRouteName}", undefined, options)`;
|
|
190
|
-
}
|
|
191
|
-
} else if (route.handler === "raw") {
|
|
192
|
-
typeDef = ""; // Raw routes don't have types
|
|
193
|
-
methodDef = `${camelMethod}: (init?: RequestInit): Promise<Response> => this.rawRequest("${fullRouteName}", init)`;
|
|
194
|
-
} else if (route.handler === "formData") {
|
|
195
|
-
const outputType = route.outputSource
|
|
196
|
-
? (route.outputSource.trim().startsWith("z.") ? zodToTypeScript(route.outputSource) : route.outputSource)
|
|
197
|
-
: "unknown";
|
|
198
|
-
|
|
199
|
-
typeDef = ` export namespace ${pascalRoute} {
|
|
200
|
-
export type Input = Expand<${inputType}>;
|
|
201
|
-
export type Output = Expand<${outputType}>;
|
|
202
|
-
}
|
|
203
|
-
export type ${pascalRoute} = { Input: ${pascalRoute}.Input; Output: ${pascalRoute}.Output };`;
|
|
204
|
-
|
|
205
|
-
methodDef = `${camelMethod}: (fields: Routes.${pascalNs}.${pascalRoute}.Input, files: File[]): Promise<Routes.${pascalNs}.${pascalRoute}.Output> => this.formDataRequest("${fullRouteName}", fields, files)`;
|
|
206
|
-
} else {
|
|
207
|
-
// typed handler (default)
|
|
208
|
-
const outputType = route.outputSource
|
|
209
|
-
? (route.outputSource.trim().startsWith("z.") ? zodToTypeScript(route.outputSource) : route.outputSource)
|
|
210
|
-
: "unknown";
|
|
211
|
-
|
|
212
|
-
typeDef = ` export namespace ${pascalRoute} {
|
|
213
|
-
export type Input = Expand<${inputType}>;
|
|
214
|
-
export type Output = Expand<${outputType}>;
|
|
215
|
-
}
|
|
216
|
-
export type ${pascalRoute} = { Input: ${pascalRoute}.Input; Output: ${pascalRoute}.Output };`;
|
|
217
|
-
|
|
218
|
-
methodDef = `${camelMethod}: (input: Routes.${pascalNs}.${pascalRoute}.Input, options?: RequestOptions): Promise<Routes.${pascalNs}.${pascalRoute}.Output> => this.request("${fullRouteName}", input, options)`;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return { methodDef, typeDef };
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Generate nested object code from a tree node
|
|
226
|
-
*/
|
|
227
|
-
function generateNestedMethods(node: NamespaceTreeNode, indent: string = " "): string {
|
|
228
|
-
const parts: string[] = [];
|
|
229
|
-
|
|
230
|
-
// Add methods at this level
|
|
231
|
-
for (const { methodDef } of node.methods) {
|
|
232
|
-
// Indent each line of the method definition
|
|
233
|
-
const indented = methodDef.split("\n").map((line, i) =>
|
|
234
|
-
i === 0 ? `${indent}${line}` : `${indent}${line}`
|
|
235
|
-
).join("\n");
|
|
236
|
-
parts.push(indented);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Add nested namespaces
|
|
240
|
-
for (const [childName, childNode] of node.children) {
|
|
241
|
-
const childContent = generateNestedMethods(childNode, indent + " ");
|
|
242
|
-
parts.push(`${indent}${childName}: {\n${childContent}\n${indent}}`);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return parts.join(",\n");
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Collect all type definitions from a tree
|
|
250
|
-
*/
|
|
251
|
-
function collectTypeDefs(tree: Map<string, NamespaceTreeNode>, prefix: string = ""): Map<string, string[]> {
|
|
252
|
-
const result = new Map<string, string[]>();
|
|
253
|
-
|
|
254
|
-
for (const [name, node] of tree) {
|
|
255
|
-
const nsPath = prefix ? `${prefix}.${name}` : name;
|
|
256
|
-
const pascalNs = name === "_root" ? "Root" : toPascalCase(nsPath);
|
|
257
|
-
|
|
258
|
-
// Collect types from this node's methods
|
|
259
|
-
const typeDefs = node.methods
|
|
260
|
-
.map(m => m.typeDef)
|
|
261
|
-
.filter(t => t.length > 0);
|
|
262
|
-
|
|
263
|
-
if (typeDefs.length > 0) {
|
|
264
|
-
if (!result.has(pascalNs)) {
|
|
265
|
-
result.set(pascalNs, []);
|
|
266
|
-
}
|
|
267
|
-
result.get(pascalNs)!.push(...typeDefs);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Recursively collect from children
|
|
271
|
-
const childTypes = collectTypeDefs(node.children, nsPath);
|
|
272
|
-
for (const [childNs, childDefs] of childTypes) {
|
|
273
|
-
if (!result.has(childNs)) {
|
|
274
|
-
result.set(childNs, []);
|
|
275
|
-
}
|
|
276
|
-
result.get(childNs)!.push(...childDefs);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return result;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Generate a fully-typed SvelteKit-compatible API client
|
|
285
|
-
*/
|
|
286
|
-
function generateTypedSvelteKitClient(routes: RouteInfo[]): string {
|
|
287
|
-
const opts = svelteKitGeneratorOptions;
|
|
288
|
-
const commonPrefix = ""; // We don't strip prefixes anymore - nested structure handles it
|
|
289
|
-
|
|
290
|
-
// Build nested tree structure from routes
|
|
291
|
-
const tree = buildRouteTree(routes, commonPrefix);
|
|
292
|
-
|
|
293
|
-
// Collect type definitions from tree
|
|
294
|
-
const typesByNamespace = collectTypeDefs(tree);
|
|
295
|
-
const typeBlocks: string[] = [];
|
|
296
|
-
for (const [nsName, typeDefs] of typesByNamespace) {
|
|
297
|
-
if (typeDefs.length > 0) {
|
|
298
|
-
typeBlocks.push(` export namespace ${nsName} {\n${typeDefs.join("\n\n")}\n }`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Generate method blocks from tree
|
|
303
|
-
const methodBlocks: string[] = [];
|
|
304
|
-
for (const [topLevel, node] of tree) {
|
|
305
|
-
if (topLevel === "_root") {
|
|
306
|
-
// Root level methods become direct class properties
|
|
307
|
-
for (const { methodDef } of node.methods) {
|
|
308
|
-
methodBlocks.push(` ${methodDef};`);
|
|
309
|
-
}
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const content = generateNestedMethods(node, " ");
|
|
314
|
-
methodBlocks.push(` ${topLevel} = {\n${content}\n };`);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return `// Auto-generated by donkeylabs generate
|
|
318
|
-
// DO NOT EDIT MANUALLY
|
|
319
|
-
|
|
320
|
-
${opts.baseImport}
|
|
321
|
-
|
|
322
|
-
// Utility type that forces TypeScript to expand types on hover
|
|
323
|
-
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
|
324
|
-
|
|
325
|
-
// ============================================
|
|
326
|
-
// Route Types
|
|
327
|
-
// ============================================
|
|
328
|
-
|
|
329
|
-
export namespace Routes {
|
|
330
|
-
${typeBlocks.join("\n\n") || " // No typed routes found"}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// ============================================
|
|
334
|
-
// API Client
|
|
335
|
-
// ============================================
|
|
336
|
-
|
|
337
|
-
export class ApiClient extends ${opts.baseClass} {
|
|
338
|
-
constructor(${opts.constructorSignature}) {
|
|
339
|
-
${opts.constructorBody}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
${methodBlocks.join("\n\n") || " // No routes defined"}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
${opts.factoryFunction}
|
|
346
|
-
`;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Generate a SvelteKit-compatible API client
|
|
351
|
-
*
|
|
352
|
-
* This is called by the donkeylabs CLI when adapter is set to "@donkeylabs/adapter-sveltekit"
|
|
353
|
-
*/
|
|
354
|
-
export async function generateClient(
|
|
355
|
-
_config: Record<string, unknown>,
|
|
356
|
-
routes: RouteInfo[] | ExtractedRoute[],
|
|
357
|
-
outputPath: string
|
|
358
|
-
): Promise<void> {
|
|
359
|
-
let code: string;
|
|
360
|
-
|
|
361
|
-
// Always try typed generation if we have routes
|
|
362
|
-
if (routes.length > 0 && isRouteInfo(routes[0])) {
|
|
363
|
-
// Full RouteInfo - generate typed client
|
|
364
|
-
code = generateTypedSvelteKitClient(routes as RouteInfo[]);
|
|
365
|
-
} else if (routes.length > 0) {
|
|
366
|
-
// Convert ExtractedRoute to RouteInfo for typed generation
|
|
367
|
-
const routeInfos: RouteInfo[] = (routes as ExtractedRoute[]).map((r) => {
|
|
368
|
-
const parts = r.name.split(".");
|
|
369
|
-
return {
|
|
370
|
-
name: r.name,
|
|
371
|
-
prefix: parts.slice(0, -1).join("."),
|
|
372
|
-
routeName: parts[parts.length - 1] || r.name,
|
|
373
|
-
handler: (r.handler || "typed") as "typed" | "raw",
|
|
374
|
-
inputSource: undefined,
|
|
375
|
-
outputSource: undefined,
|
|
376
|
-
};
|
|
377
|
-
});
|
|
378
|
-
code = generateTypedSvelteKitClient(routeInfos);
|
|
379
|
-
} else {
|
|
380
|
-
// Empty routes - generate minimal client
|
|
381
|
-
code = generateTypedSvelteKitClient([]);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Ensure output directory exists
|
|
385
|
-
const outputDir = dirname(outputPath);
|
|
386
|
-
await mkdir(outputDir, { recursive: true });
|
|
387
|
-
|
|
388
|
-
// Write the generated client
|
|
389
|
-
await writeFile(outputPath, code);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Re-export building blocks for advanced usage
|
|
393
|
-
export {
|
|
394
|
-
generateClientCode,
|
|
395
|
-
zodToTypeScript,
|
|
396
|
-
toPascalCase,
|
|
397
|
-
toCamelCase,
|
|
398
|
-
type RouteInfo,
|
|
399
|
-
type ExtractedRoute,
|
|
400
|
-
type ClientGeneratorOptions,
|
|
401
|
-
} from "@donkeylabs/server/generator";
|
package/src/hooks/index.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SvelteKit hooks helper for @donkeylabs/adapter-sveltekit
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { Handle } from "@sveltejs/kit";
|
|
6
|
-
|
|
7
|
-
// Try to import dev server reference (only available in dev mode)
|
|
8
|
-
let getDevServer: (() => any) | undefined;
|
|
9
|
-
try {
|
|
10
|
-
// Dynamic import to avoid bundling vite.ts in production
|
|
11
|
-
const viteModule = await import("../vite.js");
|
|
12
|
-
getDevServer = viteModule.getDevServer;
|
|
13
|
-
} catch {
|
|
14
|
-
// Not in dev mode or vite not available
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface DonkeylabsPlatform {
|
|
18
|
-
donkeylabs?: {
|
|
19
|
-
services: Record<string, any>;
|
|
20
|
-
core: {
|
|
21
|
-
logger: any;
|
|
22
|
-
cache: any;
|
|
23
|
-
events: any;
|
|
24
|
-
cron: any;
|
|
25
|
-
jobs: any;
|
|
26
|
-
sse: any;
|
|
27
|
-
rateLimiter: any;
|
|
28
|
-
db: any;
|
|
29
|
-
};
|
|
30
|
-
/** Direct route handler for SSR (no HTTP!) */
|
|
31
|
-
handleRoute: (routeName: string, input: any) => Promise<any>;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface DonkeylabsLocals {
|
|
36
|
-
plugins: Record<string, any>;
|
|
37
|
-
core: {
|
|
38
|
-
logger: any;
|
|
39
|
-
cache: any;
|
|
40
|
-
events: any;
|
|
41
|
-
sse: any;
|
|
42
|
-
};
|
|
43
|
-
db: any;
|
|
44
|
-
ip: string;
|
|
45
|
-
/** Direct route handler for SSR API calls */
|
|
46
|
-
handleRoute?: (routeName: string, input: any) => Promise<any>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create a SvelteKit handle function that populates event.locals
|
|
51
|
-
* with @donkeylabs/server context.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* // src/hooks.server.ts
|
|
55
|
-
* import { createHandle } from "@donkeylabs/adapter-sveltekit/hooks";
|
|
56
|
-
* export const handle = createHandle();
|
|
57
|
-
*/
|
|
58
|
-
export function createHandle(): Handle {
|
|
59
|
-
return async ({ event, resolve }) => {
|
|
60
|
-
const platform = event.platform as DonkeylabsPlatform | undefined;
|
|
61
|
-
|
|
62
|
-
if (platform?.donkeylabs) {
|
|
63
|
-
// Production mode: use platform.donkeylabs from adapter
|
|
64
|
-
const { services, core, handleRoute } = platform.donkeylabs;
|
|
65
|
-
|
|
66
|
-
// Populate locals with server context
|
|
67
|
-
(event.locals as DonkeylabsLocals).plugins = services;
|
|
68
|
-
(event.locals as DonkeylabsLocals).core = {
|
|
69
|
-
logger: core.logger,
|
|
70
|
-
cache: core.cache,
|
|
71
|
-
events: core.events,
|
|
72
|
-
sse: core.sse,
|
|
73
|
-
};
|
|
74
|
-
(event.locals as DonkeylabsLocals).db = core.db;
|
|
75
|
-
(event.locals as DonkeylabsLocals).ip = event.getClientAddress();
|
|
76
|
-
// Expose the direct route handler for SSR API calls
|
|
77
|
-
(event.locals as DonkeylabsLocals).handleRoute = handleRoute;
|
|
78
|
-
} else if (getDevServer) {
|
|
79
|
-
// Dev mode: use global dev server from vite plugin
|
|
80
|
-
const devServer = getDevServer();
|
|
81
|
-
if (devServer) {
|
|
82
|
-
const core = devServer.getCore();
|
|
83
|
-
const plugins = devServer.getServices();
|
|
84
|
-
|
|
85
|
-
(event.locals as DonkeylabsLocals).plugins = plugins;
|
|
86
|
-
(event.locals as DonkeylabsLocals).core = {
|
|
87
|
-
logger: core.logger,
|
|
88
|
-
cache: core.cache,
|
|
89
|
-
events: core.events,
|
|
90
|
-
sse: core.sse,
|
|
91
|
-
};
|
|
92
|
-
(event.locals as DonkeylabsLocals).db = core.db;
|
|
93
|
-
(event.locals as DonkeylabsLocals).ip = event.getClientAddress();
|
|
94
|
-
// Direct route handler for SSR
|
|
95
|
-
(event.locals as DonkeylabsLocals).handleRoute = async (routeName: string, input: any) => {
|
|
96
|
-
return devServer.callRoute(routeName, input, event.getClientAddress());
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return resolve(event);
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Sequence multiple handle functions together.
|
|
107
|
-
*
|
|
108
|
-
* @example
|
|
109
|
-
* import { sequence, createHandle } from "@donkeylabs/adapter-sveltekit/hooks";
|
|
110
|
-
* export const handle = sequence(createHandle(), myOtherHandle);
|
|
111
|
-
*/
|
|
112
|
-
export function sequence(...handlers: Handle[]): Handle {
|
|
113
|
-
return async ({ event, resolve }) => {
|
|
114
|
-
let resolveChain = resolve;
|
|
115
|
-
|
|
116
|
-
for (let i = handlers.length - 1; i >= 0; i--) {
|
|
117
|
-
const handler = handlers[i];
|
|
118
|
-
const next = resolveChain;
|
|
119
|
-
resolveChain = (event) => handler({ event, resolve: next });
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return resolveChain(event);
|
|
123
|
-
};
|
|
124
|
-
}
|