@mauroandre/velojs 0.0.59 → 0.0.61

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/dist/config.d.ts CHANGED
@@ -40,5 +40,13 @@ export interface VeloConfig {
40
40
  * @hono/vite-dev-server, não os substituem.
41
41
  */
42
42
  devServerExclude?: (string | RegExp)[];
43
+ /**
44
+ * Padrões de glob (`*`, `**`, `?`) que declaram módulos só de servidor ao
45
+ * diagnóstico de vazamento server->client, casados contra o caminho do
46
+ * arquivo do módulo relativo à raiz do projeto, com `/` como separador
47
+ * (ex.: `serverOnly: ["src/fsm/**", "server/**"]`). Serve só ao
48
+ * diagnóstico — nada é bloqueado ou alterado por causa da declaração.
49
+ */
50
+ serverOnly?: string[];
43
51
  }
44
52
  export declare function defineConfig(config: VeloConfig): VeloConfig;
package/dist/hooks.d.ts CHANGED
@@ -4,6 +4,8 @@ import type { SocketHandler, SocketStub } from "./sockets.js";
4
4
  import { __veloUpdatePending } from "./loader-store.js";
5
5
  import { type Freshness } from "./live-loader.js";
6
6
  export type { Freshness } from "./live-loader.js";
7
+ export { useLiveAction } from "./live-action.js";
8
+ export type { LiveActionStub, LiveActionHandle, } from "./live-action.js";
7
9
  /**
8
10
  * What `Loader()`/`useLoader()` return. `freshness` is the page-level state of
9
11
  * the live loader: `"live"` (connected), `"stale"` (connection closed,
package/dist/hooks.js CHANGED
@@ -4,14 +4,15 @@ import { useLocation as b, useParams as $, useSearch as B } from "wouter-preact"
4
4
  import { refetchModule as C, loaderLoading as D, loaderEntry as N } from "./loader-store.js";
5
5
  import { __veloUpdatePending as X } from "./loader-store.js";
6
6
  import { loaderFreshness as w, moduleChannelFreshness as O } from "./live-loader.js";
7
+ import { useLiveAction as Z } from "./live-action.js";
7
8
  function j() {
8
9
  return typeof window > "u" ? {} : window.__VELO_BOOT__ ?? {};
9
10
  }
10
- function F(e) {
11
+ function x(e) {
11
12
  const t = e.value;
12
13
  t === null || typeof t != "object" || (e.value = Array.isArray(t) ? [...t] : { ...t });
13
14
  }
14
- function J(e) {
15
+ function F(e) {
15
16
  return typeof window > "u" ? {
16
17
  data: {
17
18
  get value() {
@@ -38,7 +39,7 @@ function J(e) {
38
39
  freshnessByChannel: S({})
39
40
  };
40
41
  }
41
- function M(e, t) {
42
+ function J(e, t) {
42
43
  let n, r;
43
44
  if (Array.isArray(e) ? r = e : (n = e, r = t), typeof window > "u") {
44
45
  let a = null;
@@ -64,16 +65,16 @@ function M(e, t) {
64
65
  freshness: w(),
65
66
  freshnessByChannel: s({})
66
67
  };
67
- const l = N(n), o = D(n), u = () => C(n), h = w(), y = E(!0);
68
+ const c = N(n), o = D(n), u = () => C(n), h = w(), y = E(!0);
68
69
  return m(() => {
69
- y.current && (y.current = !1, l.value !== null) || u();
70
- }, r ?? []), { data: l, loading: o, refetch: u, freshness: h, freshnessByChannel: O(n) };
70
+ y.current && (y.current = !1, c.value !== null) || u();
71
+ }, r ?? []), { data: c, loading: o, refetch: u, freshness: h, freshnessByChannel: O(n) };
71
72
  }
72
- function q() {
73
+ function M() {
73
74
  const [, e] = b();
74
75
  return e;
75
76
  }
76
- function x() {
77
+ function q() {
77
78
  return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__params ?? {} : $();
78
79
  }
79
80
  function G() {
@@ -88,14 +89,14 @@ function Q() {
88
89
  return typeof window > "u" ? globalThis.__veloServerData?.getStore?.()?.__pathname ?? "/" : (b(), window.location.pathname);
89
90
  }
90
91
  function V(e, t = {}) {
91
- const n = s(null), r = s(null), l = s(!1), o = s(null), { channel: u, enabled: h = !0 } = t;
92
+ const n = s(null), r = s(null), c = s(!1), o = s(null), { channel: u, enabled: h = !0 } = t;
92
93
  return m(() => {
93
94
  if (typeof window > "u" || !h) return;
94
95
  if (!e.__path) {
95
96
  console.warn("[velojs] useEventStream: stream has no __path");
96
97
  return;
97
98
  }
98
- l.value = !1, o.value = null;
99
+ c.value = !1, o.value = null;
99
100
  const y = u ? `${e.__path}?channel=${encodeURIComponent(u)}` : e.__path, a = new EventSource(y);
100
101
  return a.addEventListener("snapshot", (g) => {
101
102
  try {
@@ -111,47 +112,47 @@ function V(e, t = {}) {
111
112
  }
112
113
  }), a.addEventListener("heartbeat", () => {
113
114
  }), a.addEventListener("close", () => {
114
- l.value = !0, a.close();
115
+ c.value = !0, a.close();
115
116
  }), a.addEventListener("error", () => {
116
- a.readyState === EventSource.CLOSED && (l.value = !0);
117
+ a.readyState === EventSource.CLOSED && (c.value = !0);
117
118
  }), () => {
118
119
  a.close();
119
120
  };
120
- }, [e, u, h]), { data: n, snapshot: r, closed: l, error: o };
121
+ }, [e, u, h]), { data: n, snapshot: r, closed: c, error: o };
121
122
  }
122
123
  function z(e, t = {}) {
123
- const n = s("connecting"), r = s(null), l = s(null), o = E(null), u = E([]), { channel: h, onMessage: y, onOpen: a, onClose: g, onError: v, enabled: L = !0 } = t;
124
+ const n = s("connecting"), r = s(null), c = s(null), o = E(null), u = E([]), { channel: h, onMessage: y, onOpen: a, onClose: g, onError: v, enabled: L = !0 } = t;
124
125
  return m(() => {
125
126
  if (typeof window > "u" || !L) return;
126
127
  if (!e.__path) {
127
128
  console.warn("[velojs] useSocket: stub has no __path");
128
129
  return;
129
130
  }
130
- n.value = "connecting", l.value = null;
131
+ n.value = "connecting", c.value = null;
131
132
  const d = `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}`, i = h ? `${d}${e.__path}?channel=${encodeURIComponent(h)}` : `${d}${e.__path}`, f = new WebSocket(i);
132
133
  return f.binaryType = "arraybuffer", o.current = f, f.addEventListener("open", () => {
133
134
  n.value = "open";
134
- const c = u.current;
135
+ const l = u.current;
135
136
  u.current = [];
136
- for (const _ of c)
137
+ for (const _ of l)
137
138
  try {
138
139
  f.send(_);
139
140
  } catch {
140
141
  }
141
142
  a?.();
142
- }), f.addEventListener("message", (c) => {
143
+ }), f.addEventListener("message", (l) => {
143
144
  let _;
144
- if (typeof c.data == "string")
145
- _ = c.data;
146
- else if (c.data instanceof ArrayBuffer)
147
- _ = new Uint8Array(c.data);
145
+ if (typeof l.data == "string")
146
+ _ = l.data;
147
+ else if (l.data instanceof ArrayBuffer)
148
+ _ = new Uint8Array(l.data);
148
149
  else
149
150
  return;
150
151
  r.value = _, y?.(_);
151
- }), f.addEventListener("close", (c) => {
152
- n.value = "closed", o.current = null, g?.(c);
153
- }), f.addEventListener("error", (c) => {
154
- l.value = c, v?.(c);
152
+ }), f.addEventListener("close", (l) => {
153
+ n.value = "closed", o.current = null, g?.(l);
154
+ }), f.addEventListener("error", (l) => {
155
+ c.value = l, v?.(l);
155
156
  }), () => {
156
157
  o.current = null;
157
158
  try {
@@ -176,17 +177,18 @@ function z(e, t = {}) {
176
177
  i.close(p, d);
177
178
  } catch {
178
179
  }
179
- }, status: n, lastMessage: r, error: l };
180
+ }, status: n, lastMessage: r, error: c };
180
181
  }
181
182
  export {
182
- J as Loader,
183
+ F as Loader,
183
184
  X as __veloUpdatePending,
184
- F as touch,
185
+ x as touch,
185
186
  j as useBoot,
186
187
  V as useEventStream,
187
- M as useLoader,
188
- q as useNavigate,
189
- x as useParams,
188
+ Z as useLiveAction,
189
+ J as useLoader,
190
+ M as useNavigate,
191
+ q as useParams,
190
192
  Q as usePathname,
191
193
  G as useQuery,
192
194
  z as useSocket
@@ -0,0 +1,77 @@
1
+ /** One import in the chain: `from` imports `to` at `line` (`null` = generated). */
2
+ export interface LeakLink {
3
+ from: string;
4
+ line: number | null;
5
+ to: string;
6
+ }
7
+ export interface LeakItem {
8
+ /** Display path of the leaked server module. */
9
+ target: string;
10
+ /** Why it qualifies as a server module (English). */
11
+ qualifiers: string[];
12
+ /** Full import chain, client entry -> target, one link per import. */
13
+ chain: LeakLink[];
14
+ /** Index in `chain` of the entry-point link — the cut. */
15
+ entryIndex: number;
16
+ }
17
+ export interface LeakReport {
18
+ leaks: LeakItem[];
19
+ /** True when the enumeration stopped at its safety budget. */
20
+ truncated: boolean;
21
+ /** Stable fingerprint of what ships to the client (dev re-emit trigger). */
22
+ signature: string;
23
+ /** How many modules the walk visited (informational). */
24
+ moduleCount: number;
25
+ }
26
+ export type ResolveFn = (spec: string, importer: string) => string | null;
27
+ export interface ModuleInfo {
28
+ file: string;
29
+ display: string;
30
+ edges: RawEdge[];
31
+ builtins: string[];
32
+ patterns: string[];
33
+ }
34
+ export interface CachedModule {
35
+ mtimeMs: number;
36
+ info: ModuleInfo;
37
+ }
38
+ export interface LeakAnalyzerOptions {
39
+ /** Project root — display paths and `serverOnly` matching are relative to it. */
40
+ rootDir: string;
41
+ /** Absolute app directory (velo:transform's scope). */
42
+ appDir: string;
43
+ /** Routes file name inside appDir (default "routes.tsx"). */
44
+ routesFile?: string | undefined;
45
+ /** The client entry's import specifiers (the virtual entry's imports). */
46
+ entrySpecs: string[];
47
+ /** Glob patterns declaring server-only modules (VeloConfig.serverOnly). */
48
+ serverOnly?: readonly string[] | undefined;
49
+ /** Import resolver: (specifier, importer file) -> absolute file or null. */
50
+ resolve?: ResolveFn | undefined;
51
+ /** Optional parse cache shared across runs (dev). */
52
+ cache?: Map<string, CachedModule> | undefined;
53
+ }
54
+ interface RawEdge {
55
+ spec: string;
56
+ line: number;
57
+ kind: "static" | "dynamic";
58
+ locals: string[];
59
+ }
60
+ /** True when the specifier names a Node builtin (with or without the `node:` prefix). */
61
+ export declare function isBuiltinSpec(spec: string): boolean;
62
+ /**
63
+ * Compiles a glob (`*` = within a segment, `**` across segments, `?` = one
64
+ * char) into a full-match regex over the module path relative to the project
65
+ * root, always with `/` separators.
66
+ */
67
+ export declare function globToRegex(glob: string): RegExp;
68
+ /**
69
+ * Resolution mirroring what the client build follows: relative specifiers with
70
+ * TS extension completion, bare specifiers through `node_modules` with the
71
+ * package's browser/import `exports` conditions. Returns null for anything
72
+ * that does not resolve to a file (external, virtual or unresolvable).
73
+ */
74
+ export declare function defaultResolve(spec: string, importer: string): string | null;
75
+ export declare function analyzeClientLeaks(options: LeakAnalyzerOptions): LeakReport;
76
+ export declare function formatLeakReport(report: LeakReport): string;
77
+ export {};
@@ -0,0 +1,14 @@
1
+ import "node:fs";
2
+ import "node:path";
3
+ import "node:module";
4
+ import "@babel/parser";
5
+ import "@babel/traverse";
6
+ import "@babel/types";
7
+ import { a as s, d as l, f, g, i as n } from "./vite.js";
8
+ export {
9
+ s as analyzeClientLeaks,
10
+ l as defaultResolve,
11
+ f as formatLeakReport,
12
+ g as globToRegex,
13
+ n as isBuiltinSpec
14
+ };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Live actions — client runtime.
3
+ *
4
+ * A tab is not a route: it is client state, and its content loads at open time
5
+ * from an action with its own arguments (the resource id, the file path). When
6
+ * that content is a **value that tracks server state over time**, the action
7
+ * declares interest in living (`liveActions` in the route module) and the tab
8
+ * subscribes:
9
+ *
10
+ * ```tsx
11
+ * const { data, loading, freshness } = useLiveAction(action_abrirFicha, { id });
12
+ * ```
13
+ *
14
+ * The hook receives the action itself — its client stub, which the Vite plugin
15
+ * marks with the subscription address (`__path`, the socket-stub ruler) — and
16
+ * the args. It opens ONE SSE connection per subscription at
17
+ * `GET /_live/{moduleId}/{actionName}?_args=<json>`; the connect snapshot (the
18
+ * action executed once with the declared args) is the whole loading gesture —
19
+ * there is no initial POST. Every `emit()` in any of the action's declared
20
+ * channels re-executes the action server-side with the same args and pushes the
21
+ * fresh value, which **replaces** `data` whole (no merge — the updated payload
22
+ * has the same shape as the original answer).
23
+ *
24
+ * The subscription's life is the mount: the tab component unmounts (tab closed,
25
+ * page left) and the connection closes — nothing arrives afterwards and the
26
+ * server holds nothing for the gone. It remounts (tab reopened, refresh
27
+ * restored the tabs) and the subscription is born again with a snapshot of the
28
+ * current state: reopening is indistinguishable from never having closed. The
29
+ * args are the identity — changing them reconnects (new subscription, new
30
+ * snapshot), the twin of the loader connection keyed by its route params.
31
+ *
32
+ * `freshness` is the subscription's own `"live"` / `"stale"` / `"error"` signal
33
+ * — the same three states and the same 60s silence detector as the live loader
34
+ * — and it never touches the page aggregate: a stale tab does not stale the
35
+ * page, and the other way around.
36
+ *
37
+ * On the server (SSR) the hook does not connect: the tab is client state the
38
+ * server does not know, so it returns `data` null with `loading` true. In a
39
+ * static build the load really failed (there is no server to run the action):
40
+ * no connection is opened and the handle is `data` undefined, `loading` false,
41
+ * `freshness` `"error"` — the deliberate contrast with a channel, which stays
42
+ * `"live"` because there is nothing to sync.
43
+ */
44
+ import { type Signal } from "@preact/signals";
45
+ import { type Freshness } from "./live-loader.js";
46
+ /**
47
+ * The client stub of a live action — the form the Vite plugin generates for an
48
+ * action the module declares in `liveActions`. The call signature is the
49
+ * action's own (typed from its `ActionArgs` and return); the live marks are
50
+ * what `useLiveAction` reads to find the subscription address.
51
+ */
52
+ export interface LiveActionStub<TArgs extends Record<string, unknown>, TData> {
53
+ (args: {
54
+ body: TArgs;
55
+ }): Promise<TData>;
56
+ /** Set by the Vite plugin on every stub the module declares live. */
57
+ __isVeloLiveAction?: boolean;
58
+ /** `/_live/{moduleId}/{actionName}` — the subscription address. */
59
+ __path?: string;
60
+ }
61
+ /** What `useLiveAction` returns. */
62
+ export interface LiveActionHandle<T> {
63
+ /** The action's return, replaced whole by every snapshot. */
64
+ data: Signal<T | null | undefined>;
65
+ /** True until the first snapshot or error of the current subscription. */
66
+ loading: Signal<boolean>;
67
+ /** The subscription's own freshness — never the page aggregate's. */
68
+ freshness: Signal<Freshness>;
69
+ }
70
+ /**
71
+ * Subscribes to a live action and reads its value as it tracks server state.
72
+ *
73
+ * The connection is keyed by the args: passing different args is a different
74
+ * subscription (the old one closes, the new one opens with a fresh snapshot).
75
+ * The life is the mount — unmount closes, remount reopens with the current
76
+ * state. See the module docs for the full contract.
77
+ *
78
+ * ```tsx
79
+ * export const Component = () => {
80
+ * const { data, loading, freshness } = useLiveAction(action_abrirFicha, { id: 7 });
81
+ * if (loading.value) return <div class="tab-loading" />;
82
+ * return <article class={freshness.value}>{data.value?.nome}</article>;
83
+ * };
84
+ * ```
85
+ */
86
+ export declare function useLiveAction<TArgs extends Record<string, unknown>, TData>(action: LiveActionStub<TArgs, TData>, args: TArgs): LiveActionHandle<TData>;
@@ -0,0 +1,52 @@
1
+ import { useSignal as s } from "@preact/signals";
2
+ import { useEffect as d } from "preact/hooks";
3
+ import { CHANNEL_SILENCE_MS as _ } from "./live-loader.js";
4
+ const p = "snapshot";
5
+ function S(n, v) {
6
+ const l = s(null), t = s(!0), e = s("live"), i = JSON.stringify(v ?? {});
7
+ return d(() => {
8
+ if (typeof window > "u") return;
9
+ if (typeof __VELO_STATIC__ < "u" && __VELO_STATIC__) {
10
+ t.value = !1, e.value = "error", l.value = void 0;
11
+ return;
12
+ }
13
+ if (!n.__isVeloLiveAction || !n.__path) {
14
+ console.warn(
15
+ '[velojs] useLiveAction: this action has no live declaration — add it to the module\'s `liveActions` export (e.g. `export const liveActions = { abrirFicha: ["fichaProjeto"] };`).'
16
+ ), t.value = !1, e.value = "error";
17
+ return;
18
+ }
19
+ t.value = !0;
20
+ const f = `${n.__path}?_args=${encodeURIComponent(i)}`, o = new EventSource(f);
21
+ let r = null;
22
+ const u = () => {
23
+ r !== null && clearTimeout(r), r = setTimeout(() => {
24
+ r = null, e.value = "stale";
25
+ }, _);
26
+ };
27
+ return u(), o.addEventListener(p, (a) => {
28
+ try {
29
+ l.value = JSON.parse(a.data), t.value = !1, e.value = "live", u();
30
+ } catch {
31
+ }
32
+ }), o.addEventListener("error", (a) => {
33
+ const c = a.data;
34
+ if (typeof c == "string" && c.length > 0) {
35
+ t.value = !1, e.value = "error";
36
+ return;
37
+ }
38
+ e.value = "stale";
39
+ }), o.addEventListener("close", () => {
40
+ e.value = "stale";
41
+ }), () => {
42
+ r !== null && clearTimeout(r);
43
+ try {
44
+ o.close();
45
+ } catch {
46
+ }
47
+ };
48
+ }, [n.__path, i]), { data: l, loading: t, freshness: e };
49
+ }
50
+ export {
51
+ S as useLiveAction
52
+ };
package/dist/server.d.ts CHANGED
@@ -2,7 +2,7 @@ import { Hono } from "hono";
2
2
  import type { AppRoutes } from "./types.js";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
  export { emit, inspectChannels, registerChannels } from "./channels.js";
5
- export type { ChannelDefinition, ChannelMap, ChannelScopeContext, ChannelGroupOpenContext, ChannelGroupCloseContext, ChannelScopeResult, ChannelRegistryOptions, ChannelEmitModule, ChannelEmitKind, ChannelSlice, ChannelInspectGroup, ChannelInspectChannel, ChannelInspectWindow, ChannelInspectReport, } from "./channels.js";
5
+ export type { ChannelDefinition, ChannelMap, ChannelScopeContext, ChannelGroupOpenContext, ChannelGroupCloseContext, ChannelScopeResult, ChannelRegistryOptions, ChannelEmitModule, ChannelEmitKind, ChannelSlice, ChannelInspectGroup, ChannelInspectChannel, ChannelInspectWindow, ChannelInspectLiveAction, ChannelInspectReport, } from "./channels.js";
6
6
  export declare const serverDataStorage: AsyncLocalStorage<Record<string, unknown>>;
7
7
  type ServerCallback = (server: import("http").Server) => void;
8
8
  export declare function onServer(fn: ServerCallback): void;