@mauroandre/velojs 0.0.22 → 0.0.24

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/server.js CHANGED
@@ -1,200 +1,200 @@
1
- import { jsx as _ } from "preact/jsx-runtime";
2
- import { Hono as T } from "hono";
3
- import { logger as b } from "hono/logger";
4
- import { trimTrailingSlash as A } from "hono/trailing-slash";
5
- import { render as R } from "preact-render-to-string";
6
- import { Router as D } from "wouter-preact";
7
- import { AsyncLocalStorage as I } from "node:async_hooks";
8
- const y = new I();
9
- globalThis.__veloServerData = y;
10
- const v = [];
11
- let g = null;
12
- function L(t) {
13
- if (g) {
14
- t(g);
1
+ import { jsx as p } from "preact/jsx-runtime";
2
+ import { Hono as b } from "hono";
3
+ import { logger as q } from "hono/logger";
4
+ import { trimTrailingSlash as T } from "hono/trailing-slash";
5
+ import { render as A } from "preact-render-to-string";
6
+ import { Router as R } from "wouter-preact";
7
+ import { AsyncLocalStorage as C } from "node:async_hooks";
8
+ import { getAppContext as g } from "./app-context.js";
9
+ import { flushPendingStreamRoutes as D, registerStreamHandler as I } from "./events.js";
10
+ const v = new C();
11
+ globalThis.__veloServerData = v;
12
+ let _ = null;
13
+ function W(e) {
14
+ if (_) {
15
+ e(_);
15
16
  return;
16
17
  }
17
- v.push(t);
18
+ g().serverCallbacks.push(e);
18
19
  }
19
- function w(t) {
20
- g = t;
21
- for (const e of v) e(t);
22
- v.length = 0;
20
+ function y(e) {
21
+ _ = e;
22
+ const t = g();
23
+ for (const s of t.serverCallbacks) s(e);
24
+ t.serverCallbacks.length = 0;
23
25
  }
24
- const S = [];
25
- function U(t) {
26
- S.push(t);
26
+ function J(e) {
27
+ g().pendingRoutes.push(e);
27
28
  }
28
- const N = (t, e, s) => {
29
- if (t.req.query("_data") === "1") {
30
- const c = globalThis.__veloBuildHash || void 0, o = s ? { ...s, __buildHash: c } : { __buildHash: c };
31
- return t.json(o);
29
+ const N = (e, t, s) => {
30
+ if (e.req.query("_data") === "1") {
31
+ const a = globalThis.__veloBuildHash || void 0, r = s ? { ...s, __buildHash: a } : { __buildHash: a };
32
+ return e.json(r);
32
33
  }
33
- const r = t.req.path, n = y.run(
34
+ const n = e.req.path, o = v.run(
34
35
  s ?? {},
35
- () => R(/* @__PURE__ */ _(D, { ssrPath: r, children: e }))
36
+ () => A(/* @__PURE__ */ p(R, { ssrPath: n, children: t }))
36
37
  );
37
- if (t.header("Cache-Control", "no-cache"), !s)
38
- return t.html(n);
39
- const a = `<script>window.__PAGE_DATA__=${JSON.stringify(
38
+ if (e.header("Cache-Control", "no-cache"), !s)
39
+ return e.html(o);
40
+ const c = `<script>window.__PAGE_DATA__=${JSON.stringify(
40
41
  s
41
42
  )}<\/script>`;
42
- return t.html(n.replace("</head>", `${a}</head>`));
43
- }, O = async (t, e) => {
44
- const s = e.req.param(), r = e.req.query(), n = { params: s, query: r, c: e }, a = await Promise.all(
45
- t.map(async (o) => {
46
- if (!o.loader) return null;
47
- const l = await o.loader(n), i = o.metadata?.moduleId;
48
- return i ? { moduleId: i, loaderData: l } : null;
43
+ return e.html(o.replace("</head>", `${c}</head>`));
44
+ }, O = async (e, t) => {
45
+ const s = t.req.param(), n = t.req.query(), o = { params: s, query: n, c: t }, c = await Promise.all(
46
+ e.map(async (r) => {
47
+ if (!r.loader) return null;
48
+ const d = await r.loader(o), i = r.metadata?.moduleId;
49
+ return i ? { moduleId: i, loaderData: d } : null;
49
50
  })
50
- ), c = {
51
+ ), a = {
51
52
  __params: s,
52
- __query: r,
53
- __pathname: e.req.path
53
+ __query: n,
54
+ __pathname: t.req.path
54
55
  };
55
- for (const o of a)
56
- o && (c[o.moduleId] = o.loaderData);
56
+ for (const r of c)
57
+ r && (a[r.moduleId] = r.loaderData);
57
58
  return {
58
- components: t.map((o) => o.Component),
59
- data: c
59
+ components: e.map((r) => r.Component),
60
+ data: a
60
61
  };
61
- }, j = (t) => {
62
- if (t.length === 0) return null;
63
- const e = t.filter(Boolean);
62
+ }, j = (e) => {
64
63
  if (e.length === 0) return null;
65
- if (e.length === 1) {
66
- const n = e[0];
67
- return /* @__PURE__ */ _(n, {});
64
+ const t = e.filter(Boolean);
65
+ if (t.length === 0) return null;
66
+ if (t.length === 1) {
67
+ const o = t[0];
68
+ return /* @__PURE__ */ p(o, {});
68
69
  }
69
- const s = e[e.length - 1];
70
- return e.slice(0, -1).reduceRight((n, a) => /* @__PURE__ */ _(a, { children: n }), /* @__PURE__ */ _(s, {}));
71
- }, P = (t, e, s = [], r = []) => {
72
- for (const n of e) {
73
- const a = [...s, n.module], c = [
74
- ...r,
75
- ...n.middlewares || []
70
+ const s = t[t.length - 1];
71
+ return t.slice(0, -1).reduceRight((o, c) => /* @__PURE__ */ p(c, { children: o }), /* @__PURE__ */ p(s, {}));
72
+ }, w = (e, t, s = [], n = []) => {
73
+ for (const o of t) {
74
+ const c = [...s, o.module], a = [
75
+ ...n,
76
+ ...o.middlewares || []
76
77
  ];
77
- if (n.children)
78
- P(
79
- t,
80
- n.children,
81
- a,
82
- c
78
+ if (o.children)
79
+ w(
80
+ e,
81
+ o.children,
82
+ c,
83
+ a
83
84
  );
84
85
  else {
85
- const o = n.module.metadata?.fullPath;
86
- if (!o) {
86
+ const r = o.module.metadata?.fullPath;
87
+ if (!r) {
87
88
  console.warn(
88
- `Module ${n.module.metadata?.moduleId} has no fullPath`
89
+ `Module ${o.module.metadata?.moduleId} has no fullPath`
89
90
  );
90
91
  continue;
91
92
  }
92
- const l = async (i) => {
93
- const { components: m, data: d } = await O(a, i), u = j(m);
94
- return N(i, u, d);
93
+ const d = async (i) => {
94
+ const { components: u, data: m } = await O(c, i), l = j(u);
95
+ return N(i, l, m);
95
96
  };
96
- c.length > 0 ? t.on(["GET"], [o], ...c, l) : t.on(["GET"], [o], l);
97
+ a.length > 0 ? e.on(["GET"], [r], ...a, d) : e.on(["GET"], [r], d);
97
98
  }
98
99
  }
99
- }, E = (t, e, s = []) => {
100
- for (const r of e) {
101
- const n = r.module.metadata?.moduleId, a = [
100
+ }, S = (e, t, s = []) => {
101
+ for (const n of t) {
102
+ const o = n.module.metadata?.moduleId, c = [
102
103
  ...s,
103
- ...r.middlewares || []
104
+ ...n.middlewares || []
104
105
  ];
105
- if (n) {
106
- const c = Object.keys(r.module).filter(
107
- (o) => o.startsWith("action_")
106
+ if (o) {
107
+ const a = Object.keys(n.module).filter(
108
+ (r) => r.startsWith("action_")
108
109
  );
109
- for (const o of c) {
110
- const l = o.replace("action_", ""), i = r.module[o];
110
+ for (const r of a) {
111
+ const d = r.replace("action_", ""), i = n.module[r];
111
112
  if (typeof i == "function") {
112
- const m = `/_action/${n}/${l}`, d = async (u) => {
113
+ const u = `/_action/${o}/${d}`, m = async (l) => {
113
114
  let f = {};
114
115
  try {
115
- f = await u.req.json();
116
+ f = await l.req.json();
116
117
  } catch {
117
118
  }
118
- const h = {
119
+ const P = {
119
120
  body: f,
120
- params: u.req.param(),
121
- query: u.req.query(),
122
- c: u
121
+ params: l.req.param(),
122
+ query: l.req.query(),
123
+ c: l
123
124
  };
124
125
  try {
125
- const p = await i(h);
126
- return u.json(p ?? { ok: !0 });
127
- } catch (p) {
128
- const q = p instanceof Error ? p.message : "Action failed";
129
- return u.json({ error: q }, 500);
126
+ const h = await i(P);
127
+ return l.json(h ?? { ok: !0 });
128
+ } catch (h) {
129
+ const E = h instanceof Error ? h.message : "Action failed";
130
+ return l.json({ error: E }, 500);
130
131
  }
131
132
  };
132
- a.length > 0 ? t.on(
133
+ c.length > 0 ? e.on(
133
134
  ["POST"],
134
- [m],
135
- ...a,
136
- d
137
- ) : t.on(["POST"], [m], d);
135
+ [u],
136
+ ...c,
137
+ m
138
+ ) : e.on(["POST"], [u], m);
138
139
  }
139
140
  }
140
141
  }
141
- r.children && E(t, r.children, a);
142
+ n.children && S(e, n.children, c);
142
143
  }
143
- }, C = async (t, e, s = []) => {
144
- const { registerStreamHandler: r } = await import("./events.js"), n = (a, c) => {
145
- for (const o of a) {
146
- const l = o.module.metadata?.moduleId, i = [
144
+ }, $ = async (e, t, s = []) => {
145
+ const n = (o, c) => {
146
+ for (const a of o) {
147
+ const r = a.module.metadata?.moduleId, d = [
147
148
  ...c,
148
- ...o.middlewares || []
149
+ ...a.middlewares || []
149
150
  ];
150
- if (l) {
151
- const m = Object.keys(o.module).filter(
152
- (d) => d.startsWith("stream_")
151
+ if (r) {
152
+ const i = Object.keys(a.module).filter(
153
+ (u) => u.startsWith("stream_")
153
154
  );
154
- for (const d of m) {
155
- const u = d.replace("stream_", ""), f = o.module[d];
156
- if (f?.__isVeloEventStream) {
157
- const h = `/_event/${l}/${u}`;
158
- f.__path = h, r(
159
- t,
160
- h,
155
+ for (const u of i) {
156
+ const m = u.replace("stream_", ""), l = a.module[u];
157
+ if (l?.__isVeloEventStream) {
158
+ const f = `/_event/${r}/${m}`;
159
+ l.__path = f, I(
160
+ e,
161
161
  f,
162
- i
162
+ l,
163
+ d
163
164
  );
164
165
  }
165
166
  }
166
167
  }
167
- o.children && n(o.children, i);
168
+ a.children && n(a.children, d);
168
169
  }
169
170
  };
170
- n(e, s);
171
- }, $ = async (t) => {
172
- const e = new T();
173
- e.use(A()), process.env.NODE_ENV !== "production" && e.use("*", b());
174
- for (const r of S)
175
- await r(e);
176
- P(e, t), E(e, t), await C(e, t);
177
- const { flushPendingStreamRoutes: s } = await import("./events.js");
178
- if (s(e), process.env.NODE_ENV !== "production" && !g) {
179
- const r = globalThis.__veloDevServer;
180
- r && w(r);
171
+ n(t, s);
172
+ }, k = async (e) => {
173
+ const t = new b();
174
+ t.use(T()), process.env.NODE_ENV !== "production" && t.use("*", q());
175
+ const n = g().pendingRoutes.splice(0);
176
+ for (const o of n)
177
+ await o(t);
178
+ if (w(t, e), S(t, e), await $(t, e), D(t), process.env.NODE_ENV !== "production" && !_) {
179
+ const o = globalThis.__veloDevServer;
180
+ o && y(o);
181
181
  }
182
- return e;
183
- }, W = async (t) => {
184
- const { routes: e, port: s = Number(process.env.SERVER_PORT) || 3e3 } = t, r = await $(e);
182
+ return t;
183
+ }, z = async (e) => {
184
+ const { routes: t, port: s = Number(process.env.SERVER_PORT) || 3e3 } = e, n = await k(t);
185
185
  if (process.env.NODE_ENV === "production" && !process.env.VELO_STATIC) {
186
- const { serve: n } = await import("@hono/node-server"), { serveStatic: a } = await import("@hono/node-server/serve-static"), { join: c } = await import("node:path"), o = c(process.cwd(), "dist/client");
187
- (process.env.STATIC_BASE_URL || "").startsWith("http") || r.use("/*", a({ root: o })), console.log(`Server running on http://localhost:${s}`);
188
- const i = n({ fetch: r.fetch, port: s });
189
- w(i);
186
+ const { serve: o } = await import("@hono/node-server"), { serveStatic: c } = await import("@hono/node-server/serve-static"), { join: a } = await import("node:path"), r = a(process.cwd(), "dist/client");
187
+ (process.env.STATIC_BASE_URL || "").startsWith("http") || n.use("/*", c({ root: r })), console.log(`Server running on http://localhost:${s}`);
188
+ const i = o({ fetch: n.fetch, port: s });
189
+ y(i);
190
190
  }
191
- return r;
191
+ return n;
192
192
  };
193
193
  export {
194
- U as addRoutes,
195
- $ as createApp,
196
- $ as default,
197
- L as onServer,
198
- y as serverDataStorage,
199
- W as startServer
194
+ J as addRoutes,
195
+ k as createApp,
196
+ k as default,
197
+ W as onServer,
198
+ v as serverDataStorage,
199
+ z as startServer
200
200
  };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Resolve URLs from VeloJS function references (action_*, loader, stream_*).
3
+ * Relies on metadata injected by the VeloJS Vite plugin at build time.
4
+ */
5
+ import type { AppRoutes } from "../types.js";
6
+ interface ActionEntry {
7
+ name: string;
8
+ fn: Function;
9
+ moduleId: string;
10
+ url: string;
11
+ }
12
+ interface LoaderEntry {
13
+ fn: Function;
14
+ moduleId: string;
15
+ /** Multiple URL paths can share a loader if registered under different parents. */
16
+ paths: string[];
17
+ }
18
+ export interface ConventionRegistry {
19
+ actions: Map<Function, ActionEntry>;
20
+ loaders: Map<Function, LoaderEntry>;
21
+ }
22
+ export declare function buildConventionRegistry(routes: AppRoutes): ConventionRegistry;
23
+ export declare function resolveActionUrl(registry: ConventionRegistry, fnOrUrl: Function | string): string;
24
+ export declare function resolveLoaderPath(registry: ConventionRegistry, fnOrUrl: Function | string, params?: Record<string, string>): string;
25
+ export {};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * createTestApp — main entry point for the VeloJS testing toolkit.
3
+ *
4
+ * Builds an in-memory Hono app from your `routes` import, isolates side effects
5
+ * via AsyncLocalStorage so multiple test apps coexist, and exposes a
6
+ * FastAPI-TestClient-style API on top.
7
+ */
8
+ import type { TestApp, CreateTestAppOptions } from "./types.js";
9
+ /**
10
+ * Creates an isolated test app from your routes.
11
+ *
12
+ * ```typescript
13
+ * import { createTestApp } from "@mauroandre/velojs/testing";
14
+ * import { routes } from "../app/routes.js";
15
+ *
16
+ * const app = await createTestApp({
17
+ * routes,
18
+ * bootstrap: async () => { await connectDb(); },
19
+ * getSessionCookie: async ({ user }) => ({ session: await sign(user) }),
20
+ * });
21
+ * ```
22
+ */
23
+ export declare function createTestApp(opts: CreateTestAppOptions): Promise<TestApp>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @mauroandre/velojs/testing
3
+ *
4
+ * Backend testing toolkit for VeloJS apps. FastAPI-TestClient-style API:
5
+ * spin up the app in memory, fire HTTP requests against the registered handlers,
6
+ * subscribe to event streams, assert on the result. No socket, no browser.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createTestApp } from "@mauroandre/velojs/testing";
11
+ * import { routes } from "../app/routes.js";
12
+ *
13
+ * const app = await createTestApp({ routes });
14
+ * const res = await app.get("/api/health");
15
+ * expect(res.status).toBe(200);
16
+ * await app.close();
17
+ * ```
18
+ */
19
+ export { createTestApp } from "./createTestApp.js";
20
+ export type { TestApp, TestResponse, TestSubscription, CreateTestAppOptions, RequestOptions, BodyRequestOptions, LoaderRequestOptions, SubscribeOptions, NextOptions, MockContextOptions, Cookies, Headers, Query, Params, } from "./types.js";