@donkeylabs/adapter-sveltekit 1.1.1 → 1.1.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donkeylabs/adapter-sveltekit",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "type": "module",
5
5
  "description": "SvelteKit adapter for @donkeylabs/server - seamless SSR/browser API integration",
6
6
  "main": "./src/index.ts",
@@ -197,16 +197,16 @@ function generateTypedSvelteKitClient(routes: RouteInfo[]): string {
197
197
  const inputType = `Routes.${pascalNs}.${pascalRoute}.Input`;
198
198
  const fullRouteName = commonPrefix ? `${commonPrefix}.${r.name}` : r.name;
199
199
  // Stream routes provide three methods:
200
- // - fetch(input): POST request (programmatic)
200
+ // - fetch(input, options?): POST request (programmatic)
201
201
  // - url(input): GET URL for browser (video src, img src, download links)
202
- // - get(input): GET fetch request
202
+ // - get(input, options?): GET fetch request
203
203
  return ` ${methodName}: {
204
204
  /** POST request with JSON body (programmatic) */
205
- fetch: (input: ${inputType}): Promise<Response> => this.streamRequest("${fullRouteName}", input),
205
+ fetch: (input: ${inputType}, options?: RequestOptions): Promise<Response> => this.streamRequest("${fullRouteName}", input, options),
206
206
  /** GET URL for browser src attributes (video, img, download links) */
207
207
  url: (input: ${inputType}): string => this.streamUrl("${fullRouteName}", input),
208
208
  /** GET request with query params */
209
- get: (input: ${inputType}): Promise<Response> => this.streamGet("${fullRouteName}", input),
209
+ get: (input: ${inputType}, options?: RequestOptions): Promise<Response> => this.streamGet("${fullRouteName}", input, options),
210
210
  }`;
211
211
  });
212
212
 
package/src/vite.ts CHANGED
@@ -218,24 +218,43 @@ export function donkeylabsDev(options: DevPluginOptions = {}): Plugin {
218
218
  res.setHeader(key, value);
219
219
  }
220
220
 
221
- // Handle body streaming
221
+ // Flush headers immediately for streaming responses
222
+ if (typeof res.flushHeaders === "function") {
223
+ res.flushHeaders();
224
+ }
225
+
226
+ // Handle body streaming (non-blocking for continuous streams like MJPEG)
222
227
  if (response.body) {
223
228
  const reader = response.body.getReader();
229
+ let closed = false;
230
+
231
+ // Handle client disconnect
232
+ req.on("close", () => {
233
+ closed = true;
234
+ reader.cancel().catch(() => {});
235
+ });
236
+
237
+ // Pump without awaiting - allows continuous streams
224
238
  const pump = async () => {
225
239
  try {
226
- while (true) {
240
+ while (!closed) {
227
241
  const { done, value } = await reader.read();
228
- if (done) {
229
- res.end();
242
+ if (done || closed) {
243
+ if (!closed) res.end();
230
244
  break;
231
245
  }
232
- res.write(value);
246
+ // Write and check if client is still connected
247
+ const canContinue = res.write(value);
248
+ if (!canContinue && !closed) {
249
+ // Backpressure - wait for drain
250
+ await new Promise<void>(resolve => res.once("drain", resolve));
251
+ }
233
252
  }
234
253
  } catch {
235
- res.end();
254
+ if (!closed) res.end();
236
255
  }
237
256
  };
238
- await pump();
257
+ pump(); // Don't await - let it run in background
239
258
  } else {
240
259
  res.end();
241
260
  }