@mauroandre/velojs 0.0.18 → 0.0.19
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 +153 -3
- package/dist/events.d.ts +91 -0
- package/dist/events.js +62 -0
- package/dist/hooks.d.ts +31 -0
- package/dist/hooks.js +73 -42
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -5
- package/dist/server.js +124 -94
- package/dist/vite.d.ts +8 -0
- package/dist/vite.js +200 -169
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -501,6 +501,136 @@ export const action_login = async ({ body }: { body: LoginBody }) => {
|
|
|
501
501
|
|
|
502
502
|
**Error handling**: Actions do NOT throw on server errors. They resolve with `{ error: "message" }`. Always check `result.error` explicitly.
|
|
503
503
|
|
|
504
|
+
### Shared actions in Layouts
|
|
505
|
+
|
|
506
|
+
Actions are tied to the module where they're declared — the route is always `/_action/{moduleId}/{actionName}`. Declare an action in a Layout and import it from any child module to share it across multiple pages.
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
// app/admin/Layout.tsx — action declared once in the layout
|
|
510
|
+
import type { ActionArgs } from "@mauroandre/velojs";
|
|
511
|
+
|
|
512
|
+
export const action_logout = async ({ c }: ActionArgs) => {
|
|
513
|
+
const { deleteCookie } = await import("@mauroandre/velojs/cookie");
|
|
514
|
+
deleteCookie(c!, "session");
|
|
515
|
+
return { ok: true };
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
export const Component = ({ children }) => (/* layout with children */);
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
// app/admin/Dashboard.tsx — imports and uses the action from Layout
|
|
523
|
+
import { action_logout } from "./Layout.js";
|
|
524
|
+
|
|
525
|
+
export const Component = () => (
|
|
526
|
+
<button onClick={async () => {
|
|
527
|
+
await action_logout({});
|
|
528
|
+
window.location.href = "/login";
|
|
529
|
+
}}>Logout</button>
|
|
530
|
+
);
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
The client-side transform rewrites the import to fetch `/_action/admin/Layout/logout`, so the action always points to the correct URL regardless of where it's imported. Middlewares on the Layout apply automatically.
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Event Streams
|
|
538
|
+
|
|
539
|
+
Push real-time updates from server to client via Server-Sent Events (SSE). Useful for live progress, notifications, metrics, log streaming, AI tokens, and any server → client push scenario.
|
|
540
|
+
|
|
541
|
+
### `stream_*` convention
|
|
542
|
+
|
|
543
|
+
Declare a stream alongside `loader` / `action_*` in any page or layout module. The framework registers an SSE route at `/_event/{moduleId}/{name}` automatically:
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
// app/admin/Deploy.tsx
|
|
547
|
+
import { createEventStream } from "@mauroandre/velojs";
|
|
548
|
+
import { useEventStream, useParams } from "@mauroandre/velojs/hooks";
|
|
549
|
+
|
|
550
|
+
type DeployState = { step: string; status: "running" | "success" | "error" };
|
|
551
|
+
|
|
552
|
+
const activeDeploys = new Map<string, DeployState>();
|
|
553
|
+
|
|
554
|
+
export const stream_progress = createEventStream<DeployState>({
|
|
555
|
+
channel: (c) => c.req.query("channel") ?? "",
|
|
556
|
+
snapshot: (channel) => activeDeploys.get(channel ?? ""),
|
|
557
|
+
closeOn: (s) => s.status === "success" || s.status === "error",
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
export const Component = () => {
|
|
561
|
+
const params = useParams<{ id: string }>();
|
|
562
|
+
const { data, snapshot, closed } = useEventStream(stream_progress, {
|
|
563
|
+
channel: params.id,
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
const state = data.value ?? snapshot.value;
|
|
567
|
+
return <div>{state?.step ?? "waiting..."}</div>;
|
|
568
|
+
};
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Emit from any service that imports the stream:
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
import { stream_progress } from "./Deploy.js";
|
|
575
|
+
|
|
576
|
+
stream_progress.emit("app-123", { step: "building", status: "running" });
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### Standalone streams
|
|
580
|
+
|
|
581
|
+
For cross-cutting streams (global metrics, notifications) consumed by many pages, use an explicit `path`:
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// app/streams/metrics.ts
|
|
585
|
+
export const metricsStream = createEventStream<Metric[]>({
|
|
586
|
+
path: "/api/metrics/containers",
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
setInterval(() => metricsStream.emit(collectMetrics()), 2000);
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Configuration
|
|
593
|
+
|
|
594
|
+
| Option | Type | Description |
|
|
595
|
+
|--------|------|-------------|
|
|
596
|
+
| `path` | `string` | (Standalone) Explicit URL path |
|
|
597
|
+
| `channel` | `(c: Context) => string` | Extract channel ID from request — subscribers only get matching events |
|
|
598
|
+
| `snapshot` | `(channel) => TSnapshot \| undefined` | Returns current state on connect (and reconnect) |
|
|
599
|
+
| `closeOn` | `(event: TEvent) => boolean` | Closes the SSE connection when matching event is sent |
|
|
600
|
+
| `heartbeatMs` | `number \| false` | Keep-alive heartbeat interval, default `20000` (20s). `false` to disable |
|
|
601
|
+
| `middlewares` | `MiddlewareHandler[]` | (Standalone) Hono middlewares applied to the SSE route |
|
|
602
|
+
|
|
603
|
+
### Middleware inheritance
|
|
604
|
+
|
|
605
|
+
Streams declared with `stream_*` automatically inherit middlewares from parent route nodes — same as pages and actions. A stream under an `AdminLayout` with `authMiddleware` is protected without extra wiring.
|
|
606
|
+
|
|
607
|
+
For standalone streams, pass middlewares explicitly:
|
|
608
|
+
|
|
609
|
+
```typescript
|
|
610
|
+
export const metricsStream = createEventStream<Metric[]>({
|
|
611
|
+
path: "/api/metrics",
|
|
612
|
+
middlewares: [authMiddleware],
|
|
613
|
+
});
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### `useEventStream` hook
|
|
617
|
+
|
|
618
|
+
```typescript
|
|
619
|
+
const { data, snapshot, closed, error } = useEventStream(stream, {
|
|
620
|
+
channel: "deploy-123", // optional
|
|
621
|
+
enabled: true, // optional
|
|
622
|
+
});
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
| Signal | Description |
|
|
626
|
+
|--------|-------------|
|
|
627
|
+
| `data` | Latest event received from the server |
|
|
628
|
+
| `snapshot` | Initial state on connect (if `snapshot` is configured) |
|
|
629
|
+
| `closed` | `true` when server closed the stream (`closeOn` matched) |
|
|
630
|
+
| `error` | Parse or connection error, if any |
|
|
631
|
+
|
|
632
|
+
The hook handles `EventSource` lifecycle automatically: opens on mount, closes on unmount, re-opens when `channel` changes.
|
|
633
|
+
|
|
504
634
|
---
|
|
505
635
|
|
|
506
636
|
## Hooks
|
|
@@ -511,6 +641,7 @@ All hooks work in both SSR and client (via AsyncLocalStorage on server, wouter/D
|
|
|
511
641
|
|------|-------------|
|
|
512
642
|
| `useLoader<T>(deps?)` | Loader data with SSR + SPA support. Returns `{ data, loading, refetch }` |
|
|
513
643
|
| `Loader<T>()` | Module-level SSR-only loader. Returns `{ data, loading }` |
|
|
644
|
+
| `useEventStream<T, S>(stream, opts?)` | Subscribe to a server-sent event stream. Returns `{ data, snapshot, closed, error }` |
|
|
514
645
|
| `useParams<T>()` | Route parameters (e.g., `:id`) |
|
|
515
646
|
| `useQuery<T>()` | Query string parameters |
|
|
516
647
|
| `useNavigate()` | Programmatic navigation. Returns `navigate(path)` function |
|
|
@@ -701,7 +832,25 @@ export default [
|
|
|
701
832
|
|
|
702
833
|
### Inheritance
|
|
703
834
|
|
|
704
|
-
Middlewares accumulate from parent to child
|
|
835
|
+
Middlewares accumulate from parent to child and apply to **every nested route**, including:
|
|
836
|
+
|
|
837
|
+
- **Page routes** (GET) — when loading a page
|
|
838
|
+
- **Action routes** (POST `/_action/...`) — when calling a server action
|
|
839
|
+
- **Data fetches** (GET with `?_data=1`) — during SPA navigation
|
|
840
|
+
|
|
841
|
+
In the example above, `/master/workers` runs `authMiddleware` first, then `masterMiddleware`. The same applies when calling `action_*` functions from any page under `MasterLayout` — both middlewares run before the action executes.
|
|
842
|
+
|
|
843
|
+
```typescript
|
|
844
|
+
// This action, defined in app/master/Workers.tsx, is registered as
|
|
845
|
+
// POST /_action/master/Workers/delete
|
|
846
|
+
// with authMiddleware + masterMiddleware applied.
|
|
847
|
+
export const action_delete = async ({ body, c }: ActionArgs<{ id: string }>) => {
|
|
848
|
+
const user = c!.get("user"); // set by authMiddleware
|
|
849
|
+
// ...
|
|
850
|
+
};
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
A middleware on a Layout guards everything underneath it — pages, loaders, and actions — with no extra wiring.
|
|
705
854
|
|
|
706
855
|
### Accessing middleware data in loaders and actions
|
|
707
856
|
|
|
@@ -927,10 +1076,11 @@ Hooks (`useParams`, `useQuery`, `usePathname`, `Loader`, `useLoader`) access thi
|
|
|
927
1076
|
|
|
928
1077
|
| Import | Contents |
|
|
929
1078
|
|--------|----------|
|
|
930
|
-
| `@mauroandre/velojs` | Types (`AppRoutes`, `ActionArgs`, `LoaderArgs`, `Metadata`), `Scripts`, `Link`, `defineConfig` |
|
|
1079
|
+
| `@mauroandre/velojs` | Types (`AppRoutes`, `ActionArgs`, `LoaderArgs`, `Metadata`, `EventStream`, `EventStreamConfig`), `Scripts`, `Link`, `createEventStream`, `defineConfig` |
|
|
931
1080
|
| `@mauroandre/velojs/server` | `startServer`, `createApp`, `addRoutes`, `onServer`, `serverDataStorage` |
|
|
932
1081
|
| `@mauroandre/velojs/client` | `startClient` |
|
|
933
|
-
| `@mauroandre/velojs/hooks` | `Loader`, `useLoader`, `useParams`, `useQuery`, `useNavigate`, `usePathname`, `touch` |
|
|
1082
|
+
| `@mauroandre/velojs/hooks` | `Loader`, `useLoader`, `useEventStream`, `useParams`, `useQuery`, `useNavigate`, `usePathname`, `touch` |
|
|
1083
|
+
| `@mauroandre/velojs/events` | `createEventStream`, `EventStream`, `EventStreamConfig` (also re-exported from root) |
|
|
934
1084
|
| `@mauroandre/velojs/cookie` | `getCookie`, `setCookie`, `deleteCookie`, `getSignedCookie`, `setSignedCookie` |
|
|
935
1085
|
| `@mauroandre/velojs/factory` | `createMiddleware`, `createFactory` |
|
|
936
1086
|
| `@mauroandre/velojs/vite` | `veloPlugin` |
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VeloJS Event Streams
|
|
3
|
+
* Server-Sent Events (SSE) with snapshot, channels, and auto-close.
|
|
4
|
+
*
|
|
5
|
+
* Two ways to use:
|
|
6
|
+
*
|
|
7
|
+
* 1. Standalone (cross-cutting streams like global metrics):
|
|
8
|
+
* ```typescript
|
|
9
|
+
* export const metricsStream = createEventStream<Metric[]>({
|
|
10
|
+
* path: "/api/metrics",
|
|
11
|
+
* });
|
|
12
|
+
* metricsStream.emit(currentMetrics);
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* 2. Convention `stream_*` (per-page streams, route registered automatically):
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // app/admin/Deploy.tsx
|
|
18
|
+
* export const stream_progress = createEventStream<DeployState>({
|
|
19
|
+
* channel: (c) => c.req.param("id"),
|
|
20
|
+
* snapshot: (id) => activeDeploys.get(id),
|
|
21
|
+
* closeOn: (s) => s.status === "success" || s.status === "error",
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import type { Context, Hono, MiddlewareHandler } from "hono";
|
|
26
|
+
export interface EventStreamConfig<TEvent, TSnapshot = TEvent> {
|
|
27
|
+
/**
|
|
28
|
+
* Explicit URL path for the SSE endpoint (standalone usage).
|
|
29
|
+
* When omitted, the path is derived from the module ID at build time
|
|
30
|
+
* (convention `stream_*`).
|
|
31
|
+
*/
|
|
32
|
+
path?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Channel selector — extracts a channel ID from the request.
|
|
35
|
+
* Subscribers only receive events emitted to their channel.
|
|
36
|
+
* When omitted, the stream is a broadcast (all subscribers receive all events).
|
|
37
|
+
*/
|
|
38
|
+
channel?: (c: Context) => string;
|
|
39
|
+
/**
|
|
40
|
+
* Returns the current state of a channel for snapshot-on-connect.
|
|
41
|
+
* Sent before any new events to bring the client up to date.
|
|
42
|
+
* Must remain available even after the stream "closes" — clients reconnecting
|
|
43
|
+
* after a terminal event still need to see the final state.
|
|
44
|
+
*/
|
|
45
|
+
snapshot?: (channel: string | undefined) => TSnapshot | undefined | Promise<TSnapshot | undefined>;
|
|
46
|
+
/**
|
|
47
|
+
* Returns true when an event marks the end of the stream for that channel.
|
|
48
|
+
* The server closes the SSE connection after sending such an event.
|
|
49
|
+
*/
|
|
50
|
+
closeOn?: (event: TEvent) => boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Heartbeat interval in milliseconds. Sends `:heartbeat\n\n` to keep
|
|
53
|
+
* proxies (Cloudflare, Nginx, ALB) from closing idle connections.
|
|
54
|
+
* Default: 20000 (20s). Set to `false` to disable.
|
|
55
|
+
*/
|
|
56
|
+
heartbeatMs?: number | false;
|
|
57
|
+
/**
|
|
58
|
+
* Middlewares to apply to the SSE route (standalone streams only).
|
|
59
|
+
* For `stream_*` convention streams, middlewares are inherited from
|
|
60
|
+
* parent route nodes automatically.
|
|
61
|
+
*/
|
|
62
|
+
middlewares?: MiddlewareHandler[];
|
|
63
|
+
}
|
|
64
|
+
export interface EventStream<TEvent, TSnapshot = TEvent> {
|
|
65
|
+
/** Broadcast emit (no channel) */
|
|
66
|
+
emit(event: TEvent): void;
|
|
67
|
+
/** Channel-targeted emit */
|
|
68
|
+
emit(channel: string, event: TEvent): void;
|
|
69
|
+
/** Internal: marker for runtime detection */
|
|
70
|
+
readonly __isVeloEventStream: true;
|
|
71
|
+
/** Internal: configuration */
|
|
72
|
+
readonly __config: EventStreamConfig<TEvent, TSnapshot>;
|
|
73
|
+
/** Internal: registered listeners by channel ("" for broadcast) */
|
|
74
|
+
readonly __listeners: Map<string, Set<(event: TEvent) => void>>;
|
|
75
|
+
/** Internal: assigned URL path (set by standalone or by stream_* discovery) */
|
|
76
|
+
__path: string | undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns and clears the queue of pending standalone streams.
|
|
80
|
+
* Called by createApp to register their routes.
|
|
81
|
+
*/
|
|
82
|
+
export declare function flushPendingStreamRoutes(app: Hono): void;
|
|
83
|
+
/**
|
|
84
|
+
* Creates a typed event stream with optional channel, snapshot, and close-on-event.
|
|
85
|
+
*/
|
|
86
|
+
export declare function createEventStream<TEvent, TSnapshot = TEvent>(config?: EventStreamConfig<TEvent, TSnapshot>): EventStream<TEvent, TSnapshot>;
|
|
87
|
+
/**
|
|
88
|
+
* Registers a GET SSE route for the given stream at the given path.
|
|
89
|
+
* Used both by standalone streams and by stream_* discovery.
|
|
90
|
+
*/
|
|
91
|
+
export declare function registerStreamHandler<TEvent, TSnapshot>(app: Hono, path: string, stream: EventStream<TEvent, TSnapshot>, middlewares?: Array<(c: Context, next: () => Promise<void>) => any>): void;
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const A = [];
|
|
2
|
+
function w(t) {
|
|
3
|
+
for (const s of A) s(t);
|
|
4
|
+
A.length = 0;
|
|
5
|
+
}
|
|
6
|
+
function N(t = {}) {
|
|
7
|
+
const s = /* @__PURE__ */ new Map();
|
|
8
|
+
function e(a, f) {
|
|
9
|
+
let l, n;
|
|
10
|
+
arguments.length === 1 ? (l = "", n = a) : (l = a, n = f);
|
|
11
|
+
const c = s.get(l);
|
|
12
|
+
if (c)
|
|
13
|
+
for (const h of c) h(n);
|
|
14
|
+
}
|
|
15
|
+
const r = {
|
|
16
|
+
emit: e,
|
|
17
|
+
__isVeloEventStream: !0,
|
|
18
|
+
__config: t,
|
|
19
|
+
__listeners: s,
|
|
20
|
+
__path: t.path
|
|
21
|
+
};
|
|
22
|
+
return t.path && A.push((a) => {
|
|
23
|
+
p(a, t.path, r, t.middlewares ?? []);
|
|
24
|
+
}), r;
|
|
25
|
+
}
|
|
26
|
+
function p(t, s, e, r = []) {
|
|
27
|
+
const a = async (f) => {
|
|
28
|
+
const { streamSSE: l } = await import("hono/streaming"), n = e.__config.channel ? e.__config.channel(f) : "", c = e.__config.heartbeatMs, h = c === !1 ? 0 : c ?? 2e4;
|
|
29
|
+
return l(f, async (_) => {
|
|
30
|
+
if (e.__config.snapshot) {
|
|
31
|
+
const o = await e.__config.snapshot(
|
|
32
|
+
n === "" ? void 0 : n
|
|
33
|
+
);
|
|
34
|
+
o !== void 0 && await _.writeSSE({
|
|
35
|
+
event: "snapshot",
|
|
36
|
+
data: JSON.stringify(o)
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
let E;
|
|
40
|
+
const T = new Promise((o) => {
|
|
41
|
+
E = o;
|
|
42
|
+
});
|
|
43
|
+
let S = !1, u = null, i;
|
|
44
|
+
const d = () => {
|
|
45
|
+
S || (S = !0, u && clearInterval(u), i?.delete(g), i?.size === 0 && e.__listeners.delete(n), E());
|
|
46
|
+
}, g = (o) => {
|
|
47
|
+
S || (_.writeSSE({ data: JSON.stringify(o) }).catch(() => {
|
|
48
|
+
}), e.__config.closeOn?.(o) && (_.close(), d()));
|
|
49
|
+
};
|
|
50
|
+
i = e.__listeners.get(n), i || (i = /* @__PURE__ */ new Set(), e.__listeners.set(n, i)), i.add(g), u = h > 0 ? setInterval(() => {
|
|
51
|
+
S || _.writeSSE({ event: "heartbeat", data: "" }).catch(() => {
|
|
52
|
+
});
|
|
53
|
+
}, h) : null, _.onAbort(d), await T;
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
r.length > 0 ? t.on(["GET"], [s], ...r, a) : t.on(["GET"], [s], a);
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
N as createEventStream,
|
|
60
|
+
w as flushPendingStreamRoutes,
|
|
61
|
+
p as registerStreamHandler
|
|
62
|
+
};
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Signal } from "@preact/signals";
|
|
2
|
+
import type { EventStream } from "./events.js";
|
|
2
3
|
export declare const __veloUpdatePending: Signal<boolean>;
|
|
3
4
|
/**
|
|
4
5
|
* Força o signal a notificar mudanças após mutação de propriedades aninhadas
|
|
@@ -118,3 +119,33 @@ export declare function useQuery<T extends Record<string, string> = Record<strin
|
|
|
118
119
|
* ```
|
|
119
120
|
*/
|
|
120
121
|
export declare function usePathname(): string;
|
|
122
|
+
/**
|
|
123
|
+
* Subscribes to a server event stream (SSE) created with createEventStream.
|
|
124
|
+
*
|
|
125
|
+
* Returns:
|
|
126
|
+
* - `data`: Signal with the latest event received (null until first event)
|
|
127
|
+
* - `snapshot`: Signal with the initial snapshot if the server provided one
|
|
128
|
+
* - `closed`: Signal that becomes true when the stream is closed by the server
|
|
129
|
+
* - `error`: Signal with the connection error, if any
|
|
130
|
+
*
|
|
131
|
+
* Usage:
|
|
132
|
+
* ```tsx
|
|
133
|
+
* // Per-page stream (stream_* convention)
|
|
134
|
+
* const { data, snapshot } = useEventStream(stream_progress, { channel: deployId });
|
|
135
|
+
*
|
|
136
|
+
* // Standalone stream (no channel)
|
|
137
|
+
* const { data } = useEventStream(metricsStream);
|
|
138
|
+
* ```
|
|
139
|
+
*
|
|
140
|
+
* The connection closes automatically when the component unmounts or when
|
|
141
|
+
* the channel changes (re-opens with new channel).
|
|
142
|
+
*/
|
|
143
|
+
export declare function useEventStream<TEvent, TSnapshot = TEvent>(stream: EventStream<TEvent, TSnapshot>, options?: {
|
|
144
|
+
channel?: string;
|
|
145
|
+
enabled?: boolean;
|
|
146
|
+
}): {
|
|
147
|
+
data: Signal<TEvent | null>;
|
|
148
|
+
snapshot: Signal<TSnapshot | null>;
|
|
149
|
+
closed: Signal<boolean>;
|
|
150
|
+
error: Signal<Error | null>;
|
|
151
|
+
};
|
package/dist/hooks.js
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import { signal as
|
|
2
|
-
import {
|
|
3
|
-
import { useLocation as
|
|
4
|
-
const
|
|
5
|
-
function
|
|
1
|
+
import { signal as v, useSignal as c } from "@preact/signals";
|
|
2
|
+
import { useEffect as h, useRef as p } from "preact/hooks";
|
|
3
|
+
import { useLocation as w, useParams as g } from "wouter-preact";
|
|
4
|
+
const S = v(!1);
|
|
5
|
+
function L(e) {
|
|
6
6
|
e.value !== null && typeof e.value == "object" && (e.value = { ...e.value });
|
|
7
7
|
}
|
|
8
|
-
function
|
|
9
|
-
const
|
|
8
|
+
function m(e) {
|
|
9
|
+
const o = v(!1);
|
|
10
10
|
if (typeof window > "u")
|
|
11
11
|
return { data: {
|
|
12
12
|
get value() {
|
|
13
13
|
return e ? globalThis.__veloServerData?.getStore?.()?.[e] ?? null : null;
|
|
14
14
|
}
|
|
15
|
-
}, loading:
|
|
15
|
+
}, loading: o };
|
|
16
16
|
const t = window.__PAGE_DATA__, n = e && t?.[e] ? t[e] : null;
|
|
17
|
-
return { data:
|
|
17
|
+
return { data: v(n), loading: o };
|
|
18
18
|
}
|
|
19
|
-
function
|
|
19
|
+
function A(e, o) {
|
|
20
20
|
let t, n;
|
|
21
|
-
Array.isArray(e) ? n = e : (t = e, n =
|
|
21
|
+
Array.isArray(e) ? n = e : (t = e, n = o);
|
|
22
22
|
let r = null;
|
|
23
23
|
if (typeof window > "u" && t) {
|
|
24
|
-
const
|
|
25
|
-
|
|
24
|
+
const l = globalThis.__veloServerData?.getStore?.();
|
|
25
|
+
l?.[t] && (r = l[t]);
|
|
26
26
|
}
|
|
27
27
|
if (typeof window < "u" && t) {
|
|
28
|
-
const
|
|
29
|
-
|
|
28
|
+
const a = window.__PAGE_DATA__;
|
|
29
|
+
a?.[t] && (r = a[t], delete a[t]);
|
|
30
30
|
}
|
|
31
|
-
const u =
|
|
31
|
+
const u = c(r), s = c(!1), _ = () => {
|
|
32
32
|
if (typeof window > "u" || !t) return;
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
return
|
|
33
|
+
const a = window.location.pathname;
|
|
34
|
+
s.value = !0;
|
|
35
|
+
const f = typeof __VELO_STATIC__ < "u" && __VELO_STATIC__ ? `${a === "/" ? "" : a}/index.json` : (() => {
|
|
36
|
+
const i = new URLSearchParams(window.location.search);
|
|
37
|
+
return i.set("_data", "1"), `${a}?${i.toString()}`;
|
|
38
38
|
})();
|
|
39
|
-
fetch(
|
|
40
|
-
typeof __VELO_BUILD_HASH__ < "u" &&
|
|
39
|
+
fetch(f, { cache: "no-cache" }).then((i) => i.json()).then((i) => {
|
|
40
|
+
typeof __VELO_BUILD_HASH__ < "u" && i.__buildHash && i.__buildHash !== __VELO_BUILD_HASH__ && (S.value = !0), u.value = i[t], s.value = !1;
|
|
41
41
|
}).catch(() => {
|
|
42
|
-
|
|
42
|
+
s.value = !1;
|
|
43
43
|
});
|
|
44
|
-
},
|
|
44
|
+
}, d = p(r !== null);
|
|
45
45
|
return h(() => {
|
|
46
|
-
if (
|
|
47
|
-
|
|
46
|
+
if (d.current) {
|
|
47
|
+
d.current = !1;
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
}, n ?? []), { data: u, loading:
|
|
50
|
+
_();
|
|
51
|
+
}, n ?? []), { data: u, loading: s, refetch: _ };
|
|
52
52
|
}
|
|
53
|
-
function
|
|
54
|
-
const [, e] =
|
|
53
|
+
function P() {
|
|
54
|
+
const [, e] = w();
|
|
55
55
|
return e;
|
|
56
56
|
}
|
|
57
|
-
function
|
|
58
|
-
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__params ?? {} :
|
|
57
|
+
function T() {
|
|
58
|
+
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__params ?? {} : g();
|
|
59
59
|
}
|
|
60
60
|
function b() {
|
|
61
61
|
if (typeof window > "u")
|
|
@@ -63,21 +63,52 @@ function b() {
|
|
|
63
63
|
const e = window.__PAGE_DATA__;
|
|
64
64
|
if (e?.__query)
|
|
65
65
|
return e.__query;
|
|
66
|
-
const
|
|
67
|
-
return
|
|
66
|
+
const o = new URLSearchParams(window.location.search), t = {};
|
|
67
|
+
return o.forEach((n, r) => {
|
|
68
68
|
t[r] = n;
|
|
69
69
|
}), t;
|
|
70
70
|
}
|
|
71
71
|
function H() {
|
|
72
|
-
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__pathname ?? "/" : (
|
|
72
|
+
return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__pathname ?? "/" : (w(), window.location.pathname);
|
|
73
|
+
}
|
|
74
|
+
function U(e, o = {}) {
|
|
75
|
+
const t = c(null), n = c(null), r = c(!1), u = c(null), { channel: s, enabled: _ = !0 } = o;
|
|
76
|
+
return h(() => {
|
|
77
|
+
if (typeof window > "u" || !_) return;
|
|
78
|
+
if (!e.__path) {
|
|
79
|
+
console.warn("[velojs] useEventStream: stream has no __path");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
t.value = null, n.value = null, r.value = !1, u.value = null;
|
|
83
|
+
const d = s ? `${e.__path}?channel=${encodeURIComponent(s)}` : e.__path, a = new EventSource(d);
|
|
84
|
+
return a.addEventListener("snapshot", (l) => {
|
|
85
|
+
try {
|
|
86
|
+
n.value = JSON.parse(l.data);
|
|
87
|
+
} catch (f) {
|
|
88
|
+
u.value = f;
|
|
89
|
+
}
|
|
90
|
+
}), a.addEventListener("message", (l) => {
|
|
91
|
+
try {
|
|
92
|
+
t.value = JSON.parse(l.data);
|
|
93
|
+
} catch (f) {
|
|
94
|
+
u.value = f;
|
|
95
|
+
}
|
|
96
|
+
}), a.addEventListener("heartbeat", () => {
|
|
97
|
+
}), a.addEventListener("error", () => {
|
|
98
|
+
a.readyState === EventSource.CLOSED && (r.value = !0);
|
|
99
|
+
}), () => {
|
|
100
|
+
a.close();
|
|
101
|
+
};
|
|
102
|
+
}, [e, s, _]), { data: t, snapshot: n, closed: r, error: u };
|
|
73
103
|
}
|
|
74
104
|
export {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
105
|
+
m as Loader,
|
|
106
|
+
S as __veloUpdatePending,
|
|
107
|
+
L as touch,
|
|
108
|
+
U as useEventStream,
|
|
109
|
+
A as useLoader,
|
|
110
|
+
P as useNavigate,
|
|
111
|
+
T as useParams,
|
|
81
112
|
H as usePathname,
|
|
82
113
|
b as useQuery
|
|
83
114
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ export type { AppRoutes, RouteNode, RouteModule, LoaderArgs, ActionArgs, Metadat
|
|
|
2
2
|
export type { VeloConfig } from "./config.js";
|
|
3
3
|
export { defineConfig } from "./config.js";
|
|
4
4
|
export { Scripts, Link } from "./components.js";
|
|
5
|
+
export { createEventStream } from "./events.js";
|
|
6
|
+
export type { EventStream, EventStreamConfig } from "./events.js";
|
|
5
7
|
export type { Context, MiddlewareHandler, Next } from "hono";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { defineConfig as
|
|
2
|
-
import { Link as
|
|
1
|
+
import { defineConfig as o } from "./config.js";
|
|
2
|
+
import { Link as f, Scripts as i } from "./components.js";
|
|
3
|
+
import { createEventStream as n } from "./events.js";
|
|
3
4
|
export {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
f as Link,
|
|
6
|
+
i as Scripts,
|
|
7
|
+
n as createEventStream,
|
|
8
|
+
o as defineConfig
|
|
7
9
|
};
|