@mauroandre/velojs 0.0.53 → 0.0.55

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/README.md CHANGED
@@ -159,6 +159,8 @@ export const Component = () => {
159
159
  npm run dev # http://localhost:3000
160
160
  ```
161
161
 
162
+ To expose the dev server beyond loopback — a VPS behind a reverse proxy, an app reached by hostname — set `HOST=0.0.0.0`, in the shell or in the project's `.env`; no `--host` flag by hand. The security trade-off: a broad bind in dev (`0.0.0.0`, `::`, or `--host` with no value) turns Vite's Host check off, so a request whose `Host` header carries that domain is served instead of 403'd — the DNS-rebinding protection you get on loopback is consciously given up. Stay on localhost unless you are behind a proxy; a `server.allowedHosts` declared in `vite.config.ts` still pins the allowed list.
163
+
162
164
  ### Configuration
163
165
 
164
166
  ```typescript
@@ -167,6 +169,7 @@ veloPlugin({
167
169
  routesFile: "routes.tsx", // default
168
170
  serverInit: "server.tsx", // default
169
171
  clientInit: "client.tsx", // default
172
+ hostname: "127.0.0.1", // bind interface, dev and production (the HOST env wins)
170
173
  });
171
174
  ```
172
175
 
@@ -731,7 +734,24 @@ const { data, snapshot, closed, error } = useEventStream(stream, {
731
734
  | `closed` | `true` when server closed the stream |
732
735
  | `error` | Parse or connection error, if any |
733
736
 
734
- Lifecycle is automatic: opens on mount, closes on unmount, re-opens fresh when `channel` changes.
737
+ Lifecycle is automatic: opens on mount, closes on unmount, re-opens when `channel` changes.
738
+
739
+ **Reconnect is stale-while-revalidate.** When the connection re-opens — `channel`, `stream` or `enabled` changed — `data` and `snapshot` keep their previous values until the first event of the new connection overwrites them, the same choice the loader store makes (a refresh must not blank the screen). A channel switch therefore never paints an empty state; only `closed` and `error` reset, because a re-open is a fresh attempt. First mount still starts at `null` and fills in when the snapshot arrives. An app that wants to blank the view on a new channel can clear the signals itself when the channel changes.
740
+
741
+ **Channel coming from loader data?** Pass `enabled` so the hook does not connect before the channel exists — the first render of a SPA navigation has `data = null`, and connecting with `channel: undefined` reaches the server without `?channel=`, which a resolver answers with `null` → 403 → a flash of `closed`:
742
+
743
+ ```tsx
744
+ const { data } = useLoader<{ id: string }>();
745
+ const channel = data.value?.id;
746
+
747
+ // no connection until the loader resolves the channel
748
+ const { snapshot, closed } = useEventStream(stream_logs, {
749
+ // spread only when it exists — `exactOptionalPropertyTypes` (the repo's own
750
+ // tsconfig) rejects an explicit `undefined` in an optional property
751
+ ...(channel != null && { channel }),
752
+ enabled: channel != null,
753
+ });
754
+ ```
735
755
 
736
756
  ### `poll` helper
737
757
 
@@ -853,6 +873,9 @@ const { send, status, lastMessage, close } = useSocket(socket_terminal, {
853
873
  ```
854
874
 
855
875
  - `status: Signal<"connecting" | "open" | "closed">` — reactive.
876
+ - `lastMessage: Signal<string | Uint8Array | null>` — last frame received.
877
+ - **Reconnect preserves `lastMessage`** — changing `stub.__path`, `channel` or `enabled` opens a new socket, but the previous frame stays until the first frame of the new one arrives (stale-while-revalidate, same as `useEventStream`); `status` restarts at `"connecting"` and `error` resets. Swapping the stub object with the same `__path` does not reconnect. Clear `lastMessage` yourself in `onOpen` if a channel switch must blank the view.
878
+ - **Channel coming from loader data?** Pass `enabled: channel != null` so the socket only opens once the channel resolves — same reason as `useEventStream`.
856
879
  - **No auto-reconnect** — sockets are usually stateful (pty sessions, collaborative state); blind reconnect loses state silently. Re-mount or toggle `enabled` to reconnect.
857
880
 
858
881
  ### Testing
@@ -1252,6 +1275,7 @@ Callbacks queue until the server starts. If called after startup, executes immed
1252
1275
  | Variable | Default | Description |
1253
1276
  |----------|---------|-------------|
1254
1277
  | `PORT` | `3000` | Server port. Overrides the `port` set in `defineConfig` |
1278
+ | `HOST` | — | Bind interface, in dev and production. Overrides `defineConfig`'s `hostname`; in dev an explicit `--host`/`server.host` still wins |
1255
1279
  | `NODE_ENV` | — | Set automatically by `velojs start`. Enables static file serving |
1256
1280
  | `STATIC_BASE_URL` | `""` | CDN/bucket prefix for static assets |
1257
1281
 
@@ -1455,6 +1479,8 @@ interface VeloConfig {
1455
1479
  routesFile?: string; // default: "routes.tsx"
1456
1480
  serverInit?: string; // default: "server.tsx"
1457
1481
  clientInit?: string; // default: "client.tsx"
1482
+ port?: number; // default: 3000; the PORT env wins
1483
+ hostname?: string; // bind interface, dev and production; the HOST env wins
1458
1484
  }
1459
1485
  ```
1460
1486
 
@@ -389,6 +389,34 @@ useEventStream(stream, {
389
389
 
390
390
  The hook handles `EventSource` lifecycle automatically: opens on mount, closes on unmount, re-opens with a fresh connection when `channel` changes.
391
391
 
392
+ ### Reconnect keeps the last value (stale-while-revalidate)
393
+
394
+ When the connection re-opens — `channel`, `stream` or `enabled` changed — `data` and `snapshot` keep their previous values until the first event of the new connection overwrites them. It is the same choice the loader store makes: a refresh must not blank the screen. Without it, a channel switch (navigating from `/deploys/1` to `/deploys/2` with the stream in a layout that survives the navigation) would paint an empty state for a full round-trip before the new snapshot arrives.
395
+
396
+ `closed` and `error` do reset on a re-open: a reconnect is a fresh attempt, not a closed stream. First mount still starts at `null` and fills in when the snapshot arrives — seed it from loader data if that first frame matters to your UI.
397
+
398
+ If a channel switch must blank the view, clear the signals yourself in an effect keyed on the channel.
399
+
400
+ ### Don't connect before the channel exists
401
+
402
+ When `channel` derives from loader data, the first render of a SPA navigation has it `undefined` (the loader fetch is post-mount). The hook would then connect **without** `?channel=`, and a resolver that returns `null`/`undefined` for a missing channel answers **403** — the client sees `closed = true` and paints a broken state for the milliseconds until the loader resolves and the effect reconnects.
403
+
404
+ Pass `enabled` to gate the connection on the channel:
405
+
406
+ ```tsx
407
+ const { data } = useLoader<{ id: string }>();
408
+ const channel = data.value?.id;
409
+
410
+ const { snapshot, closed } = useEventStream(stream_logs, {
411
+ // spread only when it exists — `exactOptionalPropertyTypes` (the repo's own
412
+ // tsconfig) rejects an explicit `undefined` in an optional property
413
+ ...(channel != null && { channel }),
414
+ enabled: channel != null, // no connection until the loader resolves the id
415
+ });
416
+ ```
417
+
418
+ That flash is a documentation-level concern, not a hook one: the hook cannot distinguish "channel still loading" from "stream has no channel". `enabled` is the switch that expresses the difference.
419
+
392
420
  ## Heartbeat
393
421
 
394
422
  VeloJS sends a `:heartbeat` SSE comment every 20 seconds by default. Without this, idle connections are closed by Cloudflare (~100s), Nginx (~60s), and AWS ALB (~60s) — and the client doesn't even notice.
@@ -175,6 +175,8 @@ npm run dev
175
175
 
176
176
  Open `http://localhost:3000` in your browser. Changes to your code will hot-reload instantly.
177
177
 
178
+ The dev server binds loopback by default. To expose it beyond that — a VPS behind a reverse proxy, an app reached by a real hostname — set `HOST=0.0.0.0`, in the shell or in the project's `.env`; no `--host` flag by hand. A broad bind in dev (`0.0.0.0`, `::`, or `--host` with no value) also turns Vite's Host check off, so a request whose `Host` header carries that domain is served instead of 403'd — the DNS-rebinding protection you get on loopback is consciously traded away. A `server.allowedHosts` declared in `vite.config.ts` still pins the allowed list.
179
+
178
180
  ## Configuration
179
181
 
180
182
  The `veloPlugin()` accepts optional configuration:
@@ -79,6 +79,12 @@ By default `velojs start` binds to **all interfaces** (`::`) — the norm for No
79
79
  2. The `hostname` field in `defineConfig`.
80
80
  3. The Node default (all interfaces).
81
81
 
82
+ The same declaration covers the dev server, with one extra step at the top: `velojs dev --host <value>` (or a `server.host` in `vite.config.ts`) wins, then `HOST`, then `hostname`, then Vite's default (loopback). `HOST=0.0.0.0` in the shell or in the project's `.env` therefore lifts `velojs dev` beyond loopback with no flag by hand.
83
+
84
+ ### Dev behind a reverse proxy: the Host check
85
+
86
+ When a dev server binds broad it sits behind the proxy (or is reached by hostname), and requests arrive carrying the proxy's domain in the `Host` header. Vite's DNS-rebinding protection answers `403 Blocked request. This host is not allowed.` for any host outside its allow list — so the framework opens it whenever the declared bind is broad (`0.0.0.0`, `::`, or `--host` with no value): any `Host` is served. That is a conscious trade-off — the guard exists for loopback development, and leaving loopback means giving it up. If the project declared `server.allowedHosts`, that list wins and stays restrictive even on a broad bind.
87
+
82
88
  ### Secure bind for local/sensitive apps
83
89
 
84
90
  If your app is **local and single-user** — a cockpit with a terminal, file access, real user data — it must listen on loopback only. An all-interfaces bind exposes that surface to the whole network without any error: the developer finds out by scanning, not by the app telling them.
@@ -151,5 +151,6 @@ Callbacks registered with `onServer` queue until the server starts. If called af
151
151
  | Variable | Default | Description |
152
152
  |----------|---------|-------------|
153
153
  | `PORT` | `3000` | The port the server listens on. Overrides `defineConfig`'s `port`. |
154
+ | `HOST` | — | The bind interface, in dev and production. Overrides `defineConfig`'s `hostname`; in dev an explicit `--host`/`server.host` still wins. |
154
155
  | `NODE_ENV` | — | Set automatically by `velojs start` to `production` |
155
156
  | `STATIC_BASE_URL` | `""` | CDN/bucket prefix for static assets |
@@ -206,6 +206,22 @@ const { send, status, lastMessage, error, close } = useSocket(stub, {
206
206
  - `send(msg)` — queues messages sent while `connecting`; drops messages sent while `closed`.
207
207
  - `close(code?, reason?)` — client-initiated disconnect.
208
208
 
209
+ **Reconnect keeps `lastMessage` (stale-while-revalidate).** Changing `stub.__path`, `channel` or `enabled` opens a fresh socket, but the previous frame stays in `lastMessage` until the first frame of the new socket arrives — switching from a worker terminal to another one does not paint a blank view for a round-trip. It is the same choice the loader store and `useEventStream` make. `status` restarts at `"connecting"` and `error` resets, because a reconnect is a fresh attempt; a new stub object with the same `__path` is not a reconnect at all. If a channel switch must blank the view, clear `lastMessage` in `onOpen`.
210
+
211
+ **Channel coming from loader data?** The first render of a SPA navigation has no channel yet (the loader fetch is post-mount), and connecting then reaches the server without `?channel=` — for a guarded socket that is an error, not a connection. Pass `enabled: channel != null` to wait for the loader:
212
+
213
+ ```tsx
214
+ const { data } = useLoader<{ workerId: string }>();
215
+ const channel = data.value?.workerId;
216
+
217
+ const { send, status, lastMessage } = useSocket(socket_terminal, {
218
+ // spread only when it exists — `exactOptionalPropertyTypes` (the repo's own
219
+ // tsconfig) rejects an explicit `undefined` in an optional property
220
+ ...(channel != null && { channel }),
221
+ enabled: channel != null, // no socket until the loader resolves the id
222
+ });
223
+ ```
224
+
209
225
  **No auto-reconnect.** If the server closes the socket (network drop, server restart), `status` goes to `"closed"` and stays there. The caller decides whether/when to reconnect — typically by toggling `enabled` or re-mounting the component. This is intentional: sockets are usually stateful (pty session, collaborative cursor) and a blind reconnect would silently lose state.
210
226
 
211
227
  ## Testing
@@ -138,5 +138,6 @@ interface VeloConfig {
138
138
  serverInit?: string; // default: "server.tsx"
139
139
  clientInit?: string; // default: "client.tsx"
140
140
  port?: number; // default: 3000 (the PORT env always wins)
141
+ hostname?: string; // bind interface, dev and production; the HOST env wins
141
142
  }
142
143
  ```
package/dist/config.d.ts CHANGED
@@ -26,9 +26,11 @@ export interface VeloConfig {
26
26
  */
27
27
  port?: number;
28
28
  /**
29
- * Interface de bind do servidor de produção. A env `HOST` tem precedência.
30
- * Default do Node: todas as interfaces (o que Docker/cloud esperam).
31
- * Apps locais/sensíveis devem declarar "127.0.0.1".
29
+ * Interface de bind do servidor, em dev e em produção. A env `HOST` tem
30
+ * precedência nos dois modos; em dev a ordem é: `server.host` do Vite
31
+ * (`--host`/vite.config) > env `HOST` > este campo. Default do Node:
32
+ * todas as interfaces (o que Docker/cloud esperam). Apps
33
+ * locais/sensíveis devem declarar "127.0.0.1".
32
34
  */
33
35
  hostname?: string;
34
36
  /**
package/dist/hooks.d.ts CHANGED
@@ -166,6 +166,13 @@ export declare function usePathname(): string;
166
166
  *
167
167
  * The connection closes automatically when the component unmounts or when
168
168
  * the channel changes (re-opens with new channel).
169
+ *
170
+ * Stale-while-revalidate: on a re-open (new `channel`/`stream`/`enabled`) the
171
+ * previous `data`/`snapshot` stay in place until the first event of the new
172
+ * connection overwrites them — the same choice the loader store makes. Only
173
+ * `closed`/`error` reset, because a re-open is a fresh attempt. To connect
174
+ * only once a channel derived from loader data has resolved, pass
175
+ * `enabled: channel != null`.
169
176
  */
170
177
  export declare function useEventStream<TEvent, TSnapshot = TEvent>(stream: EventStream<TEvent, TSnapshot>, options?: {
171
178
  channel?: string;
@@ -218,6 +225,11 @@ export interface UseSocketApi {
218
225
  * becomes `"closed"` and stays that way — user flow decides whether to
219
226
  * reconnect (typically by re-mounting or toggling `enabled`).
220
227
  *
228
+ * Stale-while-revalidate: on reconnect (new `stub.__path`/`channel`/`enabled`)
229
+ * `lastMessage` keeps its previous value until the first frame of the new
230
+ * socket arrives; `status`/`error` restart as a fresh attempt
231
+ * (`"connecting"` → `"open"`).
232
+ *
221
233
  * ```tsx
222
234
  * const { send, status, lastMessage } = useSocket(socket_terminal, {
223
235
  * channel: workerId,
package/dist/hooks.js CHANGED
@@ -45,10 +45,10 @@ function B(e, n) {
45
45
  if (!t)
46
46
  return { data: u(null), loading: u(!1), refetch: () => {
47
47
  } };
48
- const o = N(t), a = m(t), c = () => D(t), d = S(!0);
48
+ const o = N(t), a = m(t), l = () => D(t), d = S(!0);
49
49
  return E(() => {
50
- d.current && (d.current = !1, o.value !== null) || c();
51
- }, r ?? []), { data: o, loading: a, refetch: c };
50
+ d.current && (d.current = !1, o.value !== null) || l();
51
+ }, r ?? []), { data: o, loading: a, refetch: l };
52
52
  }
53
53
  function J() {
54
54
  const [, e] = b();
@@ -69,15 +69,15 @@ function x() {
69
69
  return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__pathname ?? "/" : (b(), window.location.pathname);
70
70
  }
71
71
  function G(e, n = {}) {
72
- const t = u(null), r = u(null), o = u(!1), a = u(null), { channel: c, enabled: d = !0 } = n;
72
+ const t = u(null), r = u(null), o = u(!1), a = u(null), { channel: l, enabled: d = !0 } = n;
73
73
  return E(() => {
74
74
  if (typeof window > "u" || !d) return;
75
75
  if (!e.__path) {
76
76
  console.warn("[velojs] useEventStream: stream has no __path");
77
77
  return;
78
78
  }
79
- t.value = null, r.value = null, o.value = !1, a.value = null;
80
- const g = c ? `${e.__path}?channel=${encodeURIComponent(c)}` : e.__path, s = new EventSource(g);
79
+ o.value = !1, a.value = null;
80
+ const g = l ? `${e.__path}?channel=${encodeURIComponent(l)}` : e.__path, s = new EventSource(g);
81
81
  return s.addEventListener("snapshot", (h) => {
82
82
  try {
83
83
  r.value = JSON.parse(h.data);
@@ -98,41 +98,41 @@ function G(e, n = {}) {
98
98
  }), () => {
99
99
  s.close();
100
100
  };
101
- }, [e, c, d]), { data: t, snapshot: r, closed: o, error: a };
101
+ }, [e, l, d]), { data: t, snapshot: r, closed: o, error: a };
102
102
  }
103
103
  function F(e, n = {}) {
104
- const t = u("connecting"), r = u(null), o = u(null), a = S(null), c = S([]), { channel: d, onMessage: g, onOpen: s, onClose: h, onError: y, enabled: L = !0 } = n;
104
+ const t = u("connecting"), r = u(null), o = u(null), a = S(null), l = S([]), { channel: d, onMessage: g, onOpen: s, onClose: h, onError: y, enabled: L = !0 } = n;
105
105
  return E(() => {
106
106
  if (typeof window > "u" || !L) return;
107
107
  if (!e.__path) {
108
108
  console.warn("[velojs] useSocket: stub has no __path");
109
109
  return;
110
110
  }
111
- t.value = "connecting", r.value = null, o.value = null;
111
+ t.value = "connecting", o.value = null;
112
112
  const v = `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}`, i = d ? `${v}${e.__path}?channel=${encodeURIComponent(d)}` : `${v}${e.__path}`, f = new WebSocket(i);
113
113
  return f.binaryType = "arraybuffer", a.current = f, f.addEventListener("open", () => {
114
114
  t.value = "open";
115
- const l = c.current;
116
- c.current = [];
117
- for (const _ of l)
115
+ const c = l.current;
116
+ l.current = [];
117
+ for (const _ of c)
118
118
  try {
119
119
  f.send(_);
120
120
  } catch {
121
121
  }
122
122
  s?.();
123
- }), f.addEventListener("message", (l) => {
123
+ }), f.addEventListener("message", (c) => {
124
124
  let _;
125
- if (typeof l.data == "string")
126
- _ = l.data;
127
- else if (l.data instanceof ArrayBuffer)
128
- _ = new Uint8Array(l.data);
125
+ if (typeof c.data == "string")
126
+ _ = c.data;
127
+ else if (c.data instanceof ArrayBuffer)
128
+ _ = new Uint8Array(c.data);
129
129
  else
130
130
  return;
131
131
  r.value = _, g?.(_);
132
- }), f.addEventListener("close", (l) => {
133
- t.value = "closed", a.current = null, h?.(l);
134
- }), f.addEventListener("error", (l) => {
135
- o.value = l, y?.(l);
132
+ }), f.addEventListener("close", (c) => {
133
+ t.value = "closed", a.current = null, h?.(c);
134
+ }), f.addEventListener("error", (c) => {
135
+ o.value = c, y?.(c);
136
136
  }), () => {
137
137
  a.current = null;
138
138
  try {
@@ -149,7 +149,7 @@ function F(e, n = {}) {
149
149
  i.send(v);
150
150
  } catch {
151
151
  }
152
- else i && i.readyState === WebSocket.CONNECTING && c.current.push(v);
152
+ else i && i.readyState === WebSocket.CONNECTING && l.current.push(v);
153
153
  }, close: (p, v) => {
154
154
  const i = a.current;
155
155
  if (i)
package/dist/vite.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PluginOption } from "vite";
1
+ import { type PluginOption } from "vite";
2
2
  import type { VeloConfig } from "./config.js";
3
3
  import * as t from "@babel/types";
4
4
  export declare function injectMetadata(code: string, moduleId: string, fullPath?: string, path?: string): string;
package/dist/vite.js CHANGED
@@ -1,60 +1,61 @@
1
+ import { loadEnv as R } from "vite";
1
2
  import d from "node:path";
2
- import g from "node:fs";
3
- import R from "@preact/preset-vite";
4
- import $, { defaultOptions as J } from "@hono/vite-dev-server";
5
- import { parse as _ } from "@babel/parser";
6
- import j from "@babel/traverse";
7
- import L from "@babel/generator";
3
+ import S from "node:fs";
4
+ import $ from "@preact/preset-vite";
5
+ import H, { defaultOptions as J } from "@hono/vite-dev-server";
6
+ import { parse as v } from "@babel/parser";
7
+ import A from "@babel/traverse";
8
+ import N from "@babel/generator";
8
9
  import * as e from "@babel/types";
9
10
  import { buildGraph as U } from "./graph.js";
10
- const m = typeof j == "function" ? j : j.default, h = typeof L == "function" ? L : L.default;
11
- function W(u, o, n, i) {
12
- const l = _(u, {
11
+ const m = typeof A == "function" ? A : A.default, _ = typeof N == "function" ? N : N.default;
12
+ function W(p, s, r, i) {
13
+ const l = v(p, {
13
14
  sourceType: "module",
14
15
  plugins: ["typescript", "jsx"]
15
16
  });
16
17
  let t = !1;
17
18
  if (m(l, {
18
19
  // Procura: export const metadata = { ... }
19
- ExportNamedDeclaration(s) {
20
- const c = s.node.declaration;
20
+ ExportNamedDeclaration(o) {
21
+ const c = o.node.declaration;
21
22
  if (e.isVariableDeclaration(c) && c.declarations.length === 1) {
22
- const r = c.declarations[0];
23
- if (r && e.isIdentifier(r.id, { name: "metadata" }) && e.isObjectExpression(r.init)) {
23
+ const n = c.declarations[0];
24
+ if (n && e.isIdentifier(n.id, { name: "metadata" }) && e.isObjectExpression(n.init)) {
24
25
  t = !0;
25
- const f = r.init.properties.filter((v) => e.isObjectProperty(v) && e.isIdentifier(v.key) ? v.key.name !== "moduleId" && v.key.name !== "fullPath" && v.key.name !== "path" : !0), y = [
26
+ const f = n.init.properties.filter((y) => e.isObjectProperty(y) && e.isIdentifier(y.key) ? y.key.name !== "moduleId" && y.key.name !== "fullPath" && y.key.name !== "path" : !0), h = [
26
27
  e.objectProperty(
27
28
  e.identifier("moduleId"),
28
- e.stringLiteral(o)
29
+ e.stringLiteral(s)
29
30
  )
30
31
  ];
31
- n !== void 0 && y.push(
32
+ r !== void 0 && h.push(
32
33
  e.objectProperty(
33
34
  e.identifier("fullPath"),
34
- e.stringLiteral(n)
35
+ e.stringLiteral(r)
35
36
  )
36
- ), i !== void 0 && y.push(
37
+ ), i !== void 0 && h.push(
37
38
  e.objectProperty(
38
39
  e.identifier("path"),
39
40
  e.stringLiteral(i)
40
41
  )
41
- ), r.init.properties = [...y, ...f];
42
+ ), n.init.properties = [...h, ...f];
42
43
  }
43
44
  }
44
45
  }
45
46
  }), !t) {
46
- const s = [
47
+ const o = [
47
48
  e.objectProperty(
48
49
  e.identifier("moduleId"),
49
- e.stringLiteral(o)
50
+ e.stringLiteral(s)
50
51
  )
51
52
  ];
52
- n !== void 0 && s.push(
53
+ r !== void 0 && o.push(
53
54
  e.objectProperty(
54
55
  e.identifier("fullPath"),
55
- e.stringLiteral(n)
56
+ e.stringLiteral(r)
56
57
  )
57
- ), i !== void 0 && s.push(
58
+ ), i !== void 0 && o.push(
58
59
  e.objectProperty(
59
60
  e.identifier("path"),
60
61
  e.stringLiteral(i)
@@ -64,51 +65,51 @@ function W(u, o, n, i) {
64
65
  e.variableDeclaration("const", [
65
66
  e.variableDeclarator(
66
67
  e.identifier("metadata"),
67
- e.objectExpression(s)
68
+ e.objectExpression(o)
68
69
  )
69
70
  ])
70
71
  );
71
72
  l.program.body.unshift(c);
72
73
  }
73
- return h(l, { retainLines: !0 }).code;
74
+ return _(l, { retainLines: !0 }).code;
74
75
  }
75
- function M(u, o) {
76
- const n = _(u, {
76
+ function B(p, s) {
77
+ const r = v(p, {
77
78
  sourceType: "module",
78
79
  plugins: ["typescript", "jsx"]
79
80
  });
80
- return m(n, {
81
+ return m(r, {
81
82
  CallExpression(l) {
82
- const t = l.node.callee, a = e.isIdentifier(t, { name: "Loader" }) || e.isTSInstantiationExpression(t) && e.isIdentifier(t.expression, { name: "Loader" }), s = e.isIdentifier(t, { name: "useLoader" }) || e.isTSInstantiationExpression(t) && e.isIdentifier(t.expression, { name: "useLoader" });
83
- if (!a && !s) return;
84
- const c = l.node.arguments, r = e.stringLiteral(o), p = c[0];
85
- e.isStringLiteral(p) && p.value.includes("/") || (l.node.arguments = [r, ...c]);
83
+ const t = l.node.callee, a = e.isIdentifier(t, { name: "Loader" }) || e.isTSInstantiationExpression(t) && e.isIdentifier(t.expression, { name: "Loader" }), o = e.isIdentifier(t, { name: "useLoader" }) || e.isTSInstantiationExpression(t) && e.isIdentifier(t.expression, { name: "useLoader" });
84
+ if (!a && !o) return;
85
+ const c = l.node.arguments, n = e.stringLiteral(s), u = c[0];
86
+ e.isStringLiteral(u) && u.value.includes("/") || (l.node.arguments = [n, ...c]);
86
87
  }
87
- }), h(n, { retainLines: !0 }).code;
88
+ }), _(r, { retainLines: !0 }).code;
88
89
  }
89
- function B(u, o) {
90
- const n = _(u, {
90
+ function M(p, s) {
91
+ const r = v(p, {
91
92
  sourceType: "module",
92
93
  plugins: ["typescript", "jsx"]
93
94
  });
94
- return m(n, {
95
+ return m(r, {
95
96
  ExportNamedDeclaration(l) {
96
97
  const t = l.node.declaration;
97
98
  if (!e.isVariableDeclaration(t)) return;
98
99
  const a = t.declarations[0];
99
100
  if (!a || !e.isIdentifier(a.id)) return;
100
- const s = a.id.name;
101
- if (!s.startsWith("action_")) return;
102
- const c = s.replace("action_", ""), r = a.init;
103
- if (!e.isArrowFunctionExpression(r) || !r.async) return;
104
- const p = r.params, f = H(o, c, p);
105
- r.body = e.blockStatement([e.returnStatement(f)]), G(r, p);
101
+ const o = a.id.name;
102
+ if (!o.startsWith("action_")) return;
103
+ const c = o.replace("action_", ""), n = a.init;
104
+ if (!e.isArrowFunctionExpression(n) || !n.async) return;
105
+ const u = n.params, f = G(s, c, u);
106
+ n.body = e.blockStatement([e.returnStatement(f)]), z(n, u);
106
107
  }
107
- }), h(n, { retainLines: !0 }).code;
108
+ }), _(r, { retainLines: !0 }).code;
108
109
  }
109
- function H(u, o, n) {
110
- const i = `/_action/${u}/${o}`;
111
- return n.length === 0 ? e.callExpression(
110
+ function G(p, s, r) {
111
+ const i = `/_action/${p}/${s}`;
112
+ return r.length === 0 ? e.callExpression(
112
113
  e.memberExpression(
113
114
  e.callExpression(e.identifier("fetch"), [
114
115
  e.stringLiteral(i),
@@ -176,13 +177,13 @@ function H(u, o, n) {
176
177
  ]
177
178
  );
178
179
  }
179
- function G(u, o) {
180
- if (o.length === 0) return;
181
- const n = o[0];
182
- if (e.isObjectPattern(n)) {
180
+ function z(p, s) {
181
+ if (s.length === 0) return;
182
+ const r = s[0];
183
+ if (e.isObjectPattern(r)) {
183
184
  let i = null;
184
- if (e.isTSTypeAnnotation(n.typeAnnotation) && e.isTSTypeReference(n.typeAnnotation.typeAnnotation)) {
185
- const a = n.typeAnnotation.typeAnnotation;
185
+ if (e.isTSTypeAnnotation(r.typeAnnotation) && e.isTSTypeReference(r.typeAnnotation.typeAnnotation)) {
186
+ const a = r.typeAnnotation.typeAnnotation;
186
187
  e.isIdentifier(a.typeName, { name: "ActionArgs" }) && a.typeParameters?.params[0] && (i = a.typeParameters.params[0]);
187
188
  }
188
189
  const l = e.objectProperty(
@@ -199,23 +200,23 @@ function G(u, o) {
199
200
  e.tsTypeAnnotation(i)
200
201
  )
201
202
  ])
202
- )), u.params = [t];
203
+ )), p.params = [t];
203
204
  }
204
205
  }
205
- function z(u, o) {
206
- const n = _(u, {
206
+ function Y(p, s) {
207
+ const r = v(p, {
207
208
  sourceType: "module",
208
209
  plugins: ["typescript", "jsx"]
209
210
  });
210
- return m(n, {
211
+ return m(r, {
211
212
  ExportNamedDeclaration(l) {
212
213
  const t = l.node.declaration;
213
214
  if (!e.isVariableDeclaration(t)) return;
214
215
  const a = t.declarations[0];
215
216
  if (!a || !e.isIdentifier(a.id)) return;
216
- const s = a.id.name;
217
- if (!s.startsWith("stream_")) return;
218
- const c = s.replace("stream_", ""), r = `/_event/${o}/${c}`;
217
+ const o = a.id.name;
218
+ if (!o.startsWith("stream_")) return;
219
+ const c = o.replace("stream_", ""), n = `/_event/${s}/${c}`;
219
220
  a.init = e.objectExpression([
220
221
  e.objectProperty(
221
222
  e.identifier("__isVeloEventStream"),
@@ -223,26 +224,26 @@ function z(u, o) {
223
224
  ),
224
225
  e.objectProperty(
225
226
  e.identifier("__path"),
226
- e.stringLiteral(r)
227
+ e.stringLiteral(n)
227
228
  )
228
229
  ]), a.id.typeAnnotation = null;
229
230
  }
230
- }), h(n, { retainLines: !0 }).code;
231
+ }), _(r, { retainLines: !0 }).code;
231
232
  }
232
- function Y(u, o) {
233
- const n = _(u, {
233
+ function X(p, s) {
234
+ const r = v(p, {
234
235
  sourceType: "module",
235
236
  plugins: ["typescript", "jsx"]
236
237
  });
237
- return m(n, {
238
+ return m(r, {
238
239
  ExportNamedDeclaration(l) {
239
240
  const t = l.node.declaration;
240
241
  if (!e.isVariableDeclaration(t)) return;
241
242
  const a = t.declarations[0];
242
243
  if (!a || !e.isIdentifier(a.id)) return;
243
- const s = a.id.name;
244
- if (!s.startsWith("socket_")) return;
245
- const c = s.replace("socket_", ""), r = `/_socket/${o}/${c}`;
244
+ const o = a.id.name;
245
+ if (!o.startsWith("socket_")) return;
246
+ const c = o.replace("socket_", ""), n = `/_socket/${s}/${c}`;
246
247
  a.init = e.objectExpression([
247
248
  e.objectProperty(
248
249
  e.identifier("__isVeloSocket"),
@@ -250,300 +251,303 @@ function Y(u, o) {
250
251
  ),
251
252
  e.objectProperty(
252
253
  e.identifier("__path"),
253
- e.stringLiteral(r)
254
+ e.stringLiteral(n)
254
255
  )
255
256
  ]), a.id.typeAnnotation = null;
256
257
  }
257
- }), h(n, { retainLines: !0 }).code;
258
+ }), _(r, { retainLines: !0 }).code;
258
259
  }
259
- function X(u) {
260
- const o = _(u, {
260
+ function q(p) {
261
+ const s = v(p, {
261
262
  sourceType: "module",
262
263
  plugins: ["typescript", "jsx"]
263
264
  });
264
- return m(o, {
265
+ return m(s, {
265
266
  ExportNamedDeclaration(i) {
266
267
  const l = i.node.declaration;
267
268
  if (!e.isVariableDeclaration(l)) return;
268
269
  const t = l.declarations[0];
269
270
  !t || !e.isIdentifier(t.id) || t.id.name === "loader" && i.remove();
270
271
  }
271
- }), h(o, { retainLines: !0 }).code;
272
+ }), _(s, { retainLines: !0 }).code;
272
273
  }
273
- function q(u) {
274
- const o = _(u, {
274
+ function K(p) {
275
+ const s = v(p, {
275
276
  sourceType: "module",
276
277
  plugins: ["typescript", "jsx"]
277
- }), n = /* @__PURE__ */ new Set();
278
- x(o.program, n);
278
+ }), r = /* @__PURE__ */ new Set();
279
+ I(s.program, r);
279
280
  let i = !1;
280
- return m(o, {
281
+ return m(s, {
281
282
  ImportDeclaration(t) {
282
283
  const a = t.node.specifiers;
283
284
  if (a.length === 0) return;
284
- const s = a.filter((c) => n.has(c.local.name));
285
- s.length !== a.length && (i = !0, s.length === 0 ? t.remove() : t.node.specifiers = s);
285
+ const o = a.filter((c) => r.has(c.local.name));
286
+ o.length !== a.length && (i = !0, o.length === 0 ? t.remove() : t.node.specifiers = o);
286
287
  }
287
- }), i ? h(o, { retainLines: !0 }).code : u;
288
+ }), i ? _(s, { retainLines: !0 }).code : p;
288
289
  }
289
- function K(u) {
290
- const o = _(u, {
290
+ function Q(p) {
291
+ const s = v(p, {
291
292
  sourceType: "module",
292
293
  plugins: ["typescript", "jsx"]
293
- }), n = [];
294
- if (m(o, {
295
- ObjectProperty(s) {
296
- e.isIdentifier(s.node.key, { name: "middlewares" }) && (n.push(s.node.value), s.remove());
294
+ }), r = [];
295
+ if (m(s, {
296
+ ObjectProperty(o) {
297
+ e.isIdentifier(o.node.key, { name: "middlewares" }) && (r.push(o.node.value), o.remove());
297
298
  }
298
- }), n.length === 0) return u;
299
+ }), r.length === 0) return p;
299
300
  const i = /* @__PURE__ */ new Set();
300
- for (const s of n)
301
- x(s, i);
301
+ for (const o of r)
302
+ I(o, i);
302
303
  const l = /* @__PURE__ */ new Set();
303
- x(o.program, l);
304
+ I(s.program, l);
304
305
  const t = /* @__PURE__ */ new Set();
305
- for (const s of i)
306
- l.has(s) || t.add(s);
307
- return t.size > 0 && m(o, {
308
- ImportDeclaration(s) {
309
- const c = s.node.specifiers, r = c.filter((p) => !t.has(p.local.name));
310
- r.length === 0 ? s.remove() : r.length !== c.length && (s.node.specifiers = r);
306
+ for (const o of i)
307
+ l.has(o) || t.add(o);
308
+ return t.size > 0 && m(s, {
309
+ ImportDeclaration(o) {
310
+ const c = o.node.specifiers, n = c.filter((u) => !t.has(u.local.name));
311
+ n.length === 0 ? o.remove() : n.length !== c.length && (o.node.specifiers = n);
311
312
  }
312
- }), h(o, { retainLines: !0 }).code;
313
+ }), _(s, { retainLines: !0 }).code;
313
314
  }
314
- function x(u, o) {
315
- const n = (i) => {
315
+ function I(p, s) {
316
+ const r = (i) => {
316
317
  if (!(!i || typeof i != "object")) {
317
318
  if (Array.isArray(i)) {
318
- for (const l of i) n(l);
319
+ for (const l of i) r(l);
319
320
  return;
320
321
  }
321
322
  if (i.type === "Identifier") {
322
- o.add(i.name), i.typeAnnotation && n(i.typeAnnotation);
323
+ s.add(i.name), i.typeAnnotation && r(i.typeAnnotation);
323
324
  return;
324
325
  }
325
326
  if (i.type === "JSXIdentifier") {
326
- o.add(i.name);
327
+ s.add(i.name);
327
328
  return;
328
329
  }
329
330
  if (i.type === "MemberExpression") {
330
- n(i.object), i.computed && n(i.property);
331
+ r(i.object), i.computed && r(i.property);
331
332
  return;
332
333
  }
333
334
  if ((i.type === "ObjectProperty" || i.type === "ObjectMethod") && !i.computed) {
334
- i.type === "ObjectProperty" ? n(i.value) : n(i.body);
335
+ i.type === "ObjectProperty" ? r(i.value) : r(i.body);
335
336
  return;
336
337
  }
337
338
  if (!(i.type === "ImportSpecifier" || i.type === "ImportDefaultSpecifier" || i.type === "ImportNamespaceSpecifier"))
338
339
  for (const l of Object.keys(i))
339
- l === "loc" || l === "start" || l === "end" || l === "leadingComments" || l === "trailingComments" || l === "innerComments" || n(i[l]);
340
+ l === "loc" || l === "start" || l === "end" || l === "leadingComments" || l === "trailingComments" || l === "innerComments" || r(i[l]);
340
341
  }
341
342
  };
342
- n(u);
343
+ r(p);
343
344
  }
344
- function Q(u) {
345
- for (const o of u.properties)
346
- if (e.isObjectProperty(o) && e.isIdentifier(o.key) && o.key.name === "handler")
345
+ function Z(p) {
346
+ for (const s of p.properties)
347
+ if (e.isObjectProperty(s) && e.isIdentifier(s.key) && s.key.name === "handler")
347
348
  return !0;
348
349
  return !1;
349
350
  }
350
- function Z(u) {
351
- for (const o of u.properties)
352
- if (e.isObjectProperty(o) && e.isIdentifier(o.key) && o.key.name === "children" && e.isArrayExpression(o.value))
353
- return o.value;
351
+ function ee(p) {
352
+ for (const s of p.properties)
353
+ if (e.isObjectProperty(s) && e.isIdentifier(s.key) && s.key.name === "children" && e.isArrayExpression(s.value))
354
+ return s.value;
354
355
  return null;
355
356
  }
356
- function ee(u) {
357
- const o = _(u, {
357
+ function te(p) {
358
+ const s = v(p, {
358
359
  sourceType: "module",
359
360
  plugins: ["typescript", "jsx"]
360
- }), n = [], i = (c) => {
361
- for (let r = c.elements.length - 1; r >= 0; r--) {
362
- const p = c.elements[r];
363
- if (!e.isObjectExpression(p)) continue;
364
- if (Q(p)) {
365
- n.push(p), c.elements.splice(r, 1);
361
+ }), r = [], i = (c) => {
362
+ for (let n = c.elements.length - 1; n >= 0; n--) {
363
+ const u = c.elements[n];
364
+ if (!e.isObjectExpression(u)) continue;
365
+ if (Z(u)) {
366
+ r.push(u), c.elements.splice(n, 1);
366
367
  continue;
367
368
  }
368
- const f = Z(p);
369
+ const f = ee(u);
369
370
  f && i(f);
370
371
  }
371
372
  };
372
- if (m(o, {
373
+ if (m(s, {
373
374
  ExportDefaultDeclaration(c) {
374
- const r = c.node.declaration;
375
- let p = null;
376
- e.isArrayExpression(r) ? p = r : (e.isTSSatisfiesExpression(r) && e.isArrayExpression(r.expression) || e.isTSAsExpression(r) && e.isArrayExpression(r.expression)) && (p = r.expression), p && i(p);
375
+ const n = c.node.declaration;
376
+ let u = null;
377
+ e.isArrayExpression(n) ? u = n : (e.isTSSatisfiesExpression(n) && e.isArrayExpression(n.expression) || e.isTSAsExpression(n) && e.isArrayExpression(n.expression)) && (u = n.expression), u && i(u);
377
378
  }
378
- }), n.length === 0) return u;
379
+ }), r.length === 0) return p;
379
380
  const l = /* @__PURE__ */ new Set();
380
- for (const c of n)
381
- x(c, l);
381
+ for (const c of r)
382
+ I(c, l);
382
383
  const t = /* @__PURE__ */ new Set();
383
- x(o.program, t);
384
+ I(s.program, t);
384
385
  const a = /* @__PURE__ */ new Set();
385
386
  for (const c of l)
386
387
  t.has(c) || a.add(c);
387
- return a.size > 0 && m(o, {
388
+ return a.size > 0 && m(s, {
388
389
  ImportDeclaration(c) {
389
- const r = c.node.specifiers, p = r.filter((f) => !a.has(f.local.name));
390
- p.length === 0 ? c.remove() : p.length !== r.length && (c.node.specifiers = p);
390
+ const n = c.node.specifiers, u = n.filter((f) => !a.has(f.local.name));
391
+ u.length === 0 ? c.remove() : u.length !== n.length && (c.node.specifiers = u);
391
392
  }
392
- }), h(o, { retainLines: !0 }).code;
393
+ }), _(s, { retainLines: !0 }).code;
393
394
  }
394
- function A(u) {
395
- const o = _(u, {
395
+ function w(p) {
396
+ const s = v(p, {
396
397
  sourceType: "module",
397
398
  plugins: ["typescript", "jsx"]
398
- }), n = /* @__PURE__ */ new Map();
399
- m(o, {
399
+ }), r = /* @__PURE__ */ new Map();
400
+ m(s, {
400
401
  ImportDeclaration(t) {
401
402
  const a = t.node.source.value;
402
- for (const s of t.node.specifiers)
403
- e.isImportNamespaceSpecifier(s) && n.set(s.local.name, a);
403
+ for (const o of t.node.specifiers)
404
+ e.isImportNamespaceSpecifier(o) && r.set(o.local.name, a);
404
405
  }
405
406
  });
406
407
  const i = /* @__PURE__ */ new Map();
407
- m(o, {
408
+ m(s, {
408
409
  ExportDefaultDeclaration(t) {
409
410
  const a = t.node.declaration;
410
- let s = null;
411
- e.isArrayExpression(a) ? s = a : (e.isTSSatisfiesExpression(a) || e.isTSAsExpression(a)) && e.isArrayExpression(a.expression) && (s = a.expression), s && C(s.elements, "", i);
411
+ let o = null;
412
+ e.isArrayExpression(a) ? o = a : (e.isTSSatisfiesExpression(a) || e.isTSAsExpression(a)) && e.isArrayExpression(a.expression) && (o = a.expression), o && k(o.elements, "", i);
412
413
  }
413
414
  });
414
415
  const l = /* @__PURE__ */ new Map();
415
416
  for (const [t, a] of i) {
416
- const s = n.get(t);
417
- if (s) {
418
- const c = s.replace(/^\.\//, "").replace(/\.(tsx?|jsx?|js)$/, "");
417
+ const o = r.get(t);
418
+ if (o) {
419
+ const c = o.replace(/^\.\//, "").replace(/\.(tsx?|jsx?|js)$/, "");
419
420
  l.set(c, a);
420
421
  }
421
422
  }
422
423
  return l;
423
424
  }
424
- function C(u, o, n) {
425
- for (const i of u) {
425
+ function k(p, s, r) {
426
+ for (const i of p) {
426
427
  if (!e.isObjectExpression(i)) continue;
427
- let l = "", t = o, a = null, s = null;
428
+ let l = "", t = s, a = null, o = null;
428
429
  for (const c of i.properties) {
429
430
  if (!e.isObjectProperty(c)) continue;
430
- const r = c.key, p = e.isIdentifier(r) ? r.name : null;
431
- p === "path" && e.isStringLiteral(c.value) && (l = c.value.value, l && !l.startsWith("/") ? t = o + "/" + l : t = o + l), p === "module" && e.isIdentifier(c.value) && (a = c.value.name), p === "children" && e.isArrayExpression(c.value) && (s = c.value);
431
+ const n = c.key, u = e.isIdentifier(n) ? n.name : null;
432
+ u === "path" && e.isStringLiteral(c.value) && (l = c.value.value, l && !l.startsWith("/") ? t = s + "/" + l : t = s + l), u === "module" && e.isIdentifier(c.value) && (a = c.value.name), u === "children" && e.isArrayExpression(c.value) && (o = c.value);
432
433
  }
433
434
  if (a) {
434
- const r = !s && l === "/" ? o || "/" : t;
435
- n.set(a, {
436
- fullPath: r,
435
+ const n = !o && l === "/" ? s || "/" : t;
436
+ r.set(a, {
437
+ fullPath: n,
437
438
  path: l
438
439
  });
439
440
  }
440
- s && C(s.elements, t, n);
441
+ o && k(o.elements, t, r);
441
442
  }
442
443
  }
443
- const E = "virtual:velo/server-entry", b = "virtual:velo/client-entry", N = "\0" + E, P = "\0" + b;
444
- function te(u, o) {
445
- let n = d.resolve(process.cwd(), o);
446
- const i = u.routesFile ?? "routes.tsx", l = /* @__PURE__ */ new Map();
444
+ const T = "virtual:velo/server-entry", O = "virtual:velo/client-entry", C = "\0" + T, F = "\0" + O;
445
+ function re(p, s) {
446
+ let r = d.resolve(process.cwd(), s);
447
+ const i = p.routesFile ?? "routes.tsx", l = /* @__PURE__ */ new Map();
447
448
  return {
448
449
  name: "velo:transform",
449
450
  enforce: "pre",
450
451
  configResolved(t) {
451
- n = d.resolve(t.root, o);
452
+ r = d.resolve(t.root, s);
452
453
  },
453
454
  buildStart() {
454
- const t = d.join(n, i);
455
- if (g.existsSync(t)) {
456
- const a = g.readFileSync(t, "utf-8"), s = A(a);
455
+ const t = d.join(r, i);
456
+ if (S.existsSync(t)) {
457
+ const a = S.readFileSync(t, "utf-8"), o = w(a);
457
458
  l.clear();
458
- for (const [c, r] of s)
459
- l.set(c, r);
459
+ for (const [c, n] of o)
460
+ l.set(c, n);
460
461
  }
461
462
  },
462
463
  handleHotUpdate({ file: t, server: a }) {
463
- const s = d.join(n, i);
464
- if (t === s) {
465
- const c = g.readFileSync(s, "utf-8"), r = A(c);
464
+ const o = d.join(r, i);
465
+ if (t === o) {
466
+ const c = S.readFileSync(o, "utf-8"), n = w(c);
466
467
  l.clear();
467
- for (const [p, f] of r)
468
- l.set(p, f);
468
+ for (const [u, f] of n)
469
+ l.set(u, f);
469
470
  return a.ws.send({ type: "full-reload" }), [];
470
471
  }
471
472
  },
472
473
  resolveId(t) {
473
- return t === E || t.endsWith(E) ? N : t === b || t.endsWith(b) || t === "/__velo_client.js" ? P : null;
474
+ return t === T || t.endsWith(T) ? C : t === O || t.endsWith(O) || t === "/__velo_client.js" ? F : null;
474
475
  },
475
476
  load(t) {
476
- const a = u.routesFile ?? "routes.tsx", s = u.serverInit ?? "server.tsx", c = u.clientInit ?? "client.tsx", r = d.join(n, a).replace(/\.tsx?$/, ".js"), p = d.join(n, s).replace(/\.tsx?$/, ".js"), f = d.join(n, c).replace(/\.tsx?$/, ".js");
477
- return t === N ? `
477
+ const a = p.routesFile ?? "routes.tsx", o = p.serverInit ?? "server.tsx", c = p.clientInit ?? "client.tsx", n = d.join(r, a).replace(/\.tsx?$/, ".js"), u = d.join(r, o).replace(/\.tsx?$/, ".js"), f = d.join(r, c).replace(/\.tsx?$/, ".js");
478
+ return t === C ? `
478
479
  globalThis.__veloBuildHash = __VELO_BUILD_HASH__;
479
480
  globalThis.__veloClientJs = __VELO_CLIENT_JS__;
480
481
  globalThis.__veloClientCss = __VELO_CLIENT_CSS__;
481
- import "${p}";
482
- import routes from "${r}";
482
+ import "${u}";
483
+ import routes from "${n}";
483
484
  import { startServer } from "@mauroandre/velojs/server";
484
485
 
485
486
  export { routes };
486
487
  export default await startServer({ routes, port: __VELO_CONFIG_PORT__, hostname: __VELO_CONFIG_HOSTNAME__ });
487
- ` : t === P ? `
488
+ ` : t === F ? `
488
489
  import "${f}";
489
- import routes from "${r}";
490
+ import routes from "${n}";
490
491
  import { startClient } from "@mauroandre/velojs/client";
491
492
 
492
493
  startClient({ routes });
493
494
  ` : null;
494
495
  },
495
- transform(t, a, s) {
496
- const c = s?.ssr === !0;
497
- if (a.startsWith("\0") || a.includes("/.velojs/") || !a.endsWith(".tsx") && !a.endsWith(".ts") || !a.startsWith(n)) return null;
498
- let r = t, p = !1;
499
- const f = /middlewares:\s*\[/.test(t), y = /export\s+(const|function)\s+Component/.test(
496
+ transform(t, a, o) {
497
+ const c = o?.ssr === !0;
498
+ if (a.startsWith("\0") || a.includes("/.velojs/") || !a.endsWith(".tsx") && !a.endsWith(".ts") || !a.startsWith(r)) return null;
499
+ let n = t, u = !1;
500
+ const f = /middlewares:\s*\[/.test(t), h = /export\s+(const|function)\s+Component/.test(
500
501
  t
501
- ), v = /export\s+(const|function)\s+loader/.test(t), D = /export\s+const\s+action_\w+/.test(t), I = /export\s+const\s+stream_\w+/.test(t), T = /export\s+const\s+socket_\w+/.test(t), w = /\bLoader\s*</.test(t) || /\bLoader\s*\(/.test(t), F = /\buseLoader\s*</.test(t) || /\buseLoader\s*\(/.test(t), k = /\bhandler\s*:/.test(t), V = d.join(n, i);
502
- if (!c && f && (r = K(r), p = !0), !c && k && a === V && (r = ee(r), p = !0), y || v || D || I || T || w || F) {
503
- const S = d.relative(n, a).replace(/\.(tsx?|jsx?)$/, "").replace(/\\/g, "/"), O = l.get(S);
504
- r = W(r, S, O?.fullPath, O?.path), r = M(r, S), c || (r = B(
505
- r,
506
- S
507
- )), !c && I && (r = z(
508
- r,
509
- S
510
- )), !c && T && (r = Y(
511
- r,
512
- S
513
- )), c || (r = X(r)), c || (r = q(r)), p = !0;
502
+ ), y = /export\s+(const|function)\s+loader/.test(t), b = /export\s+const\s+action_\w+/.test(t), j = /export\s+const\s+stream_\w+/.test(t), x = /export\s+const\s+socket_\w+/.test(t), L = /\bLoader\s*</.test(t) || /\bLoader\s*\(/.test(t), P = /\buseLoader\s*</.test(t) || /\buseLoader\s*\(/.test(t), E = /\bhandler\s*:/.test(t), V = d.join(r, i);
503
+ if (!c && f && (n = Q(n), u = !0), !c && E && a === V && (n = te(n), u = !0), h || y || b || j || x || L || P) {
504
+ const g = d.relative(r, a).replace(/\.(tsx?|jsx?)$/, "").replace(/\\/g, "/"), D = l.get(g);
505
+ n = W(n, g, D?.fullPath, D?.path), n = B(n, g), c || (n = M(
506
+ n,
507
+ g
508
+ )), !c && j && (n = Y(
509
+ n,
510
+ g
511
+ )), !c && x && (n = X(
512
+ n,
513
+ g
514
+ )), c || (n = q(n)), c || (n = K(n)), u = !0;
514
515
  }
515
- return p ? {
516
- code: r,
516
+ return u ? {
517
+ code: n,
517
518
  map: null
518
519
  } : null;
519
520
  }
520
521
  };
521
522
  }
522
- function re(u) {
523
+ function ne(p) {
523
524
  return {
524
525
  name: "velo:config",
525
- config(o, { mode: n }) {
526
- const i = n === "server", l = n === "development", t = !!process.env.VELO_STATIC, a = o.server?.port ?? (Number(process.env.PORT) || u.port || 3e3);
527
- let s = "client.js", c = "client.css";
526
+ config(s, { mode: r }) {
527
+ const i = r === "server", l = r === "development", t = !!process.env.VELO_STATIC, a = s.server?.port ?? (Number(process.env.PORT) || p.port || 3e3), o = d.resolve(
528
+ process.cwd(),
529
+ s.root ?? "."
530
+ ), c = s.envDir ? d.resolve(o, s.envDir) : o, n = R(r, c, ""), u = process.env.HOST?.trim() || n.HOST?.trim() || void 0, f = s.server?.host ?? u ?? p.hostname, h = f === !0 || f === "0.0.0.0" || f === "::", y = s.server?.allowedHosts ?? (h ? !0 : void 0);
531
+ let b = "client.js", j = "client.css";
528
532
  if (i)
529
533
  try {
530
- const p = d.resolve(process.cwd(), "dist/client/.vite/manifest.json"), y = JSON.parse(g.readFileSync(p, "utf-8"))[b];
531
- y && (s = y.file || s, y.css?.[0] && (c = y.css[0]));
534
+ const L = d.resolve(process.cwd(), "dist/client/.vite/manifest.json"), E = JSON.parse(S.readFileSync(L, "utf-8"))[O];
535
+ E && (b = E.file || b, E.css?.[0] && (j = E.css[0]));
532
536
  } catch {
533
537
  }
534
- const r = {
538
+ const x = {
535
539
  define: {
536
540
  "process.env.NODE_ENV": JSON.stringify(l ? "development" : "production"),
537
541
  "process.env.STATIC_BASE_URL": JSON.stringify(process.env.STATIC_BASE_URL || (t ? "/client" : "")),
538
542
  __VELO_STATIC__: JSON.stringify(t),
539
543
  __VELO_BUILD_HASH__: JSON.stringify(process.env.VELO_BUILD_HASH || Date.now().toString(36)),
540
- __VELO_CLIENT_JS__: JSON.stringify(s),
541
- __VELO_CLIENT_CSS__: JSON.stringify(c),
544
+ __VELO_CLIENT_JS__: JSON.stringify(b),
545
+ __VELO_CLIENT_CSS__: JSON.stringify(j),
542
546
  // Baked into the server bundle as startServer's port fallback.
543
547
  // null when unset → startServer falls back to PORT env / 3000.
544
- __VELO_CONFIG_PORT__: JSON.stringify(u.port ?? null),
548
+ __VELO_CONFIG_PORT__: JSON.stringify(p.port ?? null),
545
549
  // Same for the bind interface: null → HOST env / Node default.
546
- __VELO_CONFIG_HOSTNAME__: JSON.stringify(u.hostname ?? null)
550
+ __VELO_CONFIG_HOSTNAME__: JSON.stringify(p.hostname ?? null)
547
551
  },
548
552
  resolve: {
549
553
  alias: {
@@ -552,11 +556,15 @@ function re(u) {
552
556
  }
553
557
  },
554
558
  server: {
555
- port: a
559
+ port: a,
560
+ // Host and allowedHosts only shape the dev server; in build
561
+ // they are inert, so we keep them out of the build config.
562
+ ...l && f !== void 0 ? { host: f } : {},
563
+ ...l && y !== void 0 ? { allowedHosts: y } : {}
556
564
  }
557
565
  };
558
- return i ? r.build = {
559
- ssr: E,
566
+ return i ? x.build = {
567
+ ssr: T,
560
568
  outDir: "dist",
561
569
  emptyOutDir: !1,
562
570
  copyPublicDir: !1,
@@ -565,107 +573,107 @@ function re(u) {
565
573
  entryFileNames: "server.js"
566
574
  }
567
575
  }
568
- } : l || (r.build = {
576
+ } : l || (x.build = {
569
577
  manifest: !0,
570
578
  outDir: "dist/client",
571
579
  rollupOptions: {
572
- input: b,
580
+ input: O,
573
581
  output: {
574
582
  entryFileNames: "client.[hash].js",
575
- assetFileNames: (p) => p.names?.[0]?.endsWith(".css") ? "client.[hash].css" : "[name].[hash][extname]"
583
+ assetFileNames: (L) => L.names?.[0]?.endsWith(".css") ? "client.[hash].css" : "[name].[hash][extname]"
576
584
  }
577
585
  }
578
- }), r;
586
+ }), x;
579
587
  }
580
588
  };
581
589
  }
582
- function ne() {
590
+ function ie() {
583
591
  return {
584
592
  name: "velo:static-url",
585
593
  apply: "build",
586
594
  enforce: "post",
587
- generateBundle(u, o) {
588
- const n = process.env.STATIC_BASE_URL || "";
589
- if (n)
590
- for (const i of Object.values(o))
595
+ generateBundle(p, s) {
596
+ const r = process.env.STATIC_BASE_URL || "";
597
+ if (r)
598
+ for (const i of Object.values(s))
591
599
  i.type === "asset" && typeof i.source == "string" && i.fileName.endsWith(".css") && (i.source = i.source.replace(
592
600
  /url\(\s*(['"]?)\/(?!\/)/g,
593
- `url($1${n}/`
601
+ `url($1${r}/`
594
602
  ));
595
603
  }
596
604
  };
597
605
  }
598
- function ie(u, o) {
599
- let n = "", i = "", l = !1, t = null;
606
+ function se(p, s) {
607
+ let r = "", i = "", l = !1, t = null;
600
608
  function a() {
601
- if (!n) return;
602
- const s = d.resolve(n, o);
603
- if (!g.existsSync(s)) return;
604
- const c = U(s), r = d.join(n, ".velojs");
605
- g.mkdirSync(r, { recursive: !0 }), g.writeFileSync(
606
- d.join(r, "graph.json"),
609
+ if (!r) return;
610
+ const o = d.resolve(r, s);
611
+ if (!S.existsSync(o)) return;
612
+ const c = U(o), n = d.join(r, ".velojs");
613
+ S.mkdirSync(n, { recursive: !0 }), S.writeFileSync(
614
+ d.join(n, "graph.json"),
607
615
  JSON.stringify(c)
608
616
  );
609
617
  }
610
618
  return {
611
619
  name: "velo:graph",
612
- configResolved(s) {
613
- n = s.root, i = s.mode;
620
+ configResolved(o) {
621
+ r = o.root, i = o.mode;
614
622
  },
615
623
  writeBundle() {
616
624
  i !== "server" && a();
617
625
  },
618
- configureServer(s) {
626
+ configureServer(o) {
619
627
  a(), l = !0;
620
- const c = d.resolve(n, o);
621
- s.watcher.on("all", (r, p) => {
622
- l && p.startsWith(c) && (t && clearTimeout(t), t = setTimeout(() => a(), 500));
628
+ const c = d.resolve(r, s);
629
+ o.watcher.on("all", (n, u) => {
630
+ l && u.startsWith(c) && (t && clearTimeout(t), t = setTimeout(() => a(), 500));
623
631
  });
624
632
  }
625
633
  };
626
634
  }
627
- function se(u) {
635
+ function oe(p) {
628
636
  return [
629
637
  ...J.exclude,
630
638
  /.*\.mjs$/,
631
- ...u.devServerExclude ?? []
639
+ ...p.devServerExclude ?? []
632
640
  ];
633
641
  }
634
- function ye(u) {
635
- const o = u ?? {}, n = o.appDirectory ?? "./app";
642
+ function _e(p) {
643
+ const s = p ?? {}, r = s.appDirectory ?? "./app";
636
644
  return [
637
- re(o),
638
- te(o, n),
639
- ne(),
640
- ie(o, n),
641
- R(),
642
- $({
643
- entry: E,
644
- exclude: se(o)
645
+ ne(s),
646
+ re(s, r),
647
+ ie(),
648
+ se(s, r),
649
+ $(),
650
+ H({
651
+ entry: T,
652
+ exclude: oe(s)
645
653
  }),
646
- oe()
654
+ ae()
647
655
  ];
648
656
  }
649
- function oe() {
657
+ function ae() {
650
658
  return {
651
659
  name: "velo:ws-bridge",
652
- configureServer(u) {
653
- globalThis.__veloDevServer = u.httpServer;
660
+ configureServer(p) {
661
+ globalThis.__veloDevServer = p.httpServer;
654
662
  }
655
663
  };
656
664
  }
657
665
  export {
658
- A as buildFullPathMap,
659
- C as collectFullPaths,
660
- se as devServerExcludeFor,
666
+ w as buildFullPathMap,
667
+ k as collectFullPaths,
668
+ oe as devServerExcludeFor,
661
669
  W as injectMetadata,
662
- q as pruneClientOnlyImports,
663
- ee as removeEndpointRoutes,
664
- X as removeLoaders,
665
- K as removeMiddlewares,
666
- B as transformActionsForClient,
667
- M as transformLoaderFunctions,
668
- Y as transformSocketsForClient,
669
- z as transformStreamsForClient,
670
- ye as veloPlugin
670
+ K as pruneClientOnlyImports,
671
+ te as removeEndpointRoutes,
672
+ q as removeLoaders,
673
+ Q as removeMiddlewares,
674
+ M as transformActionsForClient,
675
+ B as transformLoaderFunctions,
676
+ X as transformSocketsForClient,
677
+ Y as transformStreamsForClient,
678
+ _e as veloPlugin
671
679
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mauroandre/velojs",
3
- "version": "0.0.53",
3
+ "version": "0.0.55",
4
4
  "type": "module",
5
5
  "description": "Fullstack web framework built on Hono + Preact with Vite-powered AST transforms",
6
6
  "keywords": [