@mauroandre/velojs 0.0.60 → 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.
@@ -1,61 +1,61 @@
1
- import d from "node:fs";
1
+ import $ from "node:fs";
2
2
  import a from "node:path";
3
- import { expandTelemetryValue as w } from "./telemetry.js";
3
+ import { expandTelemetryValue as g } from "./telemetry.js";
4
4
  import { readProjectEnv as k } from "./telemetry-node.js";
5
5
  function j(e) {
6
- const o = e.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/);
7
- if (!o)
6
+ const r = e.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/);
7
+ if (!r)
8
8
  throw new Error(
9
9
  `[velojs] invalid duration "${e}" — use a unit: 500ms, 30s, 60m, 2h, 7d.`
10
10
  );
11
- const n = Number(o[1]), i = o[2] ?? "s";
11
+ const n = Number(r[1]), i = r[2] ?? "s";
12
12
  return n * (i === "ms" ? 1 : i === "s" ? 1e3 : i === "m" ? 6e4 : i === "h" ? 36e5 : 864e5);
13
13
  }
14
- function g(e) {
14
+ function x(e) {
15
15
  try {
16
- return d.statSync(e);
16
+ return $.statSync(e);
17
17
  } catch {
18
18
  return null;
19
19
  }
20
20
  }
21
- function x(e, o) {
22
- const n = g(e);
21
+ function L(e, r) {
22
+ const n = x(e);
23
23
  if (n?.isDirectory())
24
- return d.readdirSync(e).filter((u) => u.includes(".jsonl")).sort().map((u) => a.join(e, u));
24
+ return $.readdirSync(e).filter((c) => c.includes(".jsonl")).sort().map((c) => a.join(e, c));
25
25
  if (n?.isFile()) return [e];
26
- if (!o)
26
+ if (!r)
27
27
  throw new Error(`[velojs] velojs telemetry: file not found — ${e}`);
28
- const i = a.dirname(e), t = a.basename(e), r = t.lastIndexOf("."), s = r > 0 ? t.slice(0, r) : t, l = r > 0 ? t.slice(r) : "";
29
- if (!g(i))
28
+ const i = a.dirname(e), t = a.basename(e), o = t.lastIndexOf("."), s = o > 0 ? t.slice(0, o) : t, l = o > 0 ? t.slice(o) : "";
29
+ if (!x(i))
30
30
  throw new Error(
31
31
  `[velojs] velojs telemetry: no telemetry files at ${e} — the directory does not exist.`
32
32
  );
33
- const h = d.readdirSync(i).filter((u) => u === t ? !0 : u.startsWith(`${s}.`) ? l === "" || u.includes(l) : !1).sort().map((u) => a.join(i, u));
33
+ const h = $.readdirSync(i).filter((c) => c === t ? !0 : c.startsWith(`${s}.`) ? l === "" || c.includes(l) : !1).sort().map((c) => a.join(i, c));
34
34
  if (h.length === 0)
35
35
  throw new Error(
36
36
  `[velojs] velojs telemetry: no telemetry files found for ${e} — set VELO_TELEMETRY_FILE (see .env.example) or pass the JSONL path explicitly.`
37
37
  );
38
38
  return h;
39
39
  }
40
- function M(e, o, n, i) {
40
+ function M(e, r, n, i) {
41
41
  if (e) {
42
- const l = w(e, { ...n, ...o }), h = a.isAbsolute(l) ? l : a.resolve(i, l);
43
- return x(h, !1);
42
+ const l = g(e, { ...n, ...r }), h = a.isAbsolute(l) ? l : a.resolve(i, l);
43
+ return L(h, !1);
44
44
  }
45
- const t = o.VELO_TELEMETRY_FILE ?? n.VELO_TELEMETRY_FILE ?? "";
45
+ const t = r.VELO_TELEMETRY_FILE ?? n.VELO_TELEMETRY_FILE ?? "";
46
46
  if (!t.trim())
47
47
  throw new Error(
48
48
  "[velojs] velojs telemetry: no destination — set VELO_TELEMETRY_FILE in the environment or in the project's .env (see .env.example), or pass the JSONL path as an argument."
49
49
  );
50
- const r = w(t.trim(), { ...n, ...o }), s = a.isAbsolute(r) ? r : a.resolve(i, r);
51
- return x(s, !0);
50
+ const o = g(t.trim(), { ...n, ...r }), s = a.isAbsolute(o) ? o : a.resolve(i, o);
51
+ return L(s, !0);
52
52
  }
53
53
  function O(e) {
54
- const o = [];
54
+ const r = [];
55
55
  for (const n of e) {
56
56
  let i;
57
57
  try {
58
- i = d.readFileSync(n, "utf-8");
58
+ i = $.readFileSync(n, "utf-8");
59
59
  } catch (t) {
60
60
  throw new Error(
61
61
  `[velojs] velojs telemetry: cannot read ${n} — ${t instanceof Error ? t.message : String(t)}`
@@ -63,56 +63,56 @@ function O(e) {
63
63
  }
64
64
  for (const t of i.split(`
65
65
  `)) {
66
- const r = t.trim();
67
- if (r)
66
+ const o = t.trim();
67
+ if (o)
68
68
  try {
69
- const s = JSON.parse(r);
70
- s && typeof s == "object" && typeof s.type == "string" && o.push(s);
69
+ const s = JSON.parse(o);
70
+ s && typeof s == "object" && typeof s.type == "string" && r.push(s);
71
71
  } catch {
72
72
  }
73
73
  }
74
74
  }
75
- return o.sort((n, i) => {
76
- const t = Date.parse(n.ts ?? ""), r = Date.parse(i.ts ?? "");
77
- return (Number.isFinite(t) ? t : 0) - (Number.isFinite(r) ? r : 0);
78
- }), o;
75
+ return r.sort((n, i) => {
76
+ const t = Date.parse(n.ts ?? ""), o = Date.parse(i.ts ?? "");
77
+ return (Number.isFinite(t) ? t : 0) - (Number.isFinite(o) ? o : 0);
78
+ }), r;
79
79
  }
80
- function N(e, o, n) {
81
- if (o === null) return e;
82
- const i = n - o;
80
+ function N(e, r, n) {
81
+ if (r === null) return e;
82
+ const i = n - r;
83
83
  return e.filter((t) => {
84
- const r = Date.parse(t.ts ?? "");
85
- return Number.isFinite(r) && r >= i;
84
+ const o = Date.parse(t.ts ?? "");
85
+ return Number.isFinite(o) && o >= i;
86
86
  });
87
87
  }
88
- function L(e, o) {
88
+ function T(e, r) {
89
89
  if (e.length === 0) return null;
90
90
  const n = [...e].sort((s, l) => s - l);
91
91
  if (n.length === 1) return n[0];
92
- const i = (n.length - 1) * o, t = Math.floor(i), r = Math.ceil(i);
93
- return t === r ? n[t] : n[t] + (n[r] - n[t]) * (i - t);
92
+ const i = (n.length - 1) * r, t = Math.floor(i), o = Math.ceil(i);
93
+ return t === o ? n[t] : n[t] + (n[o] - n[t]) * (i - t);
94
94
  }
95
- function y(e, o, n) {
95
+ function m(e, r, n) {
96
96
  const i = /* @__PURE__ */ new Map();
97
97
  for (const t of e) {
98
- if (!o(t)) continue;
99
- const r = n(t);
100
- let s = i.get(r);
101
- s || (s = { durations: [], count: 0, errors: 0, bytes: 0 }, i.set(r, s)), s.count += 1, t.status === "error" && (s.errors += 1), typeof t.duration == "number" && Number.isFinite(t.duration) && s.durations.push(t.duration), typeof t.bytes == "number" && Number.isFinite(t.bytes) && (s.bytes += t.bytes);
98
+ if (!r(t)) continue;
99
+ const o = n(t);
100
+ let s = i.get(o);
101
+ s || (s = { durations: [], count: 0, errors: 0, bytes: 0 }, i.set(o, s)), s.count += 1, t.status === "error" && (s.errors += 1), typeof t.duration == "number" && Number.isFinite(t.duration) && s.durations.push(t.duration), typeof t.bytes == "number" && Number.isFinite(t.bytes) && (s.bytes += t.bytes);
102
102
  }
103
- return [...i.entries()].map(([t, r]) => ({
103
+ return [...i.entries()].map(([t, o]) => ({
104
104
  key: t,
105
- count: r.count,
106
- errors: r.errors,
107
- p50: L(r.durations, 0.5),
108
- p95: L(r.durations, 0.95),
109
- bytes: r.bytes
110
- })).sort((t, r) => r.count - t.count || (t.key < r.key ? -1 : t.key > r.key ? 1 : 0));
105
+ count: o.count,
106
+ errors: o.errors,
107
+ p50: T(o.durations, 0.5),
108
+ p95: T(o.durations, 0.95),
109
+ bytes: o.bytes
110
+ })).sort((t, o) => o.count - t.count || (t.key < o.key ? -1 : t.key > o.key ? 1 : 0));
111
111
  }
112
112
  const R = (e) => e.type === "page" || e.type === "data" || e.type === "endpoint", D = (e) => {
113
- const o = e.route ?? "(no route)";
114
- return e.type === "endpoint" && e.method ? `${e.method} ${o}` : o;
115
- }, S = (e) => `${e.module ?? "(no module)"}/${e.name ?? "(no name)"}`, _ = (e) => typeof e.channel == "string";
113
+ const r = e.route ?? "(no route)";
114
+ return e.type === "endpoint" && e.method ? `${e.method} ${r}` : r;
115
+ }, d = (e) => `${e.module ?? "(no module)"}/${e.name ?? "(no name)"}`, _ = (e) => typeof e.channel == "string", v = (e) => e.type === "live-action-connect" || (e.type === "delivery" || e.type === "close") && typeof e.name == "string";
116
116
  function B(e) {
117
117
  switch (e.type) {
118
118
  case "page":
@@ -120,13 +120,15 @@ function B(e) {
120
120
  case "endpoint":
121
121
  return `${e.method ? `${e.method} ` : ""}${e.route ?? "(no route)"}`;
122
122
  case "action":
123
- return S(e);
123
+ return d(e);
124
+ case "live-action-connect":
125
+ return `${d(e)} (live)`;
124
126
  case "emit":
125
127
  return `${e.channel ?? "?"} (${e.mode ?? "?"}${e.connections !== void 0 ? `, ${e.connections} conn` : ""})`;
126
128
  case "delivery":
127
- return `${e.channel ?? "?"} #${e.deliveries ?? "?"}`;
129
+ return e.channel === void 0 && e.name !== void 0 ? `${d(e)} #${e.deliveries ?? "?"}` : `${e.channel ?? "?"} #${e.deliveries ?? "?"}`;
128
130
  case "close":
129
- return `${e.channel ?? "?"} ${e.deliveries ?? 0} deliveries`;
131
+ return e.channel === void 0 && e.name !== void 0 ? `${d(e)} ${e.deliveries ?? 0} deliveries` : `${e.channel ?? "?"} ${e.deliveries ?? 0} deliveries`;
130
132
  case "stream-connect":
131
133
  return `${e.route ?? "?"}${e.channel ? ` [${e.channel}]` : ""}`;
132
134
  case "socket-connect":
@@ -139,14 +141,15 @@ function B(e) {
139
141
  return e.route ?? e.channel ?? "?";
140
142
  }
141
143
  }
142
- function I(e, o) {
144
+ function A(e, r) {
143
145
  return {
144
146
  files: e,
145
- events: o,
146
- routes: y(o, R, D),
147
- actions: y(o, (n) => n.type === "action", S),
148
- channels: y(o, _, (n) => n.channel),
149
- timeline: o.map((n) => ({
147
+ events: r,
148
+ routes: m(r, R, D),
149
+ actions: m(r, (n) => n.type === "action", d),
150
+ channels: m(r, _, (n) => n.channel),
151
+ liveActions: m(r, v, d),
152
+ timeline: r.map((n) => ({
150
153
  ts: n.ts ?? "",
151
154
  type: n.type,
152
155
  label: B(n),
@@ -157,59 +160,59 @@ function I(e, o) {
157
160
  }))
158
161
  };
159
162
  }
160
- const V = "—";
163
+ const I = "—";
161
164
  function p(e) {
162
- return e === null ? V : e >= 1e3 ? `${(e / 1e3).toFixed(2)}s` : `${e.toFixed(1)}ms`;
165
+ return e === null ? I : e >= 1e3 ? `${(e / 1e3).toFixed(2)}s` : `${e.toFixed(1)}ms`;
163
166
  }
164
- function E(e) {
167
+ function w(e) {
165
168
  return e <= 0 ? "0" : e >= 1024 * 1024 ? `${(e / (1024 * 1024)).toFixed(1)}MB` : e >= 1024 ? `${(e / 1024).toFixed(1)}KB` : `${e}B`;
166
169
  }
167
- function $(e, o) {
170
+ function y(e, r) {
168
171
  const n = [e];
169
- if (o.length === 0)
172
+ if (r.length === 0)
170
173
  return n.push(" (nothing)"), n;
171
174
  const i = {
172
- key: Math.max(3, ...o.map((s) => s.key.length)),
173
- count: Math.max(5, ...o.map((s) => String(s.count).length)),
174
- errors: Math.max(6, ...o.map((s) => String(s.errors).length)),
175
- p50: Math.max(4, ...o.map((s) => p(s.p50).length)),
176
- p95: Math.max(4, ...o.map((s) => p(s.p95).length)),
177
- bytes: Math.max(5, ...o.map((s) => E(s.bytes).length))
178
- }, t = (s, l) => s.padEnd(l), r = (s, l) => s.padStart(l);
175
+ key: Math.max(3, ...r.map((s) => s.key.length)),
176
+ count: Math.max(5, ...r.map((s) => String(s.count).length)),
177
+ errors: Math.max(6, ...r.map((s) => String(s.errors).length)),
178
+ p50: Math.max(4, ...r.map((s) => p(s.p50).length)),
179
+ p95: Math.max(4, ...r.map((s) => p(s.p95).length)),
180
+ bytes: Math.max(5, ...r.map((s) => w(s.bytes).length))
181
+ }, t = (s, l) => s.padEnd(l), o = (s, l) => s.padStart(l);
179
182
  n.push(
180
- ` ${t("key", i.key)} ${r("count", i.count)} ${r("errors", i.errors)} ${r("p50", i.p50)} ${r("p95", i.p95)} ${r("bytes", i.bytes)}`
183
+ ` ${t("key", i.key)} ${o("count", i.count)} ${o("errors", i.errors)} ${o("p50", i.p50)} ${o("p95", i.p95)} ${o("bytes", i.bytes)}`
181
184
  );
182
- for (const s of o)
185
+ for (const s of r)
183
186
  n.push(
184
- ` ${t(s.key, i.key)} ${r(String(s.count), i.count)} ${r(String(s.errors), i.errors)} ${r(p(s.p50), i.p50)} ${r(p(s.p95), i.p95)} ${r(E(s.bytes), i.bytes)}`
187
+ ` ${t(s.key, i.key)} ${o(String(s.count), i.count)} ${o(String(s.errors), i.errors)} ${o(p(s.p50), i.p50)} ${o(p(s.p95), i.p95)} ${o(w(s.bytes), i.bytes)}`
185
188
  );
186
189
  return n;
187
190
  }
188
- function J(e) {
189
- const o = ["Timeline"];
191
+ function V(e) {
192
+ const r = ["Timeline"];
190
193
  if (e.length === 0)
191
- return o.push(" (nothing)"), o;
194
+ return r.push(" (nothing)"), r;
192
195
  for (const n of e) {
193
- const i = n.ts.replace("T", " ").replace("Z", ""), t = n.duration === null ? "" : ` ${p(n.duration)}`, r = n.status ? ` ${n.status}` : "", s = n.bytes === null ? "" : ` ${E(n.bytes)}`;
194
- o.push(` ${i} ${n.type.padEnd(15)} ${n.label}${t}${r}${s}`);
196
+ const i = n.ts.replace("T", " ").replace("Z", ""), t = n.duration === null ? "" : ` ${p(n.duration)}`, o = n.status ? ` ${n.status}` : "", s = n.bytes === null ? "" : ` ${w(n.bytes)}`;
197
+ r.push(` ${i} ${n.type.padEnd(15)} ${n.label}${t}${o}${s}`);
195
198
  }
196
- return o;
199
+ return r;
197
200
  }
198
- function v(e, o, n, i) {
201
+ function J(e, r, n, i) {
199
202
  const t = ["VeloJS telemetry"];
200
203
  t.push(`files: ${e.files.length}`);
201
204
  for (const s of e.files) t.push(` ${s}`);
202
- const r = o === null ? "the whole file" : `last ${i ?? W(o)} — since ${new Date(n - o).toISOString()}`;
203
- return t.push(`window: ${r}`), t.push(`events: ${e.events.length}`), t.push(""), t.push(...$("By route", e.routes)), t.push(""), t.push(...$("By action", e.actions)), t.push(""), t.push(...$("By channel", e.channels)), t.push(""), t.push(...J(e.timeline)), t.join(`
205
+ const o = r === null ? "the whole file" : `last ${i ?? W(r)} — since ${new Date(n - r).toISOString()}`;
206
+ return t.push(`window: ${o}`), t.push(`events: ${e.events.length}`), t.push(""), t.push(...y("By route", e.routes)), t.push(""), t.push(...y("By action", e.actions)), t.push(""), t.push(...y("By channel", e.channels)), t.push(""), t.push(...y("By live action", e.liveActions)), t.push(""), t.push(...V(e.timeline)), t.join(`
204
207
  `);
205
208
  }
206
209
  function W(e) {
207
210
  return e % 864e5 === 0 ? `${e / 864e5}d` : e % 36e5 === 0 ? `${e / 36e5}h` : e % 6e4 === 0 ? `${e / 6e4}m` : e % 1e3 === 0 ? `${e / 1e3}s` : `${e}ms`;
208
211
  }
209
- const b = `Usage: velojs telemetry [path] [--last <duration>]
212
+ const E = `Usage: velojs telemetry [path] [--last <duration>]
210
213
 
211
214
  Ranks the JSONL written by the native telemetry (count, errors, p50/p95,
212
- bytes) by route, action and channel, and prints the timeline.
215
+ bytes) by route, action, channel and live action, and prints the timeline.
213
216
 
214
217
  path JSONL file copied from production (or a directory of them).
215
218
  Without it, VELO_TELEMETRY_FILE from the environment or the
@@ -221,45 +224,45 @@ Examples:
221
224
  velojs telemetry
222
225
  velojs telemetry --last 60m
223
226
  velojs telemetry ./telemetry.prod.jsonl`;
224
- async function U(e, o = {}) {
225
- const n = o.cwd ?? process.cwd(), i = o.env ?? process.env, t = o.write ?? ((f) => console.log(f));
226
- let r = null, s = null, l;
227
+ async function q(e, r = {}) {
228
+ const n = r.cwd ?? process.cwd(), i = r.env ?? process.env, t = r.write ?? ((f) => console.log(f));
229
+ let o = null, s = null, l;
227
230
  for (let f = 0; f < e.length; f += 1) {
228
- const c = e[f];
229
- if (c === "--help" || c === "-h") {
230
- t(b);
231
+ const u = e[f];
232
+ if (u === "--help" || u === "-h") {
233
+ t(E);
231
234
  return;
232
235
  }
233
- if (c === "--last") {
234
- const m = e[f + 1];
235
- if (m === void 0)
236
+ if (u === "--last") {
237
+ const b = e[f + 1];
238
+ if (b === void 0)
236
239
  throw new Error("[velojs] velojs telemetry: --last requires a duration (e.g. --last 60m).");
237
- s = j(m), l = m, f += 1;
240
+ s = j(b), l = b, f += 1;
238
241
  continue;
239
242
  }
240
- if (c.startsWith("--last=")) {
241
- l = c.slice(7), s = j(l);
243
+ if (u.startsWith("--last=")) {
244
+ l = u.slice(7), s = j(l);
242
245
  continue;
243
246
  }
244
- if (c.startsWith("-"))
245
- throw new Error(`[velojs] velojs telemetry: unknown flag "${c}".
247
+ if (u.startsWith("-"))
248
+ throw new Error(`[velojs] velojs telemetry: unknown flag "${u}".
246
249
 
247
- ${b}`);
248
- if (r !== null)
249
- throw new Error(`[velojs] velojs telemetry: unexpected argument "${c}".
250
+ ${E}`);
251
+ if (o !== null)
252
+ throw new Error(`[velojs] velojs telemetry: unexpected argument "${u}".
250
253
 
251
- ${b}`);
252
- r = c;
254
+ ${E}`);
255
+ o = u;
253
256
  }
254
- const h = k(n), u = M(r, i, h, n), T = N(O(u), s, Date.now()), F = I(u, T);
255
- t(v(F, s, Date.now(), l));
257
+ const h = k(n), c = M(o, i, h, n), S = N(O(c), s, Date.now()), F = A(c, S);
258
+ t(J(F, s, Date.now(), l));
256
259
  }
257
260
  export {
258
- I as buildTelemetryReport,
259
- x as collectTelemetryFiles,
260
- v as formatTelemetryReport,
261
+ A as buildTelemetryReport,
262
+ L as collectTelemetryFiles,
263
+ J as formatTelemetryReport,
261
264
  j as parseDuration,
262
265
  O as readTelemetryEvents,
263
- U as runTelemetryCli,
266
+ q as runTelemetryCli,
264
267
  N as windowEvents
265
268
  };
@@ -2,8 +2,8 @@
2
2
  * Native telemetry — boundary traces, first-class JSONL (client-safe core).
3
3
  *
4
4
  * The framework owns a finite set of bottlenecks (page SSR, `?_data=1` refetch,
5
- * action, declarative endpoint, stream connect, socket setup, channel connect)
6
- * plus the emit chain that runs outside any request. This module turns each of
5
+ * action, declarative endpoint, stream connect, socket setup, channel connect,
6
+ * live action connect) plus the emit chain that runs outside any request. This module turns each of
7
7
  * them into one JSONL event with duration, status, response bytes, child spans
8
8
  * and — on long connections — one delivery line per frame delivered and a final
9
9
  * close line. The file is the production artifact; `velojs telemetry` ranks it.
@@ -22,7 +22,7 @@
22
22
  * injected at boot — the client bundle never reaches it.
23
23
  */
24
24
  /** Every line of the JSONL carries one of these types. */
25
- export type TelemetryEventType = "boot" | "page" | "data" | "action" | "endpoint" | "stream-connect" | "socket-connect" | "channel-connect" | "emit" | "delivery" | "close";
25
+ export type TelemetryEventType = "boot" | "page" | "data" | "action" | "endpoint" | "stream-connect" | "socket-connect" | "channel-connect" | "live-action-connect" | "emit" | "delivery" | "close";
26
26
  export type TelemetryStatus = "ok" | "error";
27
27
  /** One child span: name, start relative to the trace, own duration and status. */
28
28
  export interface TelemetrySpan {
@@ -21,4 +21,4 @@
21
21
  * snapshot — no sleep, no retry.
22
22
  */
23
23
  export { createTestApp } from "./createTestApp.js";
24
- export type { TestApp, TestResponse, TestSubscription, TestChannelSubscription, ChannelEvent, ChannelModuleRef, CreateTestAppOptions, RequestOptions, BodyRequestOptions, LoaderRequestOptions, SubscribeOptions, ChannelSubscribeOptions, NextOptions, MockContextOptions, Cookies, Headers, Query, Params, } from "./types.js";
24
+ export type { TestApp, TestResponse, TestSubscription, TestChannelSubscription, ChannelEvent, ChannelModuleRef, CreateTestAppOptions, RequestOptions, BodyRequestOptions, LoaderRequestOptions, SubscribeOptions, ChannelSubscribeOptions, LiveActionSubscribeOptions, NextOptions, MockContextOptions, Cookies, Headers, Query, Params, } from "./types.js";