@qzsy/vinext 0.1.9 → 0.1.11

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/deploy.js CHANGED
@@ -378,7 +378,7 @@ import { fetchWorkerFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath
378
378
  import type { PagesPipelineDeps } from "vinext/server/pages-request-pipeline";
379
379
  import { handleImageOptimization, DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath } from "vinext/server/image-optimization";
380
380
  import type { ImageConfig } from "vinext/server/image-optimization";
381
- import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, isOpenRedirectShaped } from "vinext/server/request-pipeline";
381
+ import { bufferRequestBodyForHeaderClone, cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, isOpenRedirectShaped } from "vinext/server/request-pipeline";
382
382
  import { notFoundStaticAssetResponse } from "vinext/server/http-error-responses";
383
383
  import { assetPrefixPathname, isNextStaticPath } from "vinext/utils/asset-prefix";
384
384
  import { hasBasePath, stripBasePath } from "vinext/utils/base-path";
@@ -452,6 +452,7 @@ export default {
452
452
  // forged to influence routing or impersonate internal state.
453
453
  // Request.headers is immutable in Workers, so build a clean copy.
454
454
  {
455
+ request = await bufferRequestBodyForHeaderClone(request);
455
456
  const filteredHeaders = filterInternalHeaders(request.headers);
456
457
  request = cloneRequestWithHeaders(request, filteredHeaders);
457
458
  }
package/dist/index.js CHANGED
@@ -341,6 +341,22 @@ const _fontGoogleShimPath = resolveShimModulePath(_shimsDir, "font-google");
341
341
  const _appBrowserServerActionClientPath = resolveShimModulePath(normalizePathSeparators(path.resolve(__dirname, "server")), "app-browser-server-action-client");
342
342
  const _appRscHandlerPath = resolveShimModulePath(normalizePathSeparators(path.resolve(__dirname, "server")), "app-rsc-handler");
343
343
  const _canExternalizeAppRscHandler = _appRscHandlerPath.endsWith(".js");
344
+ const _defaultImageLoaderShimPath = resolveShimModulePath(_shimsDir, "default-image-loader");
345
+ /** Absolute path to `images.loaderFile` when configured; set in vinext:config. */
346
+ let _imageLoaderFile;
347
+ function isImageShimModule(importer) {
348
+ if (!importer) return false;
349
+ const normalized = normalizePathSeparators(importer.split("?")[0]);
350
+ return /\/shims\/image\.(tsx?|jsx?)$/.test(normalized);
351
+ }
352
+ function isDefaultImageLoaderRequest(id, importer) {
353
+ const cleanId = id.startsWith("\0") ? id.slice(1) : id;
354
+ const normalizedId = normalizePathSeparators(cleanId.split("?")[0]);
355
+ if (normalizedId === normalizePathSeparators(_defaultImageLoaderShimPath)) return true;
356
+ if (/\/shims\/default-image-loader\.(tsx?|jsx?)$/.test(normalizedId)) return true;
357
+ if (isImageShimModule(importer) && (cleanId === "./default-image-loader.js" || cleanId === "./default-image-loader" || cleanId === "./default-image-loader.ts" || cleanId === "./default-image-loader.tsx")) return true;
358
+ return false;
359
+ }
344
360
  function isValidExportIdentifier(name) {
345
361
  return /^[$A-Z_a-z][$\w]*$/.test(name);
346
362
  }
@@ -623,6 +639,16 @@ function vinext(options = {}) {
623
639
  serverOnlyShimPath: resolveShimModulePath(shimsDir, "server-only")
624
640
  }),
625
641
  dataUrlCssPlugin(),
642
+ {
643
+ name: "vinext:image-loader-file",
644
+ enforce: "pre",
645
+ resolveId: {
646
+ filter: { id: /default-image-loader/ },
647
+ handler(id, importer) {
648
+ if (_imageLoaderFile && isDefaultImageLoaderRequest(id, importer)) return _imageLoaderFile;
649
+ }
650
+ }
651
+ },
626
652
  {
627
653
  name: "vinext:config",
628
654
  enforce: "pre",
@@ -827,9 +853,7 @@ function vinext(options = {}) {
827
853
  overlay: false
828
854
  };
829
855
  const cssModulesOverride = config.css?.modules === false || typeof config.css?.modules === "object" && "Loader" in config.css.modules ? {} : { modules: { Loader: sassComposesLoader.Loader } };
830
- const defaultImageLoaderShimPath = resolveShimModulePath(shimsDir, "default-image-loader");
831
- const imageLoaderFile = nextConfig.images?.loaderFile;
832
- const imageLoaderAlias = imageLoaderFile != null ? { [defaultImageLoaderShimPath]: imageLoaderFile } : {};
856
+ _imageLoaderFile = nextConfig.images?.loaderFile;
833
857
  const viteConfig = {
834
858
  appType: "custom",
835
859
  build: {
@@ -877,8 +901,7 @@ function vinext(options = {}) {
877
901
  alias: {
878
902
  ...tsconfigPathAliases,
879
903
  ...nextConfig.aliases,
880
- ...nextShimMap,
881
- ...imageLoaderAlias
904
+ ...nextShimMap
882
905
  },
883
906
  dedupe: [
884
907
  "react",
@@ -922,10 +945,10 @@ function vinext(options = {}) {
922
945
  const incomingInclude = config.optimizeDeps?.include ?? [];
923
946
  const depOptimizeAliasPlugin = {
924
947
  name: "vinext:dep-optimize-alias",
925
- resolveId(id) {
948
+ resolveId(id, importer) {
926
949
  const shimBase = _reactServerShims.get(id);
927
950
  if (shimBase !== void 0) return resolveShimModulePath(shimsDir, shimBase);
928
- if (imageLoaderFile != null && id === defaultImageLoaderShimPath) return imageLoaderFile;
951
+ if (_imageLoaderFile && isDefaultImageLoaderRequest(id, importer)) return _imageLoaderFile;
929
952
  }
930
953
  };
931
954
  const depOptimizeNodeEnvOptions = getDepOptimizeNodeEnvOptions(viteMajorVersion, nodeEnvDefine);
@@ -3,7 +3,7 @@ import { runWithExecutionContext } from "../shims/request-context.js";
3
3
  import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER } from "./headers.js";
4
4
  import { badRequestResponse, notFoundResponse, notFoundStaticAssetResponse } from "./http-error-responses.js";
5
5
  import { isOpenRedirectShaped } from "./open-redirect.js";
6
- import { cloneRequestWithHeaders, filterInternalHeaders } from "./request-pipeline.js";
6
+ import { bufferRequestBodyForHeaderClone, cloneRequestWithHeaders, filterInternalHeaders } from "./request-pipeline.js";
7
7
  import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
8
8
  import { resolveStaticAssetSignal } from "./worker-utils.js";
9
9
  import { readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
@@ -26,6 +26,7 @@ async function handleRequest(request, env, ctx) {
26
26
  }
27
27
  if (isNextStaticPath(url.pathname, __workerBasePath, __workerAssetPathPrefix)) return notFoundStaticAssetResponse();
28
28
  {
29
+ request = await bufferRequestBodyForHeaderClone(request);
29
30
  const prerenderRouteParamsPayload = readTrustedPrerenderRouteParams(request);
30
31
  const filteredHeaders = filterInternalHeaders(request.headers);
31
32
  const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParamsPayload);
@@ -4,7 +4,7 @@ import { getRequestExecutionContext } from "../shims/request-context.js";
4
4
  import { ACTION_REVALIDATED_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER } from "./headers.js";
5
5
  import { isExternalUrl, matchRedirect, matchRewrite, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, sanitizeDestination } from "../config/config-matchers.js";
6
6
  import { notFoundResponse } from "./http-error-responses.js";
7
- import { applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, normalizeTrailingSlash, resolvePublicFileRoute } from "./request-pipeline.js";
7
+ import { applyConfigHeadersToResponse, bufferRequestBodyForHeaderClone, cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, normalizeTrailingSlash, resolvePublicFileRoute } from "./request-pipeline.js";
8
8
  import { headersContextFromRequest } from "../shims/headers.js";
9
9
  import { ensureFetchPatch, setCurrentFetchSoftTags } from "../shims/fetch-cache.js";
10
10
  import { mergeRewriteQuery } from "../utils/query.js";
@@ -18,10 +18,10 @@ import { buildNextDataNotFoundResponse, normalizePagesDataRequest } from "./page
18
18
  import { matchPrerenderRouteParamsPayload, readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
19
19
  import { getRenderedConcreteUrlPathsForRoute } from "./pregenerated-concrete-paths.js";
20
20
  import { pickRootParams, setRootParams } from "../shims/root-params.js";
21
+ import { flattenErrorCauses } from "../utils/error-cause.js";
21
22
  import { createServerActionNotFoundResponse, getServerActionNotFoundMessage } from "./server-action-not-found.js";
22
23
  import { buildPageCacheTags } from "./implicit-tags.js";
23
24
  import { buildPostMwRequestContext } from "./app-post-middleware-context.js";
24
- import { flattenErrorCauses } from "../utils/error-cause.js";
25
25
  import { finalizeAppRscResponse } from "./app-rsc-response-finalizer.js";
26
26
  import { normalizeRscRequest } from "./app-rsc-request-normalization.js";
27
27
  import { runWithPrerenderWorkUnit } from "./prerender-work-unit-setup.js";
@@ -488,6 +488,7 @@ function createAppRscHandler(options) {
488
488
  return async function appRscHandler(rawRequest, ctx) {
489
489
  options.registerCacheAdapters();
490
490
  await options.ensureInstrumentation?.();
491
+ rawRequest = await bufferRequestBodyForHeaderClone(rawRequest);
491
492
  const mwCtx = rawRequest.headers.get(VINEXT_MW_CTX_HEADER);
492
493
  const hasDataRequestHeader = rawRequest.headers.get("x-nextjs-data") === "1";
493
494
  const pagesDataUrl = new URL(rawRequest.url);
@@ -161,6 +161,18 @@ declare function processMiddlewareHeaders(headers: Headers): void;
161
161
  * @returns A new Headers with internal framework headers removed
162
162
  */
163
163
  declare function filterInternalHeaders(headers: Headers): Headers;
164
+ /**
165
+ * Materialize the request body before header/url cloning.
166
+ *
167
+ * `cloneRequestWithHeaders` / `cloneRequestWithUrl` can lose a streaming body
168
+ * on Node/undici when the input is a foreign Request or when the underlying
169
+ * stream was partially consumed. Server actions and route handlers both rely on
170
+ * the body surviving internal header filtering, so we read it once up front
171
+ * and rebuild a fresh Request for the rest of the pipeline.
172
+ *
173
+ * Multipart bodies are left streaming so FormData parsing still works.
174
+ */
175
+ declare function bufferRequestBodyForHeaderClone(request: Request): Promise<Request>;
164
176
  /**
165
177
  * Clone a Request while overriding headers, preserving metadata when possible.
166
178
  *
@@ -184,4 +196,4 @@ declare function cloneRequestWithHeaders(request: Request, headers: Headers): Re
184
196
  */
185
197
  declare function cloneRequestWithUrl(request: Request, url: string): Request;
186
198
  //#endregion
187
- export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
199
+ export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, bufferRequestBodyForHeaderClone, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
@@ -365,6 +365,56 @@ function getRequestCf(request) {
365
365
  const cf = Reflect.get(request, "cf");
366
366
  return cf === void 0 ? void 0 : cf;
367
367
  }
368
+ const METHODS_THAT_MAY_HAVE_BODY = new Set([
369
+ "POST",
370
+ "PUT",
371
+ "PATCH",
372
+ "DELETE"
373
+ ]);
374
+ /**
375
+ * Materialize the request body before header/url cloning.
376
+ *
377
+ * `cloneRequestWithHeaders` / `cloneRequestWithUrl` can lose a streaming body
378
+ * on Node/undici when the input is a foreign Request or when the underlying
379
+ * stream was partially consumed. Server actions and route handlers both rely on
380
+ * the body surviving internal header filtering, so we read it once up front
381
+ * and rebuild a fresh Request for the rest of the pipeline.
382
+ *
383
+ * Multipart bodies are left streaming so FormData parsing still works.
384
+ */
385
+ async function bufferRequestBodyForHeaderClone(request) {
386
+ if (!METHODS_THAT_MAY_HAVE_BODY.has(request.method.toUpperCase())) return request;
387
+ if (request.body === null) return request;
388
+ if ((request.headers.get("content-type") ?? "").startsWith("multipart/form-data")) return request;
389
+ try {
390
+ const bytes = await request.arrayBuffer();
391
+ const init = {
392
+ method: request.method,
393
+ headers: request.headers,
394
+ body: bytes,
395
+ redirect: request.redirect,
396
+ referrer: request.referrer,
397
+ referrerPolicy: request.referrerPolicy,
398
+ signal: request.signal,
399
+ credentials: request.credentials,
400
+ integrity: request.integrity,
401
+ keepalive: request.keepalive,
402
+ mode: request.mode,
403
+ cache: request.cache
404
+ };
405
+ if (bytes.byteLength > 0) init.duplex = "half";
406
+ const buffered = new Request(request.url, init);
407
+ const cf = getRequestCf(request);
408
+ if (cf !== void 0) Object.defineProperty(buffered, "cf", {
409
+ value: cf,
410
+ enumerable: true,
411
+ configurable: true
412
+ });
413
+ return buffered;
414
+ } catch {
415
+ return request;
416
+ }
417
+ }
368
418
  /**
369
419
  * Clone a Request while overriding headers, preserving metadata when possible.
370
420
  *
@@ -444,4 +494,4 @@ function cloneRequestWithUrl(request, url) {
444
494
  return cloned;
445
495
  }
446
496
  //#endregion
447
- export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
497
+ export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, bufferRequestBodyForHeaderClone, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qzsy/vinext",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
5
5
  "license": "MIT",
6
6
  "repository": {