@donkeylabs/adapter-sveltekit 2.0.6 → 2.0.9
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 +2 -2
- package/src/generator/index.ts +9 -3
- package/src/vite.ts +93 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@donkeylabs/adapter-sveltekit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SvelteKit adapter for @donkeylabs/server - seamless SSR/browser API integration",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -55,4 +55,4 @@
|
|
|
55
55
|
"directory": "packages/adapter-sveltekit"
|
|
56
56
|
},
|
|
57
57
|
"license": "MIT"
|
|
58
|
-
}
|
|
58
|
+
}
|
package/src/generator/index.ts
CHANGED
|
@@ -33,7 +33,7 @@ function isRouteInfo(route: RouteInfo | ExtractedRoute): route is RouteInfo {
|
|
|
33
33
|
/** SvelteKit-specific generator options */
|
|
34
34
|
export const svelteKitGeneratorOptions: ClientGeneratorOptions = {
|
|
35
35
|
baseImport:
|
|
36
|
-
'import { UnifiedApiClientBase, SSEConnection, type ClientOptions, type RequestOptions } from "@donkeylabs/adapter-sveltekit/client";',
|
|
36
|
+
'import { UnifiedApiClientBase, SSEConnection, type ClientOptions, type RequestOptions, type SSEConnectionOptions } from "@donkeylabs/adapter-sveltekit/client";',
|
|
37
37
|
baseClass: "UnifiedApiClientBase",
|
|
38
38
|
constructorSignature: "options?: ClientOptions",
|
|
39
39
|
constructorBody: "super(options);",
|
|
@@ -178,7 +178,7 @@ function generateTypedSvelteKitClient(routes: RouteInfo[]): string {
|
|
|
178
178
|
const outputType = `Routes.${pascalNs}.${pascalRoute}.Output`;
|
|
179
179
|
// Use original route name with prefix for the request
|
|
180
180
|
const fullRouteName = commonPrefix ? `${commonPrefix}.${r.name}` : r.name;
|
|
181
|
-
return ` ${methodName}: (input: ${inputType}): Promise<${outputType}> => this.request("${fullRouteName}", input)`;
|
|
181
|
+
return ` ${methodName}: (input: ${inputType}, options?: RequestOptions): Promise<${outputType}> => this.request("${fullRouteName}", input, options)`;
|
|
182
182
|
});
|
|
183
183
|
|
|
184
184
|
const rawMethodEntries = nsRoutes
|
|
@@ -220,7 +220,13 @@ function generateTypedSvelteKitClient(routes: RouteInfo[]): string {
|
|
|
220
220
|
const eventsType = `Routes.${pascalNs}.${pascalRoute}.Events`;
|
|
221
221
|
const fullRouteName = commonPrefix ? `${commonPrefix}.${r.name}` : r.name;
|
|
222
222
|
// SSE returns typed SSEConnection for type-safe event handling
|
|
223
|
-
|
|
223
|
+
// With input: (input, options?) => sseConnect(route, input, options)
|
|
224
|
+
// Without input: (options?) => sseConnect(route, undefined, options)
|
|
225
|
+
if (hasInput) {
|
|
226
|
+
return ` ${methodName}: (input: ${inputType}, options?: SSEConnectionOptions): SSEConnection<${eventsType}> => this.sseConnect("${fullRouteName}", input, options)`;
|
|
227
|
+
} else {
|
|
228
|
+
return ` ${methodName}: (options?: SSEConnectionOptions): SSEConnection<${eventsType}> => this.sseConnect("${fullRouteName}", undefined, options)`;
|
|
229
|
+
}
|
|
224
230
|
});
|
|
225
231
|
|
|
226
232
|
const formDataMethodEntries = nsRoutes
|
package/src/vite.ts
CHANGED
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Plugin, ViteDevServer } from "vite";
|
|
10
|
-
import { spawn, type ChildProcess } from "node:child_process";
|
|
11
|
-
import { resolve } from "node:path";
|
|
10
|
+
import { spawn, type ChildProcess, exec } from "node:child_process";
|
|
11
|
+
import { resolve, join } from "node:path";
|
|
12
|
+
import { watch, type FSWatcher } from "node:fs";
|
|
13
|
+
import { promisify } from "node:util";
|
|
14
|
+
|
|
15
|
+
const execAsync = promisify(exec);
|
|
12
16
|
import http from "node:http";
|
|
13
17
|
|
|
14
18
|
export interface DevPluginOptions {
|
|
@@ -25,6 +29,18 @@ export interface DevPluginOptions {
|
|
|
25
29
|
* @default 3001
|
|
26
30
|
*/
|
|
27
31
|
backendPort?: number;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Watch server files and auto-regenerate types on changes.
|
|
35
|
+
* @default true
|
|
36
|
+
*/
|
|
37
|
+
watchTypes?: boolean;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Directory to watch for server file changes.
|
|
41
|
+
* @default "./src/server"
|
|
42
|
+
*/
|
|
43
|
+
watchDir?: string;
|
|
28
44
|
}
|
|
29
45
|
|
|
30
46
|
// Check if running with Bun runtime (bun --bun)
|
|
@@ -66,7 +82,12 @@ function setDevServer(server: any) {
|
|
|
66
82
|
* });
|
|
67
83
|
*/
|
|
68
84
|
export function donkeylabsDev(options: DevPluginOptions = {}): Plugin {
|
|
69
|
-
const {
|
|
85
|
+
const {
|
|
86
|
+
serverEntry = "./src/server/index.ts",
|
|
87
|
+
backendPort = 3001,
|
|
88
|
+
watchTypes = true,
|
|
89
|
+
watchDir = "./src/server",
|
|
90
|
+
} = options;
|
|
70
91
|
|
|
71
92
|
// State for subprocess mode
|
|
72
93
|
let backendProcess: ChildProcess | null = null;
|
|
@@ -76,6 +97,71 @@ export function donkeylabsDev(options: DevPluginOptions = {}): Plugin {
|
|
|
76
97
|
let appServer: any = null;
|
|
77
98
|
let serverReady = false;
|
|
78
99
|
|
|
100
|
+
// State for file watcher
|
|
101
|
+
let fileWatcher: FSWatcher | null = null;
|
|
102
|
+
let isGenerating = false;
|
|
103
|
+
let lastGenerationTime = 0;
|
|
104
|
+
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
105
|
+
|
|
106
|
+
const COOLDOWN_MS = 2000;
|
|
107
|
+
const DEBOUNCE_MS = 500;
|
|
108
|
+
|
|
109
|
+
// Patterns to ignore (generated files)
|
|
110
|
+
const IGNORED_PATTERNS = [/schema\.ts$/, /\.d\.ts$/];
|
|
111
|
+
|
|
112
|
+
function shouldIgnoreFile(filename: string): boolean {
|
|
113
|
+
return IGNORED_PATTERNS.some((pattern) => pattern.test(filename));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function regenerateTypes() {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
if (now - lastGenerationTime < COOLDOWN_MS || isGenerating) return;
|
|
119
|
+
|
|
120
|
+
isGenerating = true;
|
|
121
|
+
lastGenerationTime = now;
|
|
122
|
+
console.log("\x1b[36m[donkeylabs-dev]\x1b[0m Server files changed, regenerating types...");
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
await execAsync("bunx donkeylabs generate");
|
|
126
|
+
console.log("\x1b[32m[donkeylabs-dev]\x1b[0m Types regenerated successfully");
|
|
127
|
+
} catch (e: any) {
|
|
128
|
+
console.error("\x1b[31m[donkeylabs-dev]\x1b[0m Error regenerating types:", e.message);
|
|
129
|
+
} finally {
|
|
130
|
+
isGenerating = false;
|
|
131
|
+
lastGenerationTime = Date.now();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function debouncedRegenerate() {
|
|
136
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
137
|
+
debounceTimer = setTimeout(regenerateTypes, DEBOUNCE_MS);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function startFileWatcher() {
|
|
141
|
+
if (!watchTypes || fileWatcher) return;
|
|
142
|
+
|
|
143
|
+
const watchPath = resolve(process.cwd(), watchDir);
|
|
144
|
+
try {
|
|
145
|
+
fileWatcher = watch(watchPath, { recursive: true }, (_eventType, filename) => {
|
|
146
|
+
if (!filename) return;
|
|
147
|
+
if (!filename.endsWith(".ts")) return;
|
|
148
|
+
if (shouldIgnoreFile(filename)) return;
|
|
149
|
+
debouncedRegenerate();
|
|
150
|
+
});
|
|
151
|
+
console.log(`\x1b[36m[donkeylabs-dev]\x1b[0m Watching ${watchDir} for changes...`);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
console.warn(`\x1b[33m[donkeylabs-dev]\x1b[0m Could not watch ${watchDir}:`, err);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function stopFileWatcher() {
|
|
158
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
159
|
+
if (fileWatcher) {
|
|
160
|
+
fileWatcher.close();
|
|
161
|
+
fileWatcher = null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
79
165
|
return {
|
|
80
166
|
name: "donkeylabs-dev",
|
|
81
167
|
enforce: "pre",
|
|
@@ -97,6 +183,9 @@ export function donkeylabsDev(options: DevPluginOptions = {}): Plugin {
|
|
|
97
183
|
async configureServer(server: ViteDevServer) {
|
|
98
184
|
const serverEntryResolved = resolve(process.cwd(), serverEntry);
|
|
99
185
|
|
|
186
|
+
// Start file watcher for auto type regeneration
|
|
187
|
+
startFileWatcher();
|
|
188
|
+
|
|
100
189
|
if (isBunRuntime) {
|
|
101
190
|
// ========== IN-PROCESS MODE (bun --bun run dev) ==========
|
|
102
191
|
// Import and initialize server directly - no subprocess, no proxy
|
|
@@ -486,6 +575,7 @@ export function donkeylabsDev(options: DevPluginOptions = {}): Plugin {
|
|
|
486
575
|
},
|
|
487
576
|
|
|
488
577
|
async closeBundle() {
|
|
578
|
+
stopFileWatcher();
|
|
489
579
|
if (backendProcess) {
|
|
490
580
|
backendProcess.kill();
|
|
491
581
|
backendProcess = null;
|