litestar-vite-plugin 0.22.2 → 0.23.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.
@@ -45,6 +45,12 @@ interface AstroConfigPartial {
45
45
  port?: number;
46
46
  strictPort?: boolean;
47
47
  proxy?: Record<string, unknown>;
48
+ hmr?: {
49
+ protocol?: "ws" | "wss";
50
+ host?: string;
51
+ clientPort?: number;
52
+ path?: string;
53
+ };
48
54
  };
49
55
  };
50
56
  }
package/dist/js/astro.js CHANGED
@@ -2,7 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { readBridgeConfig } from "./shared/bridge-schema.js";
4
4
  import { DEBOUNCE_MS } from "./shared/constants.js";
5
- import { normalizeHost, resolveHotFilePath } from "./shared/network.js";
5
+ import { normalizeHost, resolveHotFilePath, resolveLitestarPort } from "./shared/network.js";
6
6
  import { createLitestarTypeGenPlugin } from "./shared/typegen-plugin.js";
7
7
  function resolveConfig(config = {}) {
8
8
  let hotFile;
@@ -19,6 +19,8 @@ function resolveConfig(config = {}) {
19
19
  }
20
20
  }
21
21
  const runtime = readBridgeConfig();
22
+ let assetUrl;
23
+ let litestarPort;
22
24
  if (runtime) {
23
25
  hasPythonConfig = true;
24
26
  const hot = runtime.hotFile;
@@ -26,10 +28,15 @@ function resolveConfig(config = {}) {
26
28
  proxyMode = runtime.proxyMode;
27
29
  port = runtime.port;
28
30
  pythonExecutor = runtime.executor;
31
+ assetUrl = runtime.assetUrl;
29
32
  if (runtime.types) {
30
33
  pythonTypesConfig = runtime.types;
31
34
  }
32
35
  }
36
+ const resolvedLitestarPort = resolveLitestarPort(runtime?.litestarPort, runtime?.appUrl);
37
+ if (resolvedLitestarPort !== null) {
38
+ litestarPort = resolvedLitestarPort;
39
+ }
33
40
  let typesConfig = false;
34
41
  const defaultTypesOutput = "src/generated";
35
42
  const buildTypeDefaults = (output) => ({
@@ -106,6 +113,8 @@ function resolveConfig(config = {}) {
106
113
  hotFile,
107
114
  proxyMode,
108
115
  port,
116
+ litestarPort,
117
+ assetUrl,
109
118
  executor: pythonExecutor,
110
119
  hasPythonConfig
111
120
  };
@@ -114,6 +123,7 @@ function createProxyPlugin(config) {
114
123
  return {
115
124
  name: "litestar-astro-proxy",
116
125
  config() {
126
+ const hmrPath = `${(config.assetUrl ?? "/static").replace(/\/$/, "")}/vite-hmr`;
117
127
  return {
118
128
  server: {
119
129
  // Force IPv4 binding for consistency with Python proxy configuration
@@ -125,6 +135,15 @@ function createProxyPlugin(config) {
125
135
  port: config.port,
126
136
  strictPort: true
127
137
  } : {},
138
+ // Route HMR through the Litestar port so DevTools never sees the framework port.
139
+ ...config.litestarPort !== void 0 ? {
140
+ hmr: {
141
+ protocol: "ws",
142
+ host: "127.0.0.1",
143
+ clientPort: config.litestarPort,
144
+ path: hmrPath
145
+ }
146
+ } : {},
128
147
  proxy: {
129
148
  [config.apiPrefix]: {
130
149
  target: config.apiProxy,
package/dist/js/nuxt.js CHANGED
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import colors from "picocolors";
4
4
  import { readBridgeConfig } from "./shared/bridge-schema.js";
5
5
  import { DEBOUNCE_MS } from "./shared/constants.js";
6
- import { normalizeHost, resolveHotFilePath } from "./shared/network.js";
6
+ import { normalizeHost, resolveHotFilePath, resolveLitestarPort } from "./shared/network.js";
7
7
  import { createLitestarTypeGenPlugin } from "./shared/typegen-plugin.js";
8
8
  function resolveConfig(config = {}) {
9
9
  let hotFile;
@@ -19,6 +19,8 @@ function resolveConfig(config = {}) {
19
19
  }
20
20
  }
21
21
  let pythonExecutor;
22
+ let assetUrl;
23
+ let litestarPort;
22
24
  const runtime = readBridgeConfig();
23
25
  if (runtime) {
24
26
  hasPythonConfig = true;
@@ -27,10 +29,15 @@ function resolveConfig(config = {}) {
27
29
  proxyMode = runtime.proxyMode;
28
30
  devPort = runtime.port;
29
31
  pythonExecutor = runtime.executor;
32
+ assetUrl = runtime.assetUrl;
30
33
  if (runtime.types) {
31
34
  pythonTypesConfig = runtime.types;
32
35
  }
33
36
  }
37
+ const resolvedLitestarPort = resolveLitestarPort(runtime?.litestarPort, runtime?.appUrl);
38
+ if (resolvedLitestarPort !== null) {
39
+ litestarPort = resolvedLitestarPort;
40
+ }
34
41
  let typesConfig = false;
35
42
  const defaultTypesOutput = "generated";
36
43
  const buildTypeDefaults = (output) => ({
@@ -107,6 +114,8 @@ function resolveConfig(config = {}) {
107
114
  hotFile,
108
115
  proxyMode,
109
116
  devPort,
117
+ litestarPort,
118
+ assetUrl,
110
119
  executor: config.executor ?? pythonExecutor,
111
120
  hasPythonConfig
112
121
  };
@@ -131,6 +140,8 @@ function createProxyPlugin(config) {
131
140
  name: "litestar-nuxt-proxy",
132
141
  async config() {
133
142
  hmrPort = await getPort();
143
+ const hmrPath = `${(config.assetUrl ?? "/static").replace(/\/$/, "")}/vite-hmr`;
144
+ const browserHmrPort = config.litestarPort ?? config.devPort;
134
145
  return {
135
146
  server: {
136
147
  // Force IPv4 binding for consistency with Python proxy configuration
@@ -142,11 +153,13 @@ function createProxyPlugin(config) {
142
153
  port: config.devPort,
143
154
  strictPort: true
144
155
  } : {},
145
- // Avoid HMR port collisions by letting Vite pick a free port for WS
156
+ // Vite serves HMR on a separate internal port; browsers reach it through
157
+ // Litestar's /static/vite-hmr WebSocket handler.
146
158
  hmr: {
147
159
  port: hmrPort,
148
160
  host: "127.0.0.1",
149
- clientPort: config.devPort
161
+ ...browserHmrPort !== void 0 ? { clientPort: browserHmrPort } : {},
162
+ ...config.litestarPort !== void 0 ? { path: hmrPath, protocol: "ws" } : {}
150
163
  }
151
164
  }
152
165
  };
@@ -34,6 +34,13 @@ export interface BridgeSchema {
34
34
  assetUrl: string;
35
35
  deployAssetUrl: string | null;
36
36
  appUrl: string | null;
37
+ /**
38
+ * Litestar dev server port. Used by framework integrations to set
39
+ * `vite.server.hmr.clientPort`, ensuring the browser connects to Litestar
40
+ * (not the framework dev server) for HMR — preserving the single-port
41
+ * contract.
42
+ */
43
+ litestarPort: number | null;
37
44
  bundleDir: string;
38
45
  resourceDir: string;
39
46
  staticDir: string;
@@ -4,6 +4,7 @@ const allowedTopLevelKeys = /* @__PURE__ */ new Set([
4
4
  "assetUrl",
5
5
  "deployAssetUrl",
6
6
  "appUrl",
7
+ "litestarPort",
7
8
  "bundleDir",
8
9
  "resourceDir",
9
10
  "staticDir",
@@ -71,6 +72,14 @@ function assertOptionalNullableString(obj, key) {
71
72
  }
72
73
  return value;
73
74
  }
75
+ function assertOptionalNullableInteger(obj, key) {
76
+ const value = obj[key];
77
+ if (value === void 0 || value === null) return null;
78
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
79
+ fail(`"${key}" must be a positive integer or null`);
80
+ }
81
+ return value;
82
+ }
74
83
  function assertOptionalString(obj, key) {
75
84
  const value = obj[key];
76
85
  if (value === void 0) return void 0;
@@ -159,6 +168,7 @@ function parseBridgeSchema(value) {
159
168
  const assetUrl = assertString(obj, "assetUrl");
160
169
  const deployAssetUrl = assertNullableString(obj, "deployAssetUrl");
161
170
  const appUrl = assertOptionalNullableString(obj, "appUrl");
171
+ const litestarPort = assertOptionalNullableInteger(obj, "litestarPort");
162
172
  const bundleDir = assertString(obj, "bundleDir");
163
173
  const resourceDir = assertString(obj, "resourceDir");
164
174
  const staticDir = assertString(obj, "staticDir");
@@ -179,6 +189,7 @@ function parseBridgeSchema(value) {
179
189
  assetUrl,
180
190
  deployAssetUrl,
181
191
  appUrl,
192
+ litestarPort,
182
193
  bundleDir,
183
194
  resourceDir,
184
195
  staticDir,
@@ -21,6 +21,21 @@
21
21
  * ```
22
22
  */
23
23
  export declare function normalizeHost(host: string): string;
24
+ /**
25
+ * Resolve the Litestar dev server port for HMR routing.
26
+ *
27
+ * Framework integrations (Astro/Nuxt/SvelteKit) need this port to set
28
+ * `vite.server.hmr.clientPort` so the browser opens the HMR WebSocket against
29
+ * Litestar — NOT the framework dev server's port — preserving the
30
+ * single-port-via-ASGI contract.
31
+ *
32
+ * Resolution order:
33
+ * 1. `bridge.litestarPort` (preferred; written by Python ≥0.23.0).
34
+ * 2. Parse `bridge.appUrl` (works with older bridges that lack `litestarPort`).
35
+ * 3. `LITESTAR_PORT` / `PORT` env var.
36
+ * 4. `null` if no signal.
37
+ */
38
+ export declare function resolveLitestarPort(bridgeLitestarPort: number | null | undefined, bridgeAppUrl: string | null | undefined, env?: NodeJS.ProcessEnv): number | null;
24
39
  /**
25
40
  * Resolve the absolute hot file path from bundleDir + hotFile.
26
41
  *
@@ -8,6 +8,29 @@ function normalizeHost(host) {
8
8
  }
9
9
  return host;
10
10
  }
11
+ function resolveLitestarPort(bridgeLitestarPort, bridgeAppUrl, env = process.env) {
12
+ if (typeof bridgeLitestarPort === "number" && Number.isInteger(bridgeLitestarPort) && bridgeLitestarPort > 0) {
13
+ return bridgeLitestarPort;
14
+ }
15
+ if (typeof bridgeAppUrl === "string" && bridgeAppUrl.length > 0) {
16
+ try {
17
+ const parsed = new URL(bridgeAppUrl);
18
+ if (parsed.port) {
19
+ const p = Number.parseInt(parsed.port, 10);
20
+ if (!Number.isNaN(p) && p > 0) return p;
21
+ }
22
+ if (parsed.protocol === "https:") return 443;
23
+ if (parsed.protocol === "http:") return 80;
24
+ } catch {
25
+ }
26
+ }
27
+ const raw = env.LITESTAR_PORT ?? env.PORT;
28
+ if (raw) {
29
+ const p = Number.parseInt(raw, 10);
30
+ if (!Number.isNaN(p) && p > 0) return p;
31
+ }
32
+ return null;
33
+ }
11
34
  function resolveHotFilePath(bundleDir, hotFile, rootDir = process.cwd()) {
12
35
  if (path.isAbsolute(hotFile)) {
13
36
  return hotFile;
@@ -21,5 +44,6 @@ function resolveHotFilePath(bundleDir, hotFile, rootDir = process.cwd()) {
21
44
  }
22
45
  export {
23
46
  normalizeHost,
24
- resolveHotFilePath
47
+ resolveHotFilePath,
48
+ resolveLitestarPort
25
49
  };
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import colors from "picocolors";
4
4
  import { readBridgeConfig } from "./shared/bridge-schema.js";
5
5
  import { DEBOUNCE_MS } from "./shared/constants.js";
6
- import { normalizeHost, resolveHotFilePath } from "./shared/network.js";
6
+ import { normalizeHost, resolveHotFilePath, resolveLitestarPort } from "./shared/network.js";
7
7
  import { createLitestarTypeGenPlugin } from "./shared/typegen-plugin.js";
8
8
  function resolveConfig(config = {}) {
9
9
  let hotFile;
@@ -19,6 +19,8 @@ function resolveConfig(config = {}) {
19
19
  }
20
20
  }
21
21
  let pythonExecutor;
22
+ let assetUrl;
23
+ let litestarPort;
22
24
  const runtime = readBridgeConfig();
23
25
  if (runtime) {
24
26
  hasPythonConfig = true;
@@ -27,10 +29,15 @@ function resolveConfig(config = {}) {
27
29
  proxyMode = runtime.proxyMode;
28
30
  port = runtime.port;
29
31
  pythonExecutor = runtime.executor;
32
+ assetUrl = runtime.assetUrl;
30
33
  if (runtime.types) {
31
34
  pythonTypesConfig = runtime.types;
32
35
  }
33
36
  }
37
+ const resolvedLitestarPort = resolveLitestarPort(runtime?.litestarPort, runtime?.appUrl);
38
+ if (resolvedLitestarPort !== null) {
39
+ litestarPort = resolvedLitestarPort;
40
+ }
34
41
  let typesConfig = false;
35
42
  const defaultTypesOutput = "src/lib/generated";
36
43
  const buildTypeDefaults = (output) => ({
@@ -107,6 +114,8 @@ function resolveConfig(config = {}) {
107
114
  hotFile,
108
115
  proxyMode,
109
116
  port,
117
+ litestarPort,
118
+ assetUrl,
110
119
  executor: config.executor ?? pythonExecutor,
111
120
  hasPythonConfig
112
121
  };
@@ -118,6 +127,7 @@ function litestarSvelteKit(userConfig = {}) {
118
127
  name: "litestar-sveltekit",
119
128
  enforce: "pre",
120
129
  config() {
130
+ const hmrPath = `${(config.assetUrl ?? "/static").replace(/\/$/, "")}/vite-hmr`;
121
131
  return {
122
132
  server: {
123
133
  // Force IPv4 binding for consistency with Python proxy configuration
@@ -129,6 +139,15 @@ function litestarSvelteKit(userConfig = {}) {
129
139
  port: config.port,
130
140
  strictPort: true
131
141
  } : {},
142
+ // Route HMR through the Litestar port so DevTools never sees the framework port.
143
+ ...config.litestarPort !== void 0 ? {
144
+ hmr: {
145
+ protocol: "ws",
146
+ host: "127.0.0.1",
147
+ clientPort: config.litestarPort,
148
+ path: hmrPath
149
+ }
150
+ } : {},
132
151
  proxy: {
133
152
  [config.apiPrefix]: {
134
153
  target: config.apiProxy,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litestar-vite-plugin",
3
- "version": "0.22.2",
3
+ "version": "0.23.0",
4
4
  "type": "module",
5
5
  "description": "Litestar plugin for Vite.",
6
6
  "keywords": [