@donkeylabs/adapter-sveltekit 2.0.15 → 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/dist/index.js +322 -0
- package/dist/vite.d.ts +71 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +611 -0
- package/package.json +2 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @donkeylabs/adapter-sveltekit
|
|
3
|
+
*
|
|
4
|
+
* SvelteKit adapter that integrates with @donkeylabs/server.
|
|
5
|
+
* - Single Bun process serves both SvelteKit pages and API routes
|
|
6
|
+
* - Direct service calls during SSR (no HTTP overhead)
|
|
7
|
+
* - Unified API client works in both SSR and browser
|
|
8
|
+
*/
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
11
|
+
import { writeFileSync, mkdirSync } from "node:fs";
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
export default function adapter(options = {}) {
|
|
15
|
+
const { out = "build", serverEntry = "./src/server/index.ts", precompress = true, envPrefix = "", development = false, } = options;
|
|
16
|
+
return {
|
|
17
|
+
name: "@donkeylabs/adapter-sveltekit",
|
|
18
|
+
async adapt(builder) {
|
|
19
|
+
const serverDir = join(out, "server");
|
|
20
|
+
const clientDir = join(out, "client");
|
|
21
|
+
const prerenderedDir = join(out, "prerendered");
|
|
22
|
+
// 1. Clean and create output directories
|
|
23
|
+
builder.rimraf(out);
|
|
24
|
+
mkdirSync(serverDir, { recursive: true });
|
|
25
|
+
builder.log.minor("Writing SvelteKit server files...");
|
|
26
|
+
// 2. Write SvelteKit server files
|
|
27
|
+
builder.writeServer(serverDir);
|
|
28
|
+
// 3. Copy static assets and prerendered pages
|
|
29
|
+
const clientFiles = builder.writeClient(clientDir);
|
|
30
|
+
const prerenderedFiles = builder.writePrerendered(prerenderedDir);
|
|
31
|
+
// 4. Generate the manifest
|
|
32
|
+
const relativePath = relative(serverDir, ".");
|
|
33
|
+
builder.generateManifest({ relativePath });
|
|
34
|
+
// 5. Generate the unified runtime entry point
|
|
35
|
+
const serverEntryResolved = resolve(serverEntry);
|
|
36
|
+
const serverEntryRelative = relative(serverDir, serverEntryResolved);
|
|
37
|
+
const entryCode = generateEntryPoint({
|
|
38
|
+
serverEntryRelative,
|
|
39
|
+
envPrefix,
|
|
40
|
+
development,
|
|
41
|
+
clientDir: relative(serverDir, clientDir),
|
|
42
|
+
prerenderedDir: relative(serverDir, prerenderedDir),
|
|
43
|
+
});
|
|
44
|
+
writeFileSync(join(serverDir, "entry.js"), entryCode);
|
|
45
|
+
// 6. Write runtime handler (inline the full implementation)
|
|
46
|
+
writeFileSync(join(serverDir, "handler.js"), generateRuntimeHandler());
|
|
47
|
+
// 7. Precompress if enabled
|
|
48
|
+
if (precompress) {
|
|
49
|
+
builder.log.minor("Compressing assets...");
|
|
50
|
+
await builder.compress(clientDir);
|
|
51
|
+
if (prerenderedFiles.length > 0) {
|
|
52
|
+
await builder.compress(prerenderedDir);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
builder.log.success(`Adapter output written to ${out}`);
|
|
56
|
+
builder.log.minor(`Run with: bun ${out}/server/entry.js`);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function generateEntryPoint(config) {
|
|
61
|
+
const { serverEntryRelative, envPrefix, development, clientDir, prerenderedDir } = config;
|
|
62
|
+
return `// Generated by @donkeylabs/adapter-sveltekit
|
|
63
|
+
import { Server } from "./index.js";
|
|
64
|
+
import { manifest } from "./manifest.js";
|
|
65
|
+
import { createUnifiedServer } from "./handler.js";
|
|
66
|
+
|
|
67
|
+
// Import user's @donkeylabs/server setup
|
|
68
|
+
const serverModule = await import("${serverEntryRelative}");
|
|
69
|
+
const donkeylabsServer = serverModule.server || serverModule.default;
|
|
70
|
+
|
|
71
|
+
if (!donkeylabsServer) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
"@donkeylabs/adapter-sveltekit: Could not find server export. " +
|
|
74
|
+
"Make sure your server entry file exports 'server' or uses default export."
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Initialize @donkeylabs/server (migrations, plugins, routes)
|
|
79
|
+
await donkeylabsServer.initialize();
|
|
80
|
+
|
|
81
|
+
// Create SvelteKit server
|
|
82
|
+
const svelteServer = new Server(manifest);
|
|
83
|
+
await svelteServer.init({ env: process.env });
|
|
84
|
+
|
|
85
|
+
// Configuration
|
|
86
|
+
const port = Number(process.env.${envPrefix}PORT) || 3000;
|
|
87
|
+
const host = process.env.${envPrefix}HOST || "0.0.0.0";
|
|
88
|
+
const development = ${development} || process.env.NODE_ENV === "development";
|
|
89
|
+
|
|
90
|
+
// Start unified server
|
|
91
|
+
createUnifiedServer({
|
|
92
|
+
svelteServer,
|
|
93
|
+
donkeylabsServer,
|
|
94
|
+
port,
|
|
95
|
+
host,
|
|
96
|
+
clientDir: "${clientDir}",
|
|
97
|
+
prerenderedDir: "${prerenderedDir}",
|
|
98
|
+
development,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
console.log(\`Server running at http://\${host}:\${port}\`);
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
104
|
+
function generateRuntimeHandler() {
|
|
105
|
+
// Inline the full runtime handler implementation
|
|
106
|
+
return `// Generated runtime handler by @donkeylabs/adapter-sveltekit
|
|
107
|
+
import { resolve, dirname } from "node:path";
|
|
108
|
+
import { fileURLToPath } from "node:url";
|
|
109
|
+
|
|
110
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Create a unified Bun server that handles both SvelteKit and @donkeylabs/server requests.
|
|
114
|
+
*/
|
|
115
|
+
export function createUnifiedServer(config) {
|
|
116
|
+
const {
|
|
117
|
+
svelteServer,
|
|
118
|
+
donkeylabsServer,
|
|
119
|
+
port,
|
|
120
|
+
host,
|
|
121
|
+
clientDir: clientDirRelative,
|
|
122
|
+
prerenderedDir: prerenderedDirRelative,
|
|
123
|
+
development = false,
|
|
124
|
+
} = config;
|
|
125
|
+
|
|
126
|
+
// Resolve paths relative to this script's directory
|
|
127
|
+
const clientDir = resolve(__dirname, clientDirRelative);
|
|
128
|
+
const prerenderedDir = resolve(__dirname, prerenderedDirRelative);
|
|
129
|
+
|
|
130
|
+
// Get services and core from @donkeylabs/server for injection into SvelteKit
|
|
131
|
+
const services = donkeylabsServer.getServices();
|
|
132
|
+
const core = donkeylabsServer.getCore();
|
|
133
|
+
|
|
134
|
+
Bun.serve({
|
|
135
|
+
port,
|
|
136
|
+
hostname: host,
|
|
137
|
+
|
|
138
|
+
async fetch(req, server) {
|
|
139
|
+
const url = new URL(req.url);
|
|
140
|
+
const pathname = url.pathname;
|
|
141
|
+
const ip = extractClientIP(req, server.requestIP(req)?.address);
|
|
142
|
+
|
|
143
|
+
// 1. Handle CORS preflight for API routes
|
|
144
|
+
if (req.method === "OPTIONS") {
|
|
145
|
+
return new Response(null, {
|
|
146
|
+
status: 204,
|
|
147
|
+
headers: getCorsHeaders(),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 2. API routes (GET or POST - stream/html/sse use GET, typed uses POST)
|
|
152
|
+
if ((req.method === "GET" || req.method === "POST") && /^\\/[a-zA-Z][a-zA-Z0-9_.]*$/.test(pathname)) {
|
|
153
|
+
const routeName = pathname.slice(1); // Remove leading /
|
|
154
|
+
|
|
155
|
+
// Check if this is a registered API route
|
|
156
|
+
if (donkeylabsServer.hasRoute(routeName)) {
|
|
157
|
+
const response = await donkeylabsServer.handleRequest(req, routeName, ip, {
|
|
158
|
+
corsHeaders: getCorsHeaders(),
|
|
159
|
+
});
|
|
160
|
+
if (response) return response;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 3. SSE endpoint
|
|
165
|
+
if (pathname === "/sse" && req.method === "GET") {
|
|
166
|
+
return handleSSE(req, core, ip);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 4. Static assets (/_app/*, etc.)
|
|
170
|
+
if (isStaticAsset(pathname)) {
|
|
171
|
+
const staticResponse = await serveStatic(clientDir, pathname);
|
|
172
|
+
if (staticResponse) return staticResponse;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 5. Prerendered pages
|
|
176
|
+
const prerenderedResponse = await servePrerendered(prerenderedDir, pathname);
|
|
177
|
+
if (prerenderedResponse) return prerenderedResponse;
|
|
178
|
+
|
|
179
|
+
// 6. SvelteKit pages
|
|
180
|
+
return handleSvelteKit(req, svelteServer, {
|
|
181
|
+
services,
|
|
182
|
+
core,
|
|
183
|
+
ip,
|
|
184
|
+
donkeylabsServer,
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function isStaticAsset(pathname) {
|
|
191
|
+
return (
|
|
192
|
+
pathname.startsWith("/_app/") ||
|
|
193
|
+
pathname.startsWith("/static/") ||
|
|
194
|
+
pathname === "/favicon.ico" ||
|
|
195
|
+
pathname === "/robots.txt" ||
|
|
196
|
+
pathname === "/sitemap.xml" ||
|
|
197
|
+
/\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|webp|avif|json|webmanifest)$/.test(pathname)
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function serveStatic(clientDir, pathname) {
|
|
202
|
+
const filePath = clientDir + pathname;
|
|
203
|
+
const file = Bun.file(filePath);
|
|
204
|
+
|
|
205
|
+
if (await file.exists()) {
|
|
206
|
+
// Check for precompressed versions
|
|
207
|
+
const brFile = Bun.file(filePath + ".br");
|
|
208
|
+
const gzFile = Bun.file(filePath + ".gz");
|
|
209
|
+
|
|
210
|
+
if (await brFile.exists()) {
|
|
211
|
+
return new Response(brFile, {
|
|
212
|
+
headers: {
|
|
213
|
+
"Content-Type": file.type || "application/octet-stream",
|
|
214
|
+
"Content-Encoding": "br",
|
|
215
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (await gzFile.exists()) {
|
|
221
|
+
return new Response(gzFile, {
|
|
222
|
+
headers: {
|
|
223
|
+
"Content-Type": file.type || "application/octet-stream",
|
|
224
|
+
"Content-Encoding": "gzip",
|
|
225
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return new Response(file, {
|
|
231
|
+
headers: {
|
|
232
|
+
"Content-Type": file.type || "application/octet-stream",
|
|
233
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function servePrerendered(prerenderedDir, pathname) {
|
|
242
|
+
let filePath = prerenderedDir + pathname;
|
|
243
|
+
if (!pathname.endsWith(".html") && !pathname.includes(".")) {
|
|
244
|
+
filePath = prerenderedDir + (pathname === "/" ? "/index" : pathname) + ".html";
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const file = Bun.file(filePath);
|
|
248
|
+
if (await file.exists()) {
|
|
249
|
+
return new Response(file, {
|
|
250
|
+
headers: {
|
|
251
|
+
"Content-Type": "text/html",
|
|
252
|
+
"Cache-Control": "public, max-age=0, must-revalidate",
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function handleSvelteKit(req, svelteServer, context) {
|
|
261
|
+
return svelteServer.respond(req, {
|
|
262
|
+
getClientAddress: () => context.ip,
|
|
263
|
+
platform: {
|
|
264
|
+
donkeylabs: {
|
|
265
|
+
services: context.services,
|
|
266
|
+
core: context.core,
|
|
267
|
+
// Direct route handler for SSR API calls (no HTTP!)
|
|
268
|
+
handleRoute: async (routeName, input) => {
|
|
269
|
+
return context.donkeylabsServer.callRoute(routeName, input, context.ip);
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function handleSSE(req, core, ip) {
|
|
277
|
+
const url = new URL(req.url);
|
|
278
|
+
const channels = url.searchParams.get("channels")?.split(",") || [];
|
|
279
|
+
|
|
280
|
+
if (channels.length === 0) {
|
|
281
|
+
return new Response("Missing channels parameter", { status: 400 });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const lastEventId = req.headers.get("Last-Event-ID") || undefined;
|
|
285
|
+
const { client, response } = core.sse.addClient({ lastEventId });
|
|
286
|
+
|
|
287
|
+
for (const channel of channels) {
|
|
288
|
+
core.sse.subscribe(client.id, channel);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
req.signal.addEventListener("abort", () => {
|
|
292
|
+
core.sse.removeClient(client.id);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const headers = new Headers(response.headers);
|
|
296
|
+
const corsHeaders = getCorsHeaders();
|
|
297
|
+
Object.entries(corsHeaders).forEach(([k, v]) => headers.set(k, v));
|
|
298
|
+
|
|
299
|
+
return new Response(response.body, {
|
|
300
|
+
status: response.status,
|
|
301
|
+
headers,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function extractClientIP(req, socketIP) {
|
|
306
|
+
return (
|
|
307
|
+
req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
|
|
308
|
+
req.headers.get("x-real-ip") ||
|
|
309
|
+
socketIP ||
|
|
310
|
+
"127.0.0.1"
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function getCorsHeaders() {
|
|
315
|
+
return {
|
|
316
|
+
"Access-Control-Allow-Origin": "*",
|
|
317
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
318
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
`;
|
|
322
|
+
}
|
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for @donkeylabs/adapter-sveltekit dev server integration
|
|
3
|
+
*
|
|
4
|
+
* Supports two modes:
|
|
5
|
+
* - `bun --bun run dev`: Single-process mode (in-process, one port)
|
|
6
|
+
* - `bun run dev`: Subprocess mode (two processes, proxy)
|
|
7
|
+
*/
|
|
8
|
+
import type { Plugin } from "vite";
|
|
9
|
+
export interface DevPluginOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Path to your @donkeylabs/server entry file.
|
|
12
|
+
* This file should export a configured AppServer instance.
|
|
13
|
+
*
|
|
14
|
+
* @default "./src/server/index.ts"
|
|
15
|
+
*/
|
|
16
|
+
serverEntry?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Port for the backend server (subprocess mode only).
|
|
19
|
+
* @default 3001
|
|
20
|
+
*/
|
|
21
|
+
backendPort?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Watch server files and auto-regenerate types on changes.
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
watchTypes?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Directory to watch for server file changes.
|
|
29
|
+
* @default "./src/server"
|
|
30
|
+
*/
|
|
31
|
+
watchDir?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Enable hot reload for route files (dev mode only).
|
|
34
|
+
* When a route file changes, the router will be reloaded without server restart.
|
|
35
|
+
* @default true
|
|
36
|
+
*/
|
|
37
|
+
hotReloadRoutes?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Glob patterns for route files to watch for hot reload.
|
|
40
|
+
* @default ["**\/routes\/**\/*.ts"]
|
|
41
|
+
*/
|
|
42
|
+
routePatterns?: string[];
|
|
43
|
+
}
|
|
44
|
+
declare global {
|
|
45
|
+
var __donkeylabs_dev_server__: any;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get the global app server instance for SSR direct calls.
|
|
49
|
+
* This allows hooks to access the server without HTTP.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getDevServer(): any;
|
|
52
|
+
/**
|
|
53
|
+
* Vite plugin that integrates @donkeylabs/server with the dev server.
|
|
54
|
+
*
|
|
55
|
+
* - With `bun --bun run dev`: Runs in-process (single port, recommended)
|
|
56
|
+
* - With `bun run dev`: Spawns subprocess (two ports, fallback)
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // vite.config.ts
|
|
60
|
+
* import { donkeylabsDev } from "@donkeylabs/adapter-sveltekit/vite";
|
|
61
|
+
*
|
|
62
|
+
* export default defineConfig({
|
|
63
|
+
* plugins: [
|
|
64
|
+
* donkeylabsDev({ serverEntry: "./src/server/index.ts" }),
|
|
65
|
+
* sveltekit()
|
|
66
|
+
* ]
|
|
67
|
+
* });
|
|
68
|
+
*/
|
|
69
|
+
export declare function donkeylabsDev(options?: DevPluginOptions): Plugin;
|
|
70
|
+
export default donkeylabsDev;
|
|
71
|
+
//# sourceMappingURL=vite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AASlD,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAOD,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,yBAAyB,EAAE,GAAG,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,GAAG,CAElC;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAsnBpE;AAED,eAAe,aAAa,CAAC"}
|