litestar-vite-plugin 0.24.1 → 0.26.0

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.
Files changed (35) hide show
  1. package/README.md +6 -6
  2. package/dist/js/astro.d.ts +6 -0
  3. package/dist/js/astro.js +14 -77
  4. package/dist/js/dev-server-index.html +2 -2
  5. package/dist/js/helpers/csrf.d.ts +2 -0
  6. package/dist/js/helpers/csrf.js +28 -1
  7. package/dist/js/helpers/htmx.js +105 -2
  8. package/dist/js/index.d.ts +6 -0
  9. package/dist/js/index.js +75 -128
  10. package/dist/js/inertia-helpers/index.d.ts +27 -9
  11. package/dist/js/install-hint.d.ts +17 -0
  12. package/dist/js/install-hint.js +35 -1
  13. package/dist/js/nuxt.d.ts +6 -0
  14. package/dist/js/nuxt.js +12 -72
  15. package/dist/js/shared/bridge-schema.d.ts +1 -0
  16. package/dist/js/shared/bridge-schema.js +41 -9
  17. package/dist/js/shared/constants.d.ts +4 -18
  18. package/dist/js/shared/constants.js +2 -6
  19. package/dist/js/shared/emit-page-props-types.d.ts +1 -1
  20. package/dist/js/shared/emit-page-props-types.js +51 -5
  21. package/dist/js/shared/emit-schemas-types.d.ts +1 -1
  22. package/dist/js/shared/emit-schemas-types.js +86 -15
  23. package/dist/js/shared/emit-static-props-types.d.ts +1 -1
  24. package/dist/js/shared/emit-static-props-types.js +2 -2
  25. package/dist/js/shared/typegen-cache.d.ts +10 -11
  26. package/dist/js/shared/typegen-cache.js +10 -1
  27. package/dist/js/shared/typegen-core.d.ts +24 -0
  28. package/dist/js/shared/typegen-core.js +101 -45
  29. package/dist/js/shared/typegen-plugin.d.ts +26 -0
  30. package/dist/js/shared/typegen-plugin.js +191 -129
  31. package/dist/js/shared/vite-compat.d.ts +23 -15
  32. package/dist/js/shared/vite-compat.js +12 -6
  33. package/dist/js/sveltekit.d.ts +6 -0
  34. package/dist/js/sveltekit.js +14 -77
  35. package/package.json +14 -8
package/dist/js/index.js CHANGED
@@ -6,12 +6,11 @@ import { loadEnv } from "vite";
6
6
  import fullReload from "vite-plugin-full-reload";
7
7
  import { checkBackendAvailability, loadLitestarMeta } from "./litestar-meta.js";
8
8
  import { readBridgeConfig } from "./shared/bridge-schema.js";
9
- import { DEBOUNCE_MS } from "./shared/constants.js";
10
9
  import { createLogger } from "./shared/logger.js";
11
10
  import { resolveLitestarPort } from "./shared/network.js";
12
11
  import { resolveDefaultSdkClientPlugin } from "./shared/typegen-core.js";
13
- import { createLitestarTypeGenPlugin } from "./shared/typegen-plugin.js";
14
- import { buildInputOptions, resolveUserBuildInput } from "./shared/vite-compat.js";
12
+ import { createLitestarTypeGenPlugin, resolveTypesConfig } from "./shared/typegen-plugin.js";
13
+ import { buildInputOptions, hmrServerConfig, resolveUserBuildInput } from "./shared/vite-compat.js";
15
14
  let exitHandlersBound = false;
16
15
  let warnedMissingRuntimeConfig = false;
17
16
  const MAX_TRANSFORM_PAYLOAD_BYTES = 1e6;
@@ -94,32 +93,32 @@ function resolveLitestarPlugin(pluginConfig) {
94
93
  const proxyHmrClientPort = pythonDefaults?.proxyMode === "vite" ? resolveLitestarPort(pythonDefaults.litestarPort, effectiveAppUrl, process.env) : null;
95
94
  const withProxyErrorSilencer = (proxyConfig) => {
96
95
  if (!proxyConfig) return void 0;
97
- return Object.fromEntries(
98
- Object.entries(proxyConfig).map(([key, value]) => {
99
- if (typeof value !== "object" || value === null) {
100
- return [key, value];
101
- }
102
- const existingConfigure = value.configure;
103
- const valueWithWebsocket = Object.hasOwn(value, "ws") ? value : { ...value, ws: true };
104
- return [
105
- key,
106
- {
107
- ...valueWithWebsocket,
108
- configure(proxy, opts) {
109
- proxy.on("error", (err) => {
110
- const msg = String(err?.message ?? "");
111
- if (shuttingDown || msg.includes("ECONNREFUSED") || msg.includes("ECONNRESET") || msg.includes("socket hang up")) {
112
- return;
113
- }
114
- });
115
- if (typeof existingConfigure === "function") {
116
- existingConfigure(proxy, opts);
117
- }
118
- }
96
+ const entries = Object.entries(proxyConfig).map(([key, value]) => {
97
+ if (typeof value !== "object" || value === null) {
98
+ return [key, value];
99
+ }
100
+ const existingConfigure = value.configure;
101
+ const valueWithWebsocket = Object.hasOwn(value, "ws") ? value : { ...value, ws: true };
102
+ const configure = (proxy, opts) => {
103
+ proxy.on("error", (err) => {
104
+ const msg = err.message;
105
+ if (shuttingDown || msg.includes("ECONNREFUSED") || msg.includes("ECONNRESET") || msg.includes("socket hang up")) {
106
+ return;
119
107
  }
120
- ];
121
- })
122
- );
108
+ });
109
+ if (typeof existingConfigure === "function") {
110
+ existingConfigure(proxy, opts);
111
+ }
112
+ };
113
+ return [
114
+ key,
115
+ {
116
+ ...valueWithWebsocket,
117
+ configure
118
+ }
119
+ ];
120
+ });
121
+ return Object.fromEntries(entries);
123
122
  };
124
123
  const explicitServerOrigin = typeof userConfig.server?.origin === "string" && userConfig.server.origin.length > 0 ? userConfig.server.origin : void 0;
125
124
  const shouldForceDirectServerOrigin = explicitServerOrigin !== void 0 || pythonDefaults?.proxyMode === "direct";
@@ -139,15 +138,19 @@ function resolveLitestarPlugin(pluginConfig) {
139
138
  },
140
139
  server: {
141
140
  origin: shouldForceDirectServerOrigin ? explicitServerOrigin ?? "__litestar_vite_placeholder__" : proxyOriginDefault,
142
- // Auto-configure HMR to use a path that routes through Litestar proxy
143
- // Note: Vite automatically prepends `base` to `hmr.path`, so we just use "vite-hmr"
144
- // Result: base="/static/" + path="vite-hmr" = "/static/vite-hmr"
145
- hmr: userConfig.server?.hmr === false ? false : {
141
+ // Auto-configure the HMR WebSocket to use a path that routes through the Litestar proxy.
142
+ // Auto-configure the HMR WebSocket to route through the Litestar proxy.
143
+ // Vite 8.1 moved the HMR network options (path/host/port/clientPort/protocol/timeout)
144
+ // from `server.hmr.*` to `server.ws.*`; on Vite 7 / 8.0 they stay under `server.hmr.*`.
145
+ // `hmrServerConfig` picks the right key for the running version (deprecation-free on each).
146
+ // Note: Vite prepends `base` to the path, so we use "vite-hmr" => "/static/vite-hmr".
147
+ ...userConfig.server?.hmr === false || userConfig.server?.ws === false ? { hmr: false } : hmrServerConfig({
146
148
  path: "vite-hmr",
147
149
  ...proxyHmrClientPort ? { clientPort: proxyHmrClientPort } : {},
148
- ...serverConfig?.hmr,
149
- ...userConfig.server?.hmr === true ? {} : userConfig.server?.hmr
150
- },
150
+ ...serverConfig?.host ? { host: serverConfig.host } : {},
151
+ ...typeof userConfig.server?.ws === "object" ? userConfig.server.ws : {},
152
+ ...typeof userConfig.server?.hmr === "object" ? userConfig.server.hmr : {}
153
+ }),
151
154
  // Auto-configure proxy to forward API requests to Litestar backend
152
155
  // This allows the app to work when accessing Vite directly (not through Litestar proxy)
153
156
  // Only proxies /api and /schema routes - everything else is handled by Vite
@@ -583,80 +586,11 @@ function resolvePluginConfig(config) {
583
586
  if (resolvedConfig.refresh === true) {
584
587
  resolvedConfig.refresh = [{ paths: refreshPaths }];
585
588
  }
586
- let typesConfig = false;
587
- const defaultTypesOutput = "src/generated";
588
- const defaultOpenapiPath = path.join(defaultTypesOutput, "openapi.json");
589
- const defaultRoutesPath = path.join(defaultTypesOutput, "routes.json");
590
- const defaultSchemasTsPath = path.join(defaultTypesOutput, "schemas.ts");
591
- const defaultPagePropsPath = path.join(defaultTypesOutput, "inertia-pages.json");
592
- if (resolvedConfig.types === false) {
593
- } else if (resolvedConfig.types === true) {
594
- typesConfig = {
595
- enabled: true,
596
- output: defaultTypesOutput,
597
- openapiPath: defaultOpenapiPath,
598
- routesPath: defaultRoutesPath,
599
- pagePropsPath: defaultPagePropsPath,
600
- schemasTsPath: defaultSchemasTsPath,
601
- generateZod: false,
602
- generateSdk: true,
603
- generateRoutes: true,
604
- generatePageProps: true,
605
- generateSchemas: true,
606
- globalRoute: false,
607
- debounce: DEBOUNCE_MS
608
- };
609
- } else if (resolvedConfig.types === "auto" || typeof resolvedConfig.types === "undefined") {
610
- if (pythonDefaults?.types) {
611
- typesConfig = {
612
- enabled: pythonDefaults.types.enabled,
613
- output: pythonDefaults.types.output,
614
- openapiPath: pythonDefaults.types.openapiPath,
615
- routesPath: pythonDefaults.types.routesPath,
616
- pagePropsPath: pythonDefaults.types.pagePropsPath,
617
- schemasTsPath: pythonDefaults.types.schemasTsPath ?? path.join(pythonDefaults.types.output, "schemas.ts"),
618
- generateZod: pythonDefaults.types.generateZod,
619
- generateSdk: pythonDefaults.types.generateSdk,
620
- generateRoutes: pythonDefaults.types.generateRoutes,
621
- generatePageProps: pythonDefaults.types.generatePageProps,
622
- generateSchemas: pythonDefaults.types.generateSchemas ?? true,
623
- globalRoute: pythonDefaults.types.globalRoute,
624
- debounce: DEBOUNCE_MS
625
- };
626
- }
627
- } else if (typeof resolvedConfig.types === "object" && resolvedConfig.types !== null) {
628
- const userProvidedOpenapi = Object.hasOwn(resolvedConfig.types, "openapiPath");
629
- const userProvidedRoutes = Object.hasOwn(resolvedConfig.types, "routesPath");
630
- const userProvidedPageProps = Object.hasOwn(resolvedConfig.types, "pagePropsPath");
631
- const userProvidedSchemasTs = Object.hasOwn(resolvedConfig.types, "schemasTsPath");
632
- typesConfig = {
633
- enabled: resolvedConfig.types.enabled ?? true,
634
- output: resolvedConfig.types.output ?? defaultTypesOutput,
635
- openapiPath: resolvedConfig.types.openapiPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "openapi.json") : defaultOpenapiPath),
636
- routesPath: resolvedConfig.types.routesPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "routes.json") : defaultRoutesPath),
637
- pagePropsPath: resolvedConfig.types.pagePropsPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "inertia-pages.json") : defaultPagePropsPath),
638
- schemasTsPath: resolvedConfig.types.schemasTsPath ?? (resolvedConfig.types.output ? path.join(resolvedConfig.types.output, "schemas.ts") : defaultSchemasTsPath),
639
- generateZod: resolvedConfig.types.generateZod ?? false,
640
- generateSdk: resolvedConfig.types.generateSdk ?? true,
641
- generateRoutes: resolvedConfig.types.generateRoutes ?? true,
642
- generatePageProps: resolvedConfig.types.generatePageProps ?? true,
643
- generateSchemas: resolvedConfig.types.generateSchemas ?? true,
644
- globalRoute: resolvedConfig.types.globalRoute ?? false,
645
- debounce: resolvedConfig.types.debounce ?? DEBOUNCE_MS
646
- };
647
- if (!userProvidedOpenapi && resolvedConfig.types.output) {
648
- typesConfig.openapiPath = path.join(typesConfig.output, "openapi.json");
649
- }
650
- if (!userProvidedRoutes && resolvedConfig.types.output) {
651
- typesConfig.routesPath = path.join(typesConfig.output, "routes.json");
652
- }
653
- if (!userProvidedPageProps && resolvedConfig.types.output) {
654
- typesConfig.pagePropsPath = path.join(typesConfig.output, "inertia-pages.json");
655
- }
656
- if (!userProvidedSchemasTs && resolvedConfig.types.output) {
657
- typesConfig.schemasTsPath = path.join(typesConfig.output, "schemas.ts");
658
- }
659
- }
589
+ const typesConfig = resolveTypesConfig({
590
+ requested: resolvedConfig.types,
591
+ pythonConfig: pythonDefaults?.types ?? void 0,
592
+ defaultOutput: "src/generated"
593
+ });
660
594
  const bridgeInertiaEnabled = pythonDefaults?.spa !== null && pythonDefaults?.spa !== void 0;
661
595
  const inertiaMode = resolvedConfig.inertiaMode ?? bridgeInertiaEnabled;
662
596
  const effectiveResourceDir = resolvedConfig.resourceDir ?? pythonDefaults?.resourceDir ?? "src";
@@ -793,34 +727,34 @@ function createStaticPropsPlugin() {
793
727
  };
794
728
  }
795
729
  function resolveDevServerUrl(address, config, userConfig) {
796
- const configHmrProtocol = typeof config.server.hmr === "object" ? config.server.hmr.protocol : null;
797
- const clientProtocol = configHmrProtocol ? configHmrProtocol === "wss" ? "https" : "http" : null;
730
+ const configHmrNet = (typeof config.server.ws === "object" ? config.server.ws : null) ?? (typeof config.server.hmr === "object" ? config.server.hmr : null);
731
+ const configWsProtocol = configHmrNet?.protocol ?? null;
732
+ const clientProtocol = configWsProtocol ? configWsProtocol === "wss" ? "https" : "http" : null;
798
733
  const serverProtocol = config.server.https ? "https" : "http";
799
734
  const protocol = clientProtocol ?? serverProtocol;
800
- const configHmrHost = typeof config.server.hmr === "object" ? config.server.hmr.host : null;
735
+ const configWsHost = configHmrNet?.host ?? null;
801
736
  const userHost = typeof userConfig.server?.host === "string" ? userConfig.server.host : null;
802
737
  const configHost = typeof config.server.host === "string" ? config.server.host : null;
803
738
  const remoteHost = process.env.VITE_ALLOW_REMOTE && !userConfig.server?.host ? isIpv6(address) ? "[::1]" : "127.0.0.1" : null;
804
739
  const serverAddress = isIpv6(address) ? `[${address.address}]` : address.address;
805
- let host = configHmrHost ?? userHost ?? remoteHost ?? configHost ?? serverAddress;
740
+ let host = configWsHost ?? userHost ?? remoteHost ?? configHost ?? serverAddress;
806
741
  if (host === "0.0.0.0") {
807
742
  host = "127.0.0.1";
808
743
  } else if (host === "::" || host === "[::]") {
809
744
  host = "[::1]";
810
745
  }
811
- const userHmrClientPort = typeof userConfig.server?.hmr === "object" ? userConfig.server.hmr.clientPort : null;
812
- const port = userHmrClientPort ?? address.port;
746
+ const userHmrNet = (typeof userConfig.server?.ws === "object" ? userConfig.server.ws : null) ?? (typeof userConfig.server?.hmr === "object" ? userConfig.server.hmr : null);
747
+ const userWsClientPort = userHmrNet?.clientPort ?? null;
748
+ const port = userWsClientPort ?? address.port;
813
749
  return `${protocol}://${host}:${port}`;
814
750
  }
815
751
  function isIpv6(address) {
816
- return address.family === "IPv6" || // In node >=18.0 <18.4 this was an integer value. This was changed in a minor version.
817
- // See: https://github.com/laravel/vite-plugin/issues/103
818
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
819
- // @ts-ignore-next-line
820
- address.family === 6;
752
+ const family = address.family;
753
+ return family === "IPv6" || family === 6;
821
754
  }
822
755
  function noExternalInertiaHelpers(config) {
823
- const userNoExternal = config.ssr?.noExternal;
756
+ const ssrConfig = typeof config.ssr === "object" && config.ssr !== null ? config.ssr : void 0;
757
+ const userNoExternal = ssrConfig?.noExternal;
824
758
  const pluginNoExternal = ["litestar-vite-plugin"];
825
759
  if (userNoExternal === true) {
826
760
  return true;
@@ -844,11 +778,10 @@ function resolveEnvironmentServerConfig(env) {
844
778
  throw Error(`Unable to determine the host from the environment's APP_URL: [${env.APP_URL}].`);
845
779
  }
846
780
  return {
847
- hmr: { host },
848
781
  host,
849
782
  https: {
850
- key: fs.readFileSync(env.VITE_DEV_SERVER_KEY),
851
- cert: fs.readFileSync(env.VITE_DEV_SERVER_CERT)
783
+ key: fs.readFileSync(env.VITE_SERVER_KEY),
784
+ cert: fs.readFileSync(env.VITE_SERVER_CERT)
852
785
  }
853
786
  };
854
787
  }
@@ -877,7 +810,6 @@ function resolveDevelopmentEnvironmentServerConfig(host) {
877
810
  throw Error(`Unable to find certificate files for your host [${host}] in the [${configPath}/certs] directory.`);
878
811
  }
879
812
  return {
880
- hmr: { host: resolvedHost },
881
813
  host: resolvedHost,
882
814
  https: {
883
815
  key: keyPath,
@@ -915,25 +847,40 @@ function dirname() {
915
847
  }
916
848
  return path.resolve(process.cwd(), "../dist/js");
917
849
  }
850
+ let cachedDevServerPlaceholderCandidates;
851
+ let cachedDevServerPlaceholderPath;
918
852
  function getDevServerPlaceholderCandidates() {
853
+ if (cachedDevServerPlaceholderCandidates) {
854
+ return cachedDevServerPlaceholderCandidates;
855
+ }
919
856
  const distJsRoot = "dist/js";
920
857
  const placeholderName = "dev-server-index.html";
921
858
  const moduleDir = dirname();
922
859
  const candidates = [
923
- path.join(dirname(), placeholderName),
860
+ path.join(moduleDir, placeholderName),
924
861
  path.join(process.cwd(), distJsRoot, placeholderName),
925
862
  path.join(process.cwd(), "src", "js", distJsRoot, placeholderName),
926
863
  path.resolve(process.cwd(), "..", distJsRoot, placeholderName),
927
864
  path.join(moduleDir, distJsRoot, placeholderName)
928
865
  ];
929
- return [...new Set(candidates)];
866
+ cachedDevServerPlaceholderCandidates = [...new Set(candidates)];
867
+ return cachedDevServerPlaceholderCandidates;
930
868
  }
931
869
  async function loadDevServerPlaceholder() {
870
+ if (cachedDevServerPlaceholderPath) {
871
+ try {
872
+ return await fs.promises.readFile(cachedDevServerPlaceholderPath, "utf-8");
873
+ } catch {
874
+ cachedDevServerPlaceholderPath = void 0;
875
+ }
876
+ }
932
877
  const candidates = getDevServerPlaceholderCandidates();
933
878
  let lastError = new Error("Failed to load dev server placeholder index.html");
934
879
  for (const candidatePath of candidates) {
935
880
  try {
936
- return await fs.promises.readFile(candidatePath, "utf-8");
881
+ const content = await fs.promises.readFile(candidatePath, "utf-8");
882
+ cachedDevServerPlaceholderPath = candidatePath;
883
+ return content;
937
884
  } catch (error) {
938
885
  lastError = error;
939
886
  }
@@ -64,12 +64,16 @@ export declare function resolvePageComponent<T>(path: string | string[], pages:
64
64
  * Offset-based pagination props.
65
65
  *
66
66
  * Returned when a route returns Litestar's `OffsetPagination` type.
67
- * Contains items plus metadata for offset/limit pagination.
67
+ * Litestar-Vite flattens pagination metadata as top-level sibling props:
68
+ * the route prop contains the item array, and `total`, `limit`, and `offset`
69
+ * are emitted beside it.
68
70
  *
69
71
  * @example
70
72
  * ```ts
71
73
  * interface User { id: string; name: string }
72
- * const { items, total, limit, offset } = props.users as OffsetPaginationProps<User>
74
+ * type UsersPageProps = { users: User[] } & Omit<OffsetPaginationProps<User>, 'items'>
75
+ *
76
+ * const { users, total, limit, offset } = props as UsersPageProps
73
77
  * ```
74
78
  */
75
79
  export interface OffsetPaginationProps<T> {
@@ -86,12 +90,16 @@ export interface OffsetPaginationProps<T> {
86
90
  * Classic page-based pagination props.
87
91
  *
88
92
  * Returned when a route returns Litestar's `ClassicPagination` type.
89
- * Contains items plus metadata for page number pagination.
93
+ * Litestar-Vite flattens pagination metadata as top-level sibling props:
94
+ * the route prop contains the item array, and `currentPage`, `totalPages`,
95
+ * and `pageSize` are emitted beside it.
90
96
  *
91
97
  * @example
92
98
  * ```ts
93
99
  * interface Post { id: string; title: string }
94
- * const { items, currentPage, totalPages, pageSize } = props.posts as ClassicPaginationProps<Post>
100
+ * type PostsPageProps = { posts: Post[] } & Omit<ClassicPaginationProps<Post>, 'items'>
101
+ *
102
+ * const { posts, currentPage, totalPages, pageSize } = props as PostsPageProps
95
103
  * ```
96
104
  */
97
105
  export interface ClassicPaginationProps<T> {
@@ -108,12 +116,16 @@ export interface ClassicPaginationProps<T> {
108
116
  * Cursor-based pagination props.
109
117
  *
110
118
  * Used for cursor/keyset pagination, commonly with infinite scroll.
111
- * Contains items plus cursor tokens for navigation.
119
+ * Litestar-Vite flattens pagination metadata as top-level sibling props:
120
+ * the route prop contains the item array, and cursor metadata is emitted
121
+ * beside it.
112
122
  *
113
123
  * @example
114
124
  * ```ts
115
125
  * interface Message { id: string; content: string }
116
- * const { items, hasMore, nextCursor } = props.messages as CursorPaginationProps<Message>
126
+ * type MessagesPageProps = { messages: Message[] } & Omit<CursorPaginationProps<Message>, 'items'>
127
+ *
128
+ * const { messages, hasMore, nextCursor } = props as MessagesPageProps
117
129
  * if (hasMore && nextCursor) {
118
130
  * // Fetch more with cursor
119
131
  * }
@@ -139,6 +151,11 @@ export interface CursorPaginationProps<T> {
139
151
  * Union type for any pagination props.
140
152
  *
141
153
  * Use when you need to handle multiple pagination styles.
154
+ * Because pagination metadata is flattened as sibling keys, two paginators in
155
+ * one response can collide on keys such as `total`, `limit`, `offset`,
156
+ * `currentPage`, or `pageSize`. Return one flattened paginator per response,
157
+ * or wrap additional paginators in explicit prop containers with distinct
158
+ * metadata.
142
159
  *
143
160
  * @example
144
161
  * ```ts
@@ -166,11 +183,12 @@ export type PaginationProps<T> = OffsetPaginationProps<T> | ClassicPaginationPro
166
183
  * import { usePage } from '@inertiajs/vue3'
167
184
  *
168
185
  * const page = usePage()
169
- * const scrollProps = page.props.scrollProps as ScrollProps
186
+ * const scrollProps = page.props.scrollProps as Record<string, ScrollProps> | undefined
187
+ * const postsScroll = scrollProps?.posts
170
188
  *
171
189
  * function loadMore() {
172
- * if (scrollProps.nextPage) {
173
- * router.get(url, { [scrollProps.pageName]: scrollProps.nextPage })
190
+ * if (postsScroll?.nextPage) {
191
+ * router.get(url, { [postsScroll.pageName]: postsScroll.nextPage })
174
192
  * }
175
193
  * }
176
194
  * ```
@@ -13,3 +13,20 @@ export declare function resolveInstallHint(pkg?: string): string;
13
13
  * @returns The full command string (e.g., "npx @hey-api/openapi-ts ..." or "bunx @hey-api/openapi-ts ...")
14
14
  */
15
15
  export declare function resolvePackageExecutor(pkg: string, executor?: string): string;
16
+ /**
17
+ * Resolves the package executor command as argv.
18
+ *
19
+ * This is used for actual process execution so package arguments are never
20
+ * shell-joined. The string-returning ``resolvePackageExecutor`` remains the
21
+ * display/back-compat helper.
22
+ */
23
+ export interface PackageExecutorArgvOptions {
24
+ /**
25
+ * Package spec to install for executors that support an explicit package
26
+ * separate from the command binary, e.g. npm exec --package.
27
+ */
28
+ packageSpec?: string;
29
+ /** Binary command exposed by packageSpec. */
30
+ binName?: string;
31
+ }
32
+ export declare function resolvePackageExecutorArgv(args: string[], executor?: string, options?: PackageExecutorArgvOptions): string[];
@@ -63,8 +63,42 @@ function resolvePackageExecutor(pkg, executor) {
63
63
  return `npx ${pkg}`;
64
64
  }
65
65
  }
66
+ function getPackageNameFromSpec(packageSpec) {
67
+ if (packageSpec.startsWith("@")) {
68
+ const versionIndex2 = packageSpec.indexOf("@", packageSpec.indexOf("/") + 1);
69
+ return versionIndex2 === -1 ? packageSpec : packageSpec.slice(0, versionIndex2);
70
+ }
71
+ const versionIndex = packageSpec.indexOf("@");
72
+ return versionIndex === -1 ? packageSpec : packageSpec.slice(0, versionIndex);
73
+ }
74
+ function resolveDenoPackageSpec(packageSpec, binName) {
75
+ if (!binName) return packageSpec;
76
+ const packageName = getPackageNameFromSpec(packageSpec);
77
+ const defaultBinName = packageName.split("/").pop();
78
+ return defaultBinName === binName ? packageSpec : `${packageSpec}/${binName}`;
79
+ }
80
+ function resolvePackageExecutorArgv(args, executor, options = {}) {
81
+ const runtime = executor || detectExecutor();
82
+ const { packageSpec, binName } = options;
83
+ switch (runtime) {
84
+ case "bun":
85
+ return ["bunx", ...packageSpec ? [packageSpec, ...args] : args];
86
+ case "deno":
87
+ return ["deno", "run", "-A", ...packageSpec ? [`npm:${resolveDenoPackageSpec(packageSpec, binName)}`, ...args] : args];
88
+ case "pnpm":
89
+ return ["pnpm", "dlx", ...packageSpec ? [packageSpec, ...args] : args];
90
+ case "yarn":
91
+ return ["yarn", "dlx", ...packageSpec ? [packageSpec, ...args] : args];
92
+ default:
93
+ if (packageSpec && binName) {
94
+ return ["npm", "exec", "--yes", "--package", packageSpec, "--", binName, ...args];
95
+ }
96
+ return ["npx", ...args];
97
+ }
98
+ }
66
99
  export {
67
100
  detectExecutor,
68
101
  resolveInstallHint,
69
- resolvePackageExecutor
102
+ resolvePackageExecutor,
103
+ resolvePackageExecutorArgv
70
104
  };
package/dist/js/nuxt.d.ts CHANGED
@@ -100,6 +100,12 @@ export interface NuxtTypesConfig {
100
100
  * @default false
101
101
  */
102
102
  globalRoute?: boolean;
103
+ /**
104
+ * Fail Vite when type generation fails.
105
+ *
106
+ * Defaults to true during build and false during dev.
107
+ */
108
+ failOnError?: boolean;
103
109
  /**
104
110
  * Debounce time in milliseconds for type regeneration.
105
111
  *
package/dist/js/nuxt.js CHANGED
@@ -2,9 +2,9 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import colors from "picocolors";
4
4
  import { readBridgeConfig } from "./shared/bridge-schema.js";
5
- import { DEBOUNCE_MS } from "./shared/constants.js";
6
5
  import { normalizeHost, resolveHotFilePath, resolveLitestarPort } from "./shared/network.js";
7
- import { createLitestarTypeGenPlugin } from "./shared/typegen-plugin.js";
6
+ import { createLitestarTypeGenPlugin, resolveTypesConfig } from "./shared/typegen-plugin.js";
7
+ import { hmrServerConfig } from "./shared/vite-compat.js";
8
8
  function resolveConfig(config = {}) {
9
9
  let hotFile;
10
10
  let proxyMode = "vite";
@@ -38,74 +38,13 @@ function resolveConfig(config = {}) {
38
38
  if (resolvedLitestarPort !== null) {
39
39
  litestarPort = resolvedLitestarPort;
40
40
  }
41
- let typesConfig = false;
42
- const defaultTypesOutput = "generated";
43
- const buildTypeDefaults = (output) => ({
44
- openapiPath: path.join(output, "openapi.json"),
45
- routesPath: path.join(output, "routes.json"),
46
- pagePropsPath: path.join(output, "inertia-pages.json"),
47
- schemasTsPath: path.join(output, "schemas.ts")
41
+ const typesConfig = resolveTypesConfig({
42
+ requested: config.types,
43
+ pythonConfig: pythonTypesConfig ?? void 0,
44
+ defaultOutput: "generated",
45
+ mergePythonWhenTrue: true,
46
+ mergePythonForObject: true
48
47
  });
49
- if (config.types === true) {
50
- const output = pythonTypesConfig?.output ?? defaultTypesOutput;
51
- const defaults = buildTypeDefaults(output);
52
- typesConfig = {
53
- enabled: true,
54
- output,
55
- openapiPath: pythonTypesConfig?.openapiPath ?? defaults.openapiPath,
56
- routesPath: pythonTypesConfig?.routesPath ?? defaults.routesPath,
57
- pagePropsPath: pythonTypesConfig?.pagePropsPath ?? defaults.pagePropsPath,
58
- schemasTsPath: pythonTypesConfig?.schemasTsPath ?? defaults.schemasTsPath,
59
- generateZod: pythonTypesConfig?.generateZod ?? false,
60
- generateSdk: pythonTypesConfig?.generateSdk ?? true,
61
- generateRoutes: pythonTypesConfig?.generateRoutes ?? true,
62
- generatePageProps: pythonTypesConfig?.generatePageProps ?? true,
63
- generateSchemas: pythonTypesConfig?.generateSchemas ?? true,
64
- globalRoute: pythonTypesConfig?.globalRoute ?? false,
65
- debounce: DEBOUNCE_MS
66
- };
67
- } else if (typeof config.types === "object" && config.types !== null) {
68
- const userProvidedOutput = Object.hasOwn(config.types, "output");
69
- const output = config.types.output ?? pythonTypesConfig?.output ?? defaultTypesOutput;
70
- const defaults = buildTypeDefaults(output);
71
- const openapiFallback = userProvidedOutput ? defaults.openapiPath : pythonTypesConfig?.openapiPath ?? defaults.openapiPath;
72
- const routesFallback = userProvidedOutput ? defaults.routesPath : pythonTypesConfig?.routesPath ?? defaults.routesPath;
73
- const pagePropsFallback = userProvidedOutput ? defaults.pagePropsPath : pythonTypesConfig?.pagePropsPath ?? defaults.pagePropsPath;
74
- const schemasFallback = userProvidedOutput ? defaults.schemasTsPath : pythonTypesConfig?.schemasTsPath ?? defaults.schemasTsPath;
75
- typesConfig = {
76
- enabled: config.types.enabled ?? true,
77
- output,
78
- openapiPath: config.types.openapiPath ?? openapiFallback,
79
- routesPath: config.types.routesPath ?? routesFallback,
80
- pagePropsPath: config.types.pagePropsPath ?? pagePropsFallback,
81
- schemasTsPath: config.types.schemasTsPath ?? schemasFallback,
82
- generateZod: config.types.generateZod ?? pythonTypesConfig?.generateZod ?? false,
83
- generateSdk: config.types.generateSdk ?? pythonTypesConfig?.generateSdk ?? true,
84
- generateRoutes: config.types.generateRoutes ?? pythonTypesConfig?.generateRoutes ?? true,
85
- generatePageProps: config.types.generatePageProps ?? pythonTypesConfig?.generatePageProps ?? true,
86
- generateSchemas: config.types.generateSchemas ?? pythonTypesConfig?.generateSchemas ?? true,
87
- globalRoute: config.types.globalRoute ?? pythonTypesConfig?.globalRoute ?? false,
88
- debounce: config.types.debounce ?? DEBOUNCE_MS
89
- };
90
- } else if (config.types !== false && pythonTypesConfig?.enabled) {
91
- const output = pythonTypesConfig.output ?? defaultTypesOutput;
92
- const defaults = buildTypeDefaults(output);
93
- typesConfig = {
94
- enabled: true,
95
- output,
96
- openapiPath: pythonTypesConfig.openapiPath ?? defaults.openapiPath,
97
- routesPath: pythonTypesConfig.routesPath ?? defaults.routesPath,
98
- pagePropsPath: pythonTypesConfig.pagePropsPath ?? defaults.pagePropsPath,
99
- schemasTsPath: pythonTypesConfig.schemasTsPath ?? defaults.schemasTsPath,
100
- generateZod: pythonTypesConfig.generateZod ?? false,
101
- generateSdk: pythonTypesConfig.generateSdk ?? true,
102
- generateRoutes: pythonTypesConfig.generateRoutes ?? true,
103
- generatePageProps: pythonTypesConfig.generatePageProps ?? true,
104
- generateSchemas: pythonTypesConfig.generateSchemas ?? true,
105
- globalRoute: pythonTypesConfig.globalRoute ?? false,
106
- debounce: DEBOUNCE_MS
107
- };
108
- }
109
48
  return {
110
49
  apiProxy: config.apiProxy ?? "http://localhost:8000",
111
50
  apiPrefix: config.apiPrefix ?? "/api",
@@ -154,13 +93,14 @@ function createProxyPlugin(config) {
154
93
  strictPort: true
155
94
  } : {},
156
95
  // Vite serves HMR on a separate internal port; browsers reach it through
157
- // Litestar's /static/vite-hmr WebSocket handler.
158
- hmr: {
96
+ // Litestar's /static/vite-hmr WebSocket handler. Vite 8.1 moved these network
97
+ // options from server.hmr.* to server.ws.*; hmrServerConfig picks the right key.
98
+ ...hmrServerConfig({
159
99
  port: hmrPort,
160
100
  host: "127.0.0.1",
161
101
  ...browserHmrPort !== void 0 ? { clientPort: browserHmrPort } : {},
162
102
  ...config.litestarPort !== void 0 ? { path: hmrPath, protocol: "ws" } : {}
163
- }
103
+ })
164
104
  }
165
105
  };
166
106
  },
@@ -25,6 +25,7 @@ export interface BridgeTypesConfig {
25
25
  generatePageProps: boolean;
26
26
  generateSchemas: boolean;
27
27
  globalRoute: boolean;
28
+ failOnError?: boolean;
28
29
  }
29
30
  export interface BridgeSpaConfig {
30
31
  /** Use script element instead of data-page attribute for Inertia page data */