@mauroandre/velojs 0.0.50 → 0.0.52
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/agent/skills/velojs-boot-state/SKILL.md +82 -0
- package/agent/skills/velojs-production-deploy/SKILL.md +39 -0
- package/dist/components.d.ts +39 -0
- package/dist/components.js +44 -33
- package/dist/config.d.ts +6 -0
- package/dist/hooks.d.ts +24 -0
- package/dist/hooks.js +49 -45
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -8
- package/dist/server.d.ts +6 -0
- package/dist/server.js +137 -128
- package/dist/vite.js +57 -55
- package/package.json +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: velojs-boot-state
|
|
3
|
+
description: "Load localStorage/cookies BEFORE the first paint with `<Boot>` in client-root and read them with `useBoot()` — flicker-free themes, sidebars, and layout preferences. Use when UI state from the browser flashes on reload or when persisting layout preferences."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Boot state (pre-paint loading)
|
|
7
|
+
|
|
8
|
+
State stored in the browser (theme, collapsed sidebar, panel sizes) causes a flash on reload: the SSR HTML paints with defaults, then client JS corrects it — one visible frame of the wrong layout. The only code that runs between the `<head>` being parsed and the `<body>` being painted is a **synchronous inline script in the head**. `<Boot>` generates that script for you.
|
|
9
|
+
|
|
10
|
+
## Declaring what to load
|
|
11
|
+
|
|
12
|
+
In `client-root.tsx`, declare each storage separately with the exact key names your app uses:
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Boot, Scripts } from "@mauroandre/velojs";
|
|
16
|
+
|
|
17
|
+
export const Component = ({ children }) => (
|
|
18
|
+
<html>
|
|
19
|
+
<head>
|
|
20
|
+
<Boot localStorage={["theme", "sidebarHidden"]} />
|
|
21
|
+
<Boot cookies={["org"]} />
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
{children}
|
|
25
|
+
<Scripts />
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
28
|
+
);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The generated script runs before the first paint and does two things:
|
|
32
|
+
|
|
33
|
+
1. Populates `window.__VELO_BOOT__ = { theme: "dark", sidebarHidden: "true" }` — only keys that exist; absent keys stay out.
|
|
34
|
+
2. Mirrors each found key as a sanitized `data-*` attribute on `<html>`: `data-theme="dark"`, `data-sidebar-hidden`... Key sanitization: anything outside `[a-zA-Z0-9-]` becomes `-` — `"bc:terminal-colapsado"` → `data-bc-terminal-colapsado`.
|
|
35
|
+
|
|
36
|
+
## Reading
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { useBoot } from "@mauroandre/velojs/hooks";
|
|
40
|
+
|
|
41
|
+
const { theme, sidebarHidden } = useBoot();
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Values are **raw strings**, immutable by definition — the boot happened once. Seed your own signal with them for reactivity (`signal(boot.sidebarHidden === "true")`); parse numbers/JSON yourself.
|
|
45
|
+
|
|
46
|
+
On the server `useBoot()` returns `{}` — SSR always renders defaults, never touches `window`.
|
|
47
|
+
|
|
48
|
+
## The anti-flicker contract (the part that matters)
|
|
49
|
+
|
|
50
|
+
**Visual state must be CSS reacting to the mirrored data-attribute — never conditional JSX.**
|
|
51
|
+
|
|
52
|
+
```css
|
|
53
|
+
html[data-theme="dark"] { --color-surface: #11151f; }
|
|
54
|
+
html[data-sidebar-hidden="true"] .sidebar { display: none; }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- ✅ Always render the sidebar in the vdom; hide it with CSS on the attribute. The attribute is correct **before the first paint** → single correct paint, no flash, no hydration mismatch.
|
|
58
|
+
- ❌ `{!sidebarHidden && <Sidebar />}` — the client's first render diverges from the SSR DOM (hydration mismatch) and only corrects **after** paint: the flash is back.
|
|
59
|
+
|
|
60
|
+
A CSS `transition` on the mirrored property does not fire on load (the attribute is set before the first style computation) — transitions only run on interactive toggles, which is what you want.
|
|
61
|
+
|
|
62
|
+
## Writing is the app's business
|
|
63
|
+
|
|
64
|
+
`<Boot>` only loads. Persisting on change is a signal + storage write in your gestures:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
const sidebarHidden = signal(/* seeded from useBoot */);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
document.documentElement.dataset.sidebarHidden = String(sidebarHidden.value);
|
|
70
|
+
localStorage.setItem("sidebarHidden", String(sidebarHidden.value));
|
|
71
|
+
}, [sidebarHidden.value]);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Reference
|
|
75
|
+
|
|
76
|
+
| Piece | Behavior |
|
|
77
|
+
|-------|----------|
|
|
78
|
+
| `<Boot localStorage={[...]} />` | Inline pre-paint script; one `<Boot>` per storage |
|
|
79
|
+
| `<Boot cookies={[...]} />` | Same, from `document.cookie` |
|
|
80
|
+
| `useBoot()` | `Record<string, string>` — `{}` on the server, boot values on the client |
|
|
81
|
+
| Key `a:b` in storage | Attribute `data-a-b`; object key stays `"a:b"` |
|
|
82
|
+
| Storage throws (private mode) | Script is a no-op — app renders defaults |
|
|
@@ -71,6 +71,45 @@ export default defineConfig({
|
|
|
71
71
|
|
|
72
72
|
In dev, `velojs dev --port 5000` still overrides everything (it's passed straight to Vite).
|
|
73
73
|
|
|
74
|
+
## Choosing the bind interface (hostname)
|
|
75
|
+
|
|
76
|
+
By default `velojs start` binds to **all interfaces** (`::`) — the norm for Node servers and what containers and cloud platforms expect (they route traffic through the network interface; loopback-only would make the container unreachable). To change it, in order of precedence:
|
|
77
|
+
|
|
78
|
+
1. The `HOST` environment variable.
|
|
79
|
+
2. The `hostname` field in `defineConfig`.
|
|
80
|
+
3. The Node default (all interfaces).
|
|
81
|
+
|
|
82
|
+
### Secure bind for local/sensitive apps
|
|
83
|
+
|
|
84
|
+
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.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Deploy do app local: bind em loopback, sempre
|
|
88
|
+
HOST=127.0.0.1 velojs start
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Because "set HOST" is easy to forget, sensitive apps should also **fail fast if the bind is wrong** — check the real address after the server starts, via `onServer`:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// app/server.tsx
|
|
95
|
+
import { onServer } from "@mauroandre/velojs/server";
|
|
96
|
+
|
|
97
|
+
onServer((server) => {
|
|
98
|
+
const addr = server.address();
|
|
99
|
+
const loopback = ["127.0.0.1", "::1", "::ffff:127.0.0.1"];
|
|
100
|
+
if (!addr || typeof addr !== "object" || !loopback.includes(addr.address)) {
|
|
101
|
+
console.error(
|
|
102
|
+
`[${process.env.APP_NAME ?? "app"}][CRIT] ` +
|
|
103
|
+
`server bound on ${addr && typeof addr === "object" ? addr.address : "?"} — outside loopback. ` +
|
|
104
|
+
`This app runs commands on this machine: it may only listen on 127.0.0.1/::1. Aborting.`
|
|
105
|
+
);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This pattern caught a real exposure in production: the server had bound to `::` and the guard aborted instead of serving. When deploying the same app to a VPS later, the bind changes **consciously** to `HOST=0.0.0.0` — together with authentication and its own security spec, not by accident.
|
|
112
|
+
|
|
74
113
|
## Static assets on CDN
|
|
75
114
|
|
|
76
115
|
If you want to serve static assets (JS, CSS, images) from a CDN or S3 bucket instead of the Node.js server, set the `STATIC_BASE_URL` environment variable:
|
package/dist/components.d.ts
CHANGED
|
@@ -29,6 +29,45 @@ interface ScriptsProps {
|
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
export declare function Scripts({ basePath, favicon }?: ScriptsProps): import("preact").JSX.Element;
|
|
32
|
+
interface BootProps {
|
|
33
|
+
/**
|
|
34
|
+
* localStorage keys to load before the first paint. The value of each
|
|
35
|
+
* key found is exposed on `window.__VELO_BOOT__` and mirrored as a
|
|
36
|
+
* sanitized `data-*` attribute on `<html>` — CSS reacting to the
|
|
37
|
+
* attribute is what makes the first paint flicker-free.
|
|
38
|
+
*/
|
|
39
|
+
localStorage?: string[];
|
|
40
|
+
/**
|
|
41
|
+
* Cookie names to load before the first paint (same exposure/mirroring
|
|
42
|
+
* as localStorage keys). Declare storages separately: one Boot per
|
|
43
|
+
* storage keeps each script minimal.
|
|
44
|
+
*/
|
|
45
|
+
cookies?: string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Generates the pre-paint boot script. Pure and isomorphic — Boot must
|
|
49
|
+
* render the SAME string on server and client so hydration matches.
|
|
50
|
+
* Exported for tests.
|
|
51
|
+
*/
|
|
52
|
+
export declare function bootScript({ localStorage: lsKeys, cookies: ckKeys }?: BootProps): string;
|
|
53
|
+
/**
|
|
54
|
+
* Declares client state to load BEFORE the first paint. Place it in the
|
|
55
|
+
* `<head>` of client-root — a synchronous inline script is the only code
|
|
56
|
+
* that runs between the head being parsed and the body being painted, so
|
|
57
|
+
* values it mirrors as data-attributes produce a single, correct paint.
|
|
58
|
+
*
|
|
59
|
+
* Reading: `useBoot()` destructures by the declared names.
|
|
60
|
+
* Writing/persisting stays with the app (signals + storage in its gestures).
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```tsx
|
|
64
|
+
* <head>
|
|
65
|
+
* <Boot localStorage={["theme", "sidebarHidden"]} />
|
|
66
|
+
* <Boot cookies={["org"]} />
|
|
67
|
+
* </head>
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function Boot(props: BootProps): import("preact").JSX.Element | null;
|
|
32
71
|
import type { ComponentProps } from "preact";
|
|
33
72
|
import type { RouteModule } from "./types.js";
|
|
34
73
|
type WouterLinkProps = ComponentProps<typeof WouterLink>;
|
package/dist/components.js
CHANGED
|
@@ -1,58 +1,69 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useRouter as
|
|
1
|
+
import { jsx as r, jsxs as p, Fragment as g } from "preact/jsx-runtime";
|
|
2
|
+
import { useRouter as L, Link as $ } from "wouter-preact";
|
|
3
3
|
import "@preact/signals";
|
|
4
4
|
import "preact/hooks";
|
|
5
5
|
import { __veloUpdatePending as C } from "./loader-store.js";
|
|
6
|
-
function
|
|
6
|
+
function w({ basePath: t, favicon: e = "/favicon.ico" } = {}) {
|
|
7
7
|
const n = process.env.NODE_ENV !== "production";
|
|
8
|
-
|
|
9
|
-
const
|
|
8
|
+
t = t || process.env.STATIC_BASE_URL || (process.env.VELO_STATIC ? "/client" : "");
|
|
9
|
+
const s = e === !1 ? void 0 : e.endsWith(".svg") ? "image/svg+xml" : e.endsWith(".png") ? "image/png" : "image/x-icon", i = e !== !1 && /* @__PURE__ */ r("link", { rel: "icon", href: `${t}${e}`, type: s });
|
|
10
10
|
if (n)
|
|
11
|
-
return /* @__PURE__ */
|
|
11
|
+
return /* @__PURE__ */ p(g, { children: [
|
|
12
12
|
i,
|
|
13
|
-
/* @__PURE__ */
|
|
14
|
-
/* @__PURE__ */
|
|
13
|
+
/* @__PURE__ */ r("script", { type: "module", src: "/@vite/client" }),
|
|
14
|
+
/* @__PURE__ */ r("script", { type: "module", src: "/__velo_client.js" })
|
|
15
15
|
] });
|
|
16
|
-
const
|
|
17
|
-
return /* @__PURE__ */
|
|
16
|
+
const c = globalThis.__veloClientJs || (typeof __VELO_CLIENT_JS__ < "u" ? __VELO_CLIENT_JS__ : "client.js"), l = globalThis.__veloClientCss || (typeof __VELO_CLIENT_CSS__ < "u" ? __VELO_CLIENT_CSS__ : "client.css");
|
|
17
|
+
return /* @__PURE__ */ p(g, { children: [
|
|
18
18
|
i,
|
|
19
|
-
/* @__PURE__ */
|
|
20
|
-
/* @__PURE__ */
|
|
19
|
+
/* @__PURE__ */ r("link", { rel: "stylesheet", href: `${t}/${l}` }),
|
|
20
|
+
/* @__PURE__ */ r("script", { type: "module", src: `${t}/${c}` })
|
|
21
21
|
] });
|
|
22
22
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
for (
|
|
26
|
-
|
|
23
|
+
function S({ localStorage: t, cookies: e } = {}) {
|
|
24
|
+
if ((!t || t.length === 0) && (!e || e.length === 0)) return "";
|
|
25
|
+
const n = (c) => JSON.stringify(c).replace(/</g, "\\u003c"), s = t && t.length > 0 ? `var L=${n(t)};for(var i=0;i<L.length;i++){var v=localStorage.getItem(L[i]);if(v!==null)b[L[i]]=v;}` : "", i = e && e.length > 0 ? `var C=${n(e)};var s=document.cookie?document.cookie.split("; "):[];var m={};for(var j=0;j<s.length;j++){var p=s[j].indexOf("=");if(p>0){m[s[j].slice(0,p)]=decodeURIComponent(s[j].slice(p+1));}}for(var k=0;k<C.length;k++){if(C[k]in m)b[C[k]]=m[C[k]];}` : "";
|
|
26
|
+
return `(function(){var b={};try{${s}}catch(e){}try{${i}}catch(e){}if(Object.keys(b).length){window.__VELO_BOOT__=Object.assign(window.__VELO_BOOT__||{},b);var d=document.documentElement;for(var q in b){d.setAttribute("data-"+String(q).replace(/[^a-zA-Z0-9-]/g,"-"),b[q]);}}})();`;
|
|
27
|
+
}
|
|
28
|
+
function y(t) {
|
|
29
|
+
const e = S(t);
|
|
30
|
+
return e ? /* @__PURE__ */ r("script", { dangerouslySetInnerHTML: { __html: e } }) : null;
|
|
31
|
+
}
|
|
32
|
+
function m(t, e) {
|
|
33
|
+
let n = t;
|
|
34
|
+
for (const [s, i] of Object.entries(e))
|
|
35
|
+
n = n.replace(`:${s}`, i);
|
|
27
36
|
return n;
|
|
28
37
|
}
|
|
29
|
-
function I({ to:
|
|
30
|
-
const
|
|
38
|
+
function I({ to: t, params: e, search: n, absolute: s, ...i }) {
|
|
39
|
+
const c = typeof t != "string", l = L(), _ = c ? t.metadata?.fullPath ?? "/" : t, f = e ? m(_, e) : _, u = n ? `?${new URLSearchParams(n).toString()}` : "", v = `${f}${u}`;
|
|
31
40
|
if (typeof window < "u" && C.value) {
|
|
32
|
-
let
|
|
33
|
-
if (
|
|
34
|
-
const
|
|
35
|
-
|
|
41
|
+
let a;
|
|
42
|
+
if (c) {
|
|
43
|
+
const o = t.metadata?.fullPath ?? _;
|
|
44
|
+
a = `${e ? m(o, e) : o}${u}`;
|
|
36
45
|
} else {
|
|
37
|
-
const
|
|
38
|
-
|
|
46
|
+
const o = f.replace(/^~/, "");
|
|
47
|
+
a = `${!f.startsWith("~") && l.base ? l.base : ""}${o}${u}`;
|
|
39
48
|
}
|
|
40
|
-
const { onClick:
|
|
41
|
-
return /* @__PURE__ */
|
|
49
|
+
const { onClick: d, ...h } = i;
|
|
50
|
+
return /* @__PURE__ */ r(
|
|
42
51
|
"a",
|
|
43
52
|
{
|
|
44
|
-
href:
|
|
45
|
-
onClick: (
|
|
46
|
-
|
|
53
|
+
href: a,
|
|
54
|
+
onClick: (o) => {
|
|
55
|
+
d && d(o), o.defaultPrevented || (o.preventDefault(), window.location.href = a);
|
|
47
56
|
},
|
|
48
|
-
...
|
|
57
|
+
...h
|
|
49
58
|
}
|
|
50
59
|
);
|
|
51
60
|
}
|
|
52
|
-
return /* @__PURE__ */
|
|
61
|
+
return /* @__PURE__ */ r($, { to: v, ...i });
|
|
53
62
|
}
|
|
54
63
|
export {
|
|
64
|
+
y as Boot,
|
|
55
65
|
I as Link,
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
w as Scripts,
|
|
67
|
+
S as bootScript,
|
|
68
|
+
m as substituteParams
|
|
58
69
|
};
|
package/dist/config.d.ts
CHANGED
|
@@ -25,6 +25,12 @@ export interface VeloConfig {
|
|
|
25
25
|
* @default 3000
|
|
26
26
|
*/
|
|
27
27
|
port?: number;
|
|
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".
|
|
32
|
+
*/
|
|
33
|
+
hostname?: string;
|
|
28
34
|
/**
|
|
29
35
|
* Padrões adicionais de URL que o dev server deve delegar ao Vite em vez
|
|
30
36
|
* de interceptar como página (ex.: artefatos de geradores build-time como
|
package/dist/hooks.d.ts
CHANGED
|
@@ -3,6 +3,30 @@ import type { EventStream } from "./events.js";
|
|
|
3
3
|
import type { SocketHandler, SocketStub } from "./sockets.js";
|
|
4
4
|
import { __veloUpdatePending } from "./loader-store.js";
|
|
5
5
|
export { __veloUpdatePending };
|
|
6
|
+
declare global {
|
|
7
|
+
interface Window {
|
|
8
|
+
/** Populated by <Boot>'s pre-paint script — see components.tsx. */
|
|
9
|
+
__VELO_BOOT__?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Reads the boot state loaded before the first paint by `<Boot>`.
|
|
14
|
+
*
|
|
15
|
+
* Values are raw strings, immutable by definition (the boot happened once —
|
|
16
|
+
* state evolves through the app's own signals, seeded with these values).
|
|
17
|
+
* On the server it returns an empty object: there is no window to read.
|
|
18
|
+
*
|
|
19
|
+
* The anti-flicker contract: visual state derived from these values must be
|
|
20
|
+
* CSS reacting to the mirrored data-attributes on `<html>`, not conditional
|
|
21
|
+
* JSX — the attribute is correct before the first paint, a conditional
|
|
22
|
+
* render only corrects after hydration.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* const { theme, sidebarHidden } = useBoot();
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function useBoot(): Record<string, string>;
|
|
6
30
|
/**
|
|
7
31
|
* Força o signal a notificar mudanças após mutação de propriedades aninhadas
|
|
8
32
|
*
|
package/dist/hooks.js
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
|
-
import { signal as
|
|
2
|
-
import { useEffect as E, useRef as
|
|
3
|
-
import { useLocation as
|
|
4
|
-
import { refetchModule as
|
|
1
|
+
import { signal as w, useSignal as u } from "@preact/signals";
|
|
2
|
+
import { useEffect as E, useRef as S } from "preact/hooks";
|
|
3
|
+
import { useLocation as b, useParams as O, useSearch as $ } from "wouter-preact";
|
|
4
|
+
import { refetchModule as D, loaderLoading as m, loaderEntry as N } from "./loader-store.js";
|
|
5
5
|
import { __veloUpdatePending as z } from "./loader-store.js";
|
|
6
|
-
function U(
|
|
6
|
+
function U() {
|
|
7
|
+
return typeof window > "u" ? {} : window.__VELO_BOOT__ ?? {};
|
|
8
|
+
}
|
|
9
|
+
function W(e) {
|
|
7
10
|
const n = e.value;
|
|
8
11
|
n === null || typeof n != "object" || (e.value = Array.isArray(n) ? [...n] : { ...n });
|
|
9
12
|
}
|
|
10
|
-
function
|
|
13
|
+
function j(e) {
|
|
11
14
|
return typeof window > "u" ? {
|
|
12
15
|
data: {
|
|
13
16
|
get value() {
|
|
14
17
|
return e ? globalThis.__veloServerData?.getStore?.()?.[e] ?? null : null;
|
|
15
18
|
}
|
|
16
19
|
},
|
|
17
|
-
loading:
|
|
20
|
+
loading: w(!1),
|
|
18
21
|
refetch: () => {
|
|
19
22
|
}
|
|
20
23
|
} : e ? {
|
|
21
24
|
data: N(e),
|
|
22
25
|
loading: m(e),
|
|
23
|
-
refetch: () =>
|
|
24
|
-
} : { data:
|
|
26
|
+
refetch: () => D(e)
|
|
27
|
+
} : { data: w(null), loading: w(!1), refetch: () => {
|
|
25
28
|
} };
|
|
26
29
|
}
|
|
27
|
-
function
|
|
30
|
+
function B(e, n) {
|
|
28
31
|
let t, r;
|
|
29
32
|
if (Array.isArray(e) ? r = e : (t = e, r = n), typeof window > "u") {
|
|
30
33
|
let g = null;
|
|
@@ -33,40 +36,40 @@ function j(e, n) {
|
|
|
33
36
|
h?.[t] && (g = h[t]);
|
|
34
37
|
}
|
|
35
38
|
return {
|
|
36
|
-
data:
|
|
37
|
-
loading:
|
|
39
|
+
data: u(g),
|
|
40
|
+
loading: u(!1),
|
|
38
41
|
refetch: () => {
|
|
39
42
|
}
|
|
40
43
|
};
|
|
41
44
|
}
|
|
42
45
|
if (!t)
|
|
43
|
-
return { data:
|
|
46
|
+
return { data: u(null), loading: u(!1), refetch: () => {
|
|
44
47
|
} };
|
|
45
|
-
const o = N(t), a = m(t),
|
|
48
|
+
const o = N(t), a = m(t), c = () => D(t), d = S(!0);
|
|
46
49
|
return E(() => {
|
|
47
|
-
d.current && (d.current = !1, o.value !== null) ||
|
|
48
|
-
}, r ?? []), { data: o, loading: a, refetch:
|
|
50
|
+
d.current && (d.current = !1, o.value !== null) || c();
|
|
51
|
+
}, r ?? []), { data: o, loading: a, refetch: c };
|
|
49
52
|
}
|
|
50
53
|
function J() {
|
|
51
|
-
const [, e] =
|
|
54
|
+
const [, e] = b();
|
|
52
55
|
return e;
|
|
53
56
|
}
|
|
54
57
|
function M() {
|
|
55
|
-
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__params ?? {} :
|
|
58
|
+
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__params ?? {} : O();
|
|
56
59
|
}
|
|
57
60
|
function q() {
|
|
58
61
|
if (typeof window > "u")
|
|
59
62
|
return globalThis.__veloServerData?.getStore?.()?.__query ?? {};
|
|
60
|
-
const e =
|
|
63
|
+
const e = $(), n = {};
|
|
61
64
|
return new URLSearchParams(e).forEach((t, r) => {
|
|
62
65
|
n[r] = t;
|
|
63
66
|
}), n;
|
|
64
67
|
}
|
|
65
68
|
function x() {
|
|
66
|
-
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__pathname ?? "/" : (
|
|
69
|
+
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__pathname ?? "/" : (b(), window.location.pathname);
|
|
67
70
|
}
|
|
68
71
|
function G(e, n = {}) {
|
|
69
|
-
const t =
|
|
72
|
+
const t = u(null), r = u(null), o = u(!1), a = u(null), { channel: c, enabled: d = !0 } = n;
|
|
70
73
|
return E(() => {
|
|
71
74
|
if (typeof window > "u" || !d) return;
|
|
72
75
|
if (!e.__path) {
|
|
@@ -74,18 +77,18 @@ function G(e, n = {}) {
|
|
|
74
77
|
return;
|
|
75
78
|
}
|
|
76
79
|
t.value = null, r.value = null, o.value = !1, a.value = null;
|
|
77
|
-
const g =
|
|
80
|
+
const g = c ? `${e.__path}?channel=${encodeURIComponent(c)}` : e.__path, s = new EventSource(g);
|
|
78
81
|
return s.addEventListener("snapshot", (h) => {
|
|
79
82
|
try {
|
|
80
83
|
r.value = JSON.parse(h.data);
|
|
81
|
-
} catch (
|
|
82
|
-
a.value =
|
|
84
|
+
} catch (y) {
|
|
85
|
+
a.value = y;
|
|
83
86
|
}
|
|
84
87
|
}), s.addEventListener("message", (h) => {
|
|
85
88
|
try {
|
|
86
89
|
t.value = JSON.parse(h.data);
|
|
87
|
-
} catch (
|
|
88
|
-
a.value =
|
|
90
|
+
} catch (y) {
|
|
91
|
+
a.value = y;
|
|
89
92
|
}
|
|
90
93
|
}), s.addEventListener("heartbeat", () => {
|
|
91
94
|
}), s.addEventListener("close", () => {
|
|
@@ -95,12 +98,12 @@ function G(e, n = {}) {
|
|
|
95
98
|
}), () => {
|
|
96
99
|
s.close();
|
|
97
100
|
};
|
|
98
|
-
}, [e,
|
|
101
|
+
}, [e, c, d]), { data: t, snapshot: r, closed: o, error: a };
|
|
99
102
|
}
|
|
100
|
-
function
|
|
101
|
-
const t =
|
|
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;
|
|
102
105
|
return E(() => {
|
|
103
|
-
if (typeof window > "u" || !
|
|
106
|
+
if (typeof window > "u" || !L) return;
|
|
104
107
|
if (!e.__path) {
|
|
105
108
|
console.warn("[velojs] useSocket: stub has no __path");
|
|
106
109
|
return;
|
|
@@ -109,27 +112,27 @@ function B(e, n = {}) {
|
|
|
109
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);
|
|
110
113
|
return f.binaryType = "arraybuffer", a.current = f, f.addEventListener("open", () => {
|
|
111
114
|
t.value = "open";
|
|
112
|
-
const l =
|
|
113
|
-
|
|
114
|
-
for (const
|
|
115
|
+
const l = c.current;
|
|
116
|
+
c.current = [];
|
|
117
|
+
for (const _ of l)
|
|
115
118
|
try {
|
|
116
|
-
f.send(
|
|
119
|
+
f.send(_);
|
|
117
120
|
} catch {
|
|
118
121
|
}
|
|
119
122
|
s?.();
|
|
120
123
|
}), f.addEventListener("message", (l) => {
|
|
121
|
-
let
|
|
124
|
+
let _;
|
|
122
125
|
if (typeof l.data == "string")
|
|
123
|
-
|
|
126
|
+
_ = l.data;
|
|
124
127
|
else if (l.data instanceof ArrayBuffer)
|
|
125
|
-
|
|
128
|
+
_ = new Uint8Array(l.data);
|
|
126
129
|
else
|
|
127
130
|
return;
|
|
128
|
-
r.value =
|
|
131
|
+
r.value = _, g?.(_);
|
|
129
132
|
}), f.addEventListener("close", (l) => {
|
|
130
133
|
t.value = "closed", a.current = null, h?.(l);
|
|
131
134
|
}), f.addEventListener("error", (l) => {
|
|
132
|
-
o.value = l,
|
|
135
|
+
o.value = l, y?.(l);
|
|
133
136
|
}), () => {
|
|
134
137
|
a.current = null;
|
|
135
138
|
try {
|
|
@@ -137,7 +140,7 @@ function B(e, n = {}) {
|
|
|
137
140
|
} catch {
|
|
138
141
|
}
|
|
139
142
|
};
|
|
140
|
-
}, [e.__path, d,
|
|
143
|
+
}, [e.__path, d, L]), { send: (p) => {
|
|
141
144
|
let v;
|
|
142
145
|
typeof p == "string" || p instanceof Uint8Array ? v = p : v = JSON.stringify(p);
|
|
143
146
|
const i = a.current;
|
|
@@ -146,7 +149,7 @@ function B(e, n = {}) {
|
|
|
146
149
|
i.send(v);
|
|
147
150
|
} catch {
|
|
148
151
|
}
|
|
149
|
-
else i && i.readyState === WebSocket.CONNECTING &&
|
|
152
|
+
else i && i.readyState === WebSocket.CONNECTING && c.current.push(v);
|
|
150
153
|
}, close: (p, v) => {
|
|
151
154
|
const i = a.current;
|
|
152
155
|
if (i)
|
|
@@ -157,14 +160,15 @@ function B(e, n = {}) {
|
|
|
157
160
|
}, status: t, lastMessage: r, error: o };
|
|
158
161
|
}
|
|
159
162
|
export {
|
|
160
|
-
|
|
163
|
+
j as Loader,
|
|
161
164
|
z as __veloUpdatePending,
|
|
162
|
-
|
|
165
|
+
W as touch,
|
|
166
|
+
U as useBoot,
|
|
163
167
|
G as useEventStream,
|
|
164
|
-
|
|
168
|
+
B as useLoader,
|
|
165
169
|
J as useNavigate,
|
|
166
170
|
M as useParams,
|
|
167
171
|
x as usePathname,
|
|
168
172
|
q as useQuery,
|
|
169
|
-
|
|
173
|
+
F as useSocket
|
|
170
174
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { AppRoutes, RouteNode, RouteModule, LoaderArgs, ActionArgs, Metadata, HTTPMethod, EndpointHandler, EndpointHandlerArgs, } from "./types.js";
|
|
2
2
|
export type { VeloConfig } from "./config.js";
|
|
3
3
|
export { defineConfig } from "./config.js";
|
|
4
|
-
export { Scripts, Link } from "./components.js";
|
|
4
|
+
export { Scripts, Link, Boot } from "./components.js";
|
|
5
5
|
export { createEventStream, poll } from "./events.js";
|
|
6
6
|
export type { EventStream, EventStreamConfig, EmitFn, EmitOptions, SourceFn, PerChannelSourceFn, StreamPipeFn, StreamPipeArgs, ChannelResolver, } from "./events.js";
|
|
7
7
|
export type { SocketHandler, SocketHandlerArgs, SocketStub, } from "./sockets.js";
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { defineConfig as
|
|
2
|
-
import {
|
|
3
|
-
import { createEventStream as
|
|
1
|
+
import { defineConfig as r } from "./config.js";
|
|
2
|
+
import { Boot as f, Link as p, Scripts as i } from "./components.js";
|
|
3
|
+
import { createEventStream as n, poll as x } from "./events.js";
|
|
4
4
|
export {
|
|
5
|
-
f as
|
|
6
|
-
p as
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
f as Boot,
|
|
6
|
+
p as Link,
|
|
7
|
+
i as Scripts,
|
|
8
|
+
n as createEventStream,
|
|
9
|
+
r as defineConfig,
|
|
10
|
+
x as poll
|
|
10
11
|
};
|
package/dist/server.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export declare function onServer(fn: ServerCallback): void;
|
|
|
7
7
|
export interface StartServerOptions {
|
|
8
8
|
routes: AppRoutes;
|
|
9
9
|
port?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Hostname/interface to bind. Precedence: HOST env > this option > Node
|
|
12
|
+
* default (all interfaces — the ecosystem norm for containers/proxies).
|
|
13
|
+
* Local/sensitive apps should bind "127.0.0.1".
|
|
14
|
+
*/
|
|
15
|
+
hostname?: string;
|
|
10
16
|
}
|
|
11
17
|
export declare function addRoutes(fn: (app: Hono) => void | Promise<void>): void;
|
|
12
18
|
/**
|