@lunora/studio 0.0.0 → 1.0.0-alpha.10

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.
Files changed (80) hide show
  1. package/LICENSE.md +105 -0
  2. package/README.md +123 -9
  3. package/__assets__/package-og.svg +14 -0
  4. package/dist/index.d.ts +1403 -0
  5. package/dist/index.js +41 -0
  6. package/dist/mount.d.ts +21 -0
  7. package/dist/mount.js +26 -0
  8. package/dist/packem_shared/ADMIN_FUNCTIONS-DjXgvJb6.js +46 -0
  9. package/dist/packem_shared/ApiDocsPanel-yVSGHK3H.js +842 -0
  10. package/dist/packem_shared/ApiReferencePanel-1vubqG5s.js +229 -0
  11. package/dist/packem_shared/ApiTab-rwttyQ1-.js +251 -0
  12. package/dist/packem_shared/AuditPanel-D9Eh7j7E.js +200 -0
  13. package/dist/packem_shared/CommandPalette-BtgkBzZI.js +373 -0
  14. package/dist/packem_shared/ConfirmButton-C2eVi2Qv.js +59 -0
  15. package/dist/packem_shared/ConnectionBadge-B2DDM2Tn.js +111 -0
  16. package/dist/packem_shared/DEFAULT_AUTO_REFRESH_MS-Vxwaxx51.js +50 -0
  17. package/dist/packem_shared/DEFAULT_INSIGHT_THRESHOLDS-DjF0h-gA.js +89 -0
  18. package/dist/packem_shared/DEFAULT_LOCALE-CiX4W7Kr.js +27 -0
  19. package/dist/packem_shared/DataBrowser-CyckZRwj.js +4480 -0
  20. package/dist/packem_shared/DataFilters-FNquMaiu.js +249 -0
  21. package/dist/packem_shared/ErrorBoundary-BzAApI7J.js +66 -0
  22. package/dist/packem_shared/ExportImportPanel-BGJqFK9X.js +197 -0
  23. package/dist/packem_shared/FileBrowser-0pCpdKPE.js +2932 -0
  24. package/dist/packem_shared/FunctionRunner-7NH3KcTg.js +343 -0
  25. package/dist/packem_shared/FunctionStatsPanel-CXynSuqT.js +375 -0
  26. package/dist/packem_shared/GlobalDataBrowser-DAxzB-Br.js +289 -0
  27. package/dist/packem_shared/HealthPanel-CyLs7qZm.js +648 -0
  28. package/dist/packem_shared/HomePanel-BOkFRbuj.js +1334 -0
  29. package/dist/packem_shared/InsightsPanel-CvXMoZuv.js +461 -0
  30. package/dist/packem_shared/LogsPanel-BrUstOk6.js +827 -0
  31. package/dist/packem_shared/MailPanel-GnnrNqL6.js +440 -0
  32. package/dist/packem_shared/MetricsPanel-BN_uUU-_.js +1611 -0
  33. package/dist/packem_shared/MigrationsPanel-DaZuUHje.js +239 -0
  34. package/dist/packem_shared/OpenRpcReferencePanel-vbcYbv3V.js +191 -0
  35. package/dist/packem_shared/PitrPanel-LJGNP_IX.js +252 -0
  36. package/dist/packem_shared/STUDIO_ROOT_CLASS-D12gX2dV.js +3 -0
  37. package/dist/packem_shared/ScheduledJobs-UmVaMbJy.js +262 -0
  38. package/dist/packem_shared/SchemaViewer-Bam6ylvw.js +2512 -0
  39. package/dist/packem_shared/SecurityAdvisorPanel-DTmjUNqt.js +92 -0
  40. package/dist/packem_shared/SettingsPanel-C2wDK-8F.js +390 -0
  41. package/dist/packem_shared/ShardInput-gx_DZXXn.js +107 -0
  42. package/dist/packem_shared/SqlEditorPanel-D5wMIxcg.js +13 -0
  43. package/dist/packem_shared/Studio-DpgYe4qt.js +33 -0
  44. package/dist/packem_shared/StudioApp-C7aaizqc.js +657 -0
  45. package/dist/packem_shared/StudioI18nProvider-B-tDCZIi.js +48 -0
  46. package/dist/packem_shared/TableEditor-DzK3Z6rL.js +371 -0
  47. package/dist/packem_shared/advisor-view-CGcfMiPq.js +159 -0
  48. package/dist/packem_shared/aggregateMetrics-D4nUHEKU.js +108 -0
  49. package/dist/packem_shared/app.d-Nbhpihxv.d.ts +300 -0
  50. package/dist/packem_shared/badge-B2PKA1-5.js +49 -0
  51. package/dist/packem_shared/bar-chart-CzJAgqkp.js +3245 -0
  52. package/dist/packem_shared/button-BhsN2uZH.js +49 -0
  53. package/dist/packem_shared/card-DURq3ElK.js +175 -0
  54. package/dist/packem_shared/cf-links-2cktDedA.js +8 -0
  55. package/dist/packem_shared/checkbox-UNkzAxl-.js +63 -0
  56. package/dist/packem_shared/data-grid-B5CaV3wS.js +183 -0
  57. package/dist/packem_shared/dropdown-menu-WY4B_eJO.js +280 -0
  58. package/dist/packem_shared/empty-state-DY_oe0k6.js +98 -0
  59. package/dist/packem_shared/grid-features-CzUfqgsM.js +840 -0
  60. package/dist/packem_shared/input-XH4r1Pt1.js +53 -0
  61. package/dist/packem_shared/internal-rXJEAhXS.js +68 -0
  62. package/dist/packem_shared/label-D8ykjn5J.js +46 -0
  63. package/dist/packem_shared/live-status-FZ0psC1z.js +44 -0
  64. package/dist/packem_shared/reference-view-B4E49Lip.js +2180 -0
  65. package/dist/packem_shared/shard-history-DyebH1R5.js +38 -0
  66. package/dist/packem_shared/sparkline-10dG-_f0.js +93 -0
  67. package/dist/packem_shared/sql-editor-panel-Smfl_FDF.js +2562 -0
  68. package/dist/packem_shared/storage-tier-CL98eOvn.js +85 -0
  69. package/dist/packem_shared/studio-9Mq0PdMi.js +10831 -0
  70. package/dist/packem_shared/table-_RzNvy3R.js +246 -0
  71. package/dist/packem_shared/table-list-sidebar-Bg7uzbvC.js +832 -0
  72. package/dist/packem_shared/textarea-D3gaCU_-.js +46 -0
  73. package/dist/packem_shared/use-admin-query-4JQW2rr1.js +88 -0
  74. package/dist/packem_shared/useDebounced-Dxncpg6z.js +32 -0
  75. package/dist/packem_shared/utils-B05Dmz_H.js +8 -0
  76. package/dist/packem_shared/virtual-rect-CVMUskSm.js +10 -0
  77. package/dist/standalone/studio.js +378 -0
  78. package/dist/styles.css +2 -0
  79. package/package.json +78 -17
  80. package/src/theme.css +59 -0
@@ -0,0 +1,827 @@
1
+ import { c } from 'react/compiler-runtime';
2
+ import { useVirtualizer } from '@tanstack/react-virtual';
3
+ import { useState, useEffect, useMemo, useRef } from 'react';
4
+ import { L as LiveError } from './live-status-FZ0psC1z.js';
5
+ import { ShardInput } from './ShardInput-gx_DZXXn.js';
6
+ import { B as Badge } from './badge-B2PKA1-5.js';
7
+ import { E as EmptyState } from './empty-state-DY_oe0k6.js';
8
+ import { I as Input } from './input-XH4r1Pt1.js';
9
+ import { u as useAdminQuery } from './use-admin-query-4JQW2rr1.js';
10
+ import useDebounced from './useDebounced-Dxncpg6z.js';
11
+ import { useT } from './DEFAULT_LOCALE-CiX4W7Kr.js';
12
+ import { ADMIN_FUNCTIONS } from './ADMIN_FUNCTIONS-DjXgvJb6.js';
13
+ import { C as CLOUDFLARE_OBSERVABILITY_URL } from './cf-links-2cktDedA.js';
14
+ import { r as recordShard } from './shard-history-DyebH1R5.js';
15
+ import { c as cn } from './utils-B05Dmz_H.js';
16
+ import { f as flooredRectObserver } from './virtual-rect-CVMUskSm.js';
17
+ import { jsxDEV } from 'react/jsx-dev-runtime';
18
+
19
+ const SCROLL_HEIGHT = 400;
20
+ const ROW_HEIGHT = 36;
21
+ const SCROLL_STYLE = {
22
+ height: SCROLL_HEIGHT,
23
+ overflow: "auto"
24
+ };
25
+ const ROW_BASE_STYLE = {
26
+ left: 0,
27
+ position: "absolute",
28
+ top: 0,
29
+ width: "100%"
30
+ };
31
+ const LEVEL_VARIANT = {
32
+ debug: "secondary",
33
+ error: "destructive",
34
+ info: "outline",
35
+ warn: "secondary"
36
+ };
37
+ const OUTCOME_VARIANT = {
38
+ error: "destructive",
39
+ ok: "secondary"
40
+ };
41
+ const LogRow = (t0) => {
42
+ const $ = c(21);
43
+ const {
44
+ entry,
45
+ index,
46
+ measureRef,
47
+ start
48
+ } = t0;
49
+ const t1 = `translateY(${String(start)}px)`;
50
+ let t2;
51
+ if ($[0] !== t1) {
52
+ t2 = {
53
+ ...ROW_BASE_STYLE,
54
+ transform: t1
55
+ };
56
+ $[0] = t1;
57
+ $[1] = t2;
58
+ } else {
59
+ t2 = $[1];
60
+ }
61
+ const style = t2;
62
+ let t3;
63
+ if ($[2] !== entry.timestamp) {
64
+ t3 = new Date(entry.timestamp).toLocaleString();
65
+ $[2] = entry.timestamp;
66
+ $[3] = t3;
67
+ } else {
68
+ t3 = $[3];
69
+ }
70
+ let t4;
71
+ if ($[4] !== t3) {
72
+ t4 = /* @__PURE__ */ jsxDEV("span", {
73
+ className: "w-44 shrink-0 tabular-nums text-muted-foreground",
74
+ role: "gridcell",
75
+ children: t3
76
+ }, void 0, false);
77
+ $[4] = t3;
78
+ $[5] = t4;
79
+ } else {
80
+ t4 = $[5];
81
+ }
82
+ const t5 = LEVEL_VARIANT[entry.level];
83
+ let t6;
84
+ if ($[6] !== entry.level || $[7] !== t5) {
85
+ t6 = /* @__PURE__ */ jsxDEV("span", {
86
+ className: "w-20 shrink-0",
87
+ role: "gridcell",
88
+ children: /* @__PURE__ */ jsxDEV(Badge, {
89
+ variant: t5,
90
+ children: entry.level
91
+ }, void 0, false)
92
+ }, void 0, false);
93
+ $[6] = entry.level;
94
+ $[7] = t5;
95
+ $[8] = t6;
96
+ } else {
97
+ t6 = $[8];
98
+ }
99
+ const t7 = entry.functionPath ?? "—";
100
+ let t8;
101
+ if ($[9] !== t7) {
102
+ t8 = /* @__PURE__ */ jsxDEV("span", {
103
+ className: "w-48 shrink-0 truncate text-muted-foreground",
104
+ role: "gridcell",
105
+ children: t7
106
+ }, void 0, false);
107
+ $[9] = t7;
108
+ $[10] = t8;
109
+ } else {
110
+ t8 = $[10];
111
+ }
112
+ let t9;
113
+ if ($[11] !== entry.message) {
114
+ t9 = /* @__PURE__ */ jsxDEV("span", {
115
+ className: "flex-1 truncate",
116
+ role: "gridcell",
117
+ children: entry.message
118
+ }, void 0, false);
119
+ $[11] = entry.message;
120
+ $[12] = t9;
121
+ } else {
122
+ t9 = $[12];
123
+ }
124
+ let t10;
125
+ if ($[13] !== index || $[14] !== measureRef || $[15] !== style || $[16] !== t4 || $[17] !== t6 || $[18] !== t8 || $[19] !== t9) {
126
+ t10 = /* @__PURE__ */ jsxDEV("div", {
127
+ className: "flex items-center border-b border-border px-3 py-1.5 font-mono text-xs hover:bg-muted/50",
128
+ "data-index": index,
129
+ "data-testid": "lg-row",
130
+ ref: measureRef,
131
+ role: "row",
132
+ style,
133
+ children: [t4, t6, t8, t9]
134
+ }, void 0, true);
135
+ $[13] = index;
136
+ $[14] = measureRef;
137
+ $[15] = style;
138
+ $[16] = t4;
139
+ $[17] = t6;
140
+ $[18] = t8;
141
+ $[19] = t9;
142
+ $[20] = t10;
143
+ } else {
144
+ t10 = $[20];
145
+ }
146
+ return t10;
147
+ };
148
+ const tablesLabel = (tables) => tables.length === 0 ? "—" : tables.join(", ");
149
+ const RequestRow = (t0) => {
150
+ const $ = c(37);
151
+ const {
152
+ entry,
153
+ index,
154
+ measureRef,
155
+ start
156
+ } = t0;
157
+ const t1 = `translateY(${String(start)}px)`;
158
+ let t2;
159
+ if ($[0] !== t1) {
160
+ t2 = {
161
+ ...ROW_BASE_STYLE,
162
+ transform: t1
163
+ };
164
+ $[0] = t1;
165
+ $[1] = t2;
166
+ } else {
167
+ t2 = $[1];
168
+ }
169
+ const style = t2;
170
+ let t3;
171
+ if ($[2] !== entry.ts) {
172
+ t3 = new Date(entry.ts).toLocaleString();
173
+ $[2] = entry.ts;
174
+ $[3] = t3;
175
+ } else {
176
+ t3 = $[3];
177
+ }
178
+ let t4;
179
+ if ($[4] !== t3) {
180
+ t4 = /* @__PURE__ */ jsxDEV("span", {
181
+ className: "w-40 shrink-0 tabular-nums text-muted-foreground",
182
+ role: "gridcell",
183
+ children: t3
184
+ }, void 0, false);
185
+ $[4] = t3;
186
+ $[5] = t4;
187
+ } else {
188
+ t4 = $[5];
189
+ }
190
+ const t5 = OUTCOME_VARIANT[entry.outcome];
191
+ let t6;
192
+ if ($[6] !== entry.outcome || $[7] !== t5) {
193
+ t6 = /* @__PURE__ */ jsxDEV("span", {
194
+ className: "w-16 shrink-0",
195
+ role: "gridcell",
196
+ children: /* @__PURE__ */ jsxDEV(Badge, {
197
+ variant: t5,
198
+ children: entry.outcome
199
+ }, void 0, false)
200
+ }, void 0, false);
201
+ $[6] = entry.outcome;
202
+ $[7] = t5;
203
+ $[8] = t6;
204
+ } else {
205
+ t6 = $[8];
206
+ }
207
+ let t7;
208
+ if ($[9] !== entry.functionPath) {
209
+ t7 = /* @__PURE__ */ jsxDEV("span", {
210
+ className: "w-44 shrink-0 truncate",
211
+ role: "gridcell",
212
+ title: entry.functionPath,
213
+ children: entry.functionPath
214
+ }, void 0, false);
215
+ $[9] = entry.functionPath;
216
+ $[10] = t7;
217
+ } else {
218
+ t7 = $[10];
219
+ }
220
+ const t8 = entry.shardKey ?? "—";
221
+ let t9;
222
+ if ($[11] !== t8) {
223
+ t9 = /* @__PURE__ */ jsxDEV("span", {
224
+ className: "w-24 shrink-0 truncate text-muted-foreground",
225
+ role: "gridcell",
226
+ children: t8
227
+ }, void 0, false);
228
+ $[11] = t8;
229
+ $[12] = t9;
230
+ } else {
231
+ t9 = $[12];
232
+ }
233
+ const t10 = entry.userId ?? "—";
234
+ let t11;
235
+ if ($[13] !== t10) {
236
+ t11 = /* @__PURE__ */ jsxDEV("span", {
237
+ className: "w-24 shrink-0 truncate text-muted-foreground",
238
+ role: "gridcell",
239
+ children: t10
240
+ }, void 0, false);
241
+ $[13] = t10;
242
+ $[14] = t11;
243
+ } else {
244
+ t11 = $[14];
245
+ }
246
+ let t12;
247
+ if ($[15] !== entry.durationMs) {
248
+ t12 = Math.round(entry.durationMs);
249
+ $[15] = entry.durationMs;
250
+ $[16] = t12;
251
+ } else {
252
+ t12 = $[16];
253
+ }
254
+ const t13 = `${String(t12)}ms`;
255
+ let t14;
256
+ if ($[17] !== t13) {
257
+ t14 = /* @__PURE__ */ jsxDEV("span", {
258
+ className: "w-16 shrink-0 tabular-nums text-muted-foreground",
259
+ role: "gridcell",
260
+ children: t13
261
+ }, void 0, false);
262
+ $[17] = t13;
263
+ $[18] = t14;
264
+ } else {
265
+ t14 = $[18];
266
+ }
267
+ let t15;
268
+ if ($[19] !== entry.errorMessage || $[20] !== entry.outcome || $[21] !== entry.tablesRead || $[22] !== entry.tablesWritten) {
269
+ t15 = entry.outcome === "error" && entry.errorMessage !== void 0 ? entry.errorMessage : `r:${tablesLabel(entry.tablesRead)} · w:${tablesLabel(entry.tablesWritten)}`;
270
+ $[19] = entry.errorMessage;
271
+ $[20] = entry.outcome;
272
+ $[21] = entry.tablesRead;
273
+ $[22] = entry.tablesWritten;
274
+ $[23] = t15;
275
+ } else {
276
+ t15 = $[23];
277
+ }
278
+ let t16;
279
+ if ($[24] !== t15) {
280
+ t16 = /* @__PURE__ */ jsxDEV("span", {
281
+ className: "flex-1 truncate text-muted-foreground",
282
+ role: "gridcell",
283
+ children: t15
284
+ }, void 0, false);
285
+ $[24] = t15;
286
+ $[25] = t16;
287
+ } else {
288
+ t16 = $[25];
289
+ }
290
+ let t17;
291
+ if ($[26] !== index || $[27] !== measureRef || $[28] !== style || $[29] !== t11 || $[30] !== t14 || $[31] !== t16 || $[32] !== t4 || $[33] !== t6 || $[34] !== t7 || $[35] !== t9) {
292
+ t17 = /* @__PURE__ */ jsxDEV("div", {
293
+ className: "flex items-center border-b border-border px-3 py-1.5 font-mono text-xs hover:bg-muted/50",
294
+ "data-index": index,
295
+ "data-testid": "lg-req-row",
296
+ ref: measureRef,
297
+ role: "row",
298
+ style,
299
+ children: [t4, t6, t7, t9, t11, t14, t16]
300
+ }, void 0, true);
301
+ $[26] = index;
302
+ $[27] = measureRef;
303
+ $[28] = style;
304
+ $[29] = t11;
305
+ $[30] = t14;
306
+ $[31] = t16;
307
+ $[32] = t4;
308
+ $[33] = t6;
309
+ $[34] = t7;
310
+ $[35] = t9;
311
+ $[36] = t17;
312
+ } else {
313
+ t17 = $[36];
314
+ }
315
+ return t17;
316
+ };
317
+ const entriesOf = (result) => {
318
+ const {
319
+ entries
320
+ } = result ?? {};
321
+ return Array.isArray(entries) ? entries : [];
322
+ };
323
+ const buildRequestQuery = (filters) => {
324
+ const query = {};
325
+ if (filters.functionPathPrefix.trim() !== "") {
326
+ query.functionPathPrefix = filters.functionPathPrefix.trim();
327
+ }
328
+ if (filters.userId.trim() !== "") {
329
+ query.userId = filters.userId.trim();
330
+ }
331
+ if (filters.tableTouched.trim() !== "") {
332
+ query.tableTouched = filters.tableTouched.trim();
333
+ }
334
+ if (filters.outcome === "ok" || filters.outcome === "error") {
335
+ query.outcome = filters.outcome;
336
+ }
337
+ return query;
338
+ };
339
+ const LOG_LEVELS = ["debug", "info", "warn", "error"];
340
+ const TIME_RANGE_MS = {
341
+ "5m": 5 * 60 * 1e3,
342
+ "15m": 15 * 60 * 1e3,
343
+ "1h": 60 * 60 * 1e3
344
+ };
345
+ const filterLogs = (entries, criteria) => {
346
+ const needle = criteria.search.trim().toLowerCase();
347
+ const pathNeedle = criteria.path.trim().toLowerCase();
348
+ const floor = criteria.range === "all" ? Number.NEGATIVE_INFINITY : criteria.now - TIME_RANGE_MS[criteria.range];
349
+ return entries.filter((entry) => {
350
+ if (criteria.levels.size > 0 && !criteria.levels.has(entry.level)) {
351
+ return false;
352
+ }
353
+ if (entry.timestamp < floor) {
354
+ return false;
355
+ }
356
+ if (pathNeedle !== "" && !(entry.functionPath ?? "").toLowerCase().includes(pathNeedle)) {
357
+ return false;
358
+ }
359
+ return needle === "" || entry.message.toLowerCase().includes(needle);
360
+ });
361
+ };
362
+ const NO_PATH_KEY = "—";
363
+ const summarizeLogs = (entries) => {
364
+ const levelCounts = /* @__PURE__ */ new Map();
365
+ const pathCounts = /* @__PURE__ */ new Map();
366
+ for (const entry of entries) {
367
+ levelCounts.set(entry.level, (levelCounts.get(entry.level) ?? 0) + 1);
368
+ const pathKey = entry.functionPath ?? NO_PATH_KEY;
369
+ pathCounts.set(pathKey, (pathCounts.get(pathKey) ?? 0) + 1);
370
+ }
371
+ const byLevel = LOG_LEVELS.filter((level) => levelCounts.has(level)).map((level) => {
372
+ return {
373
+ count: levelCounts.get(level) ?? 0,
374
+ key: level
375
+ };
376
+ });
377
+ const byPath = [...pathCounts.entries()].map(([key, count]) => {
378
+ return {
379
+ count,
380
+ key
381
+ };
382
+ }).toSorted((a, b) => b.count === a.count ? a.key.localeCompare(b.key) : b.count - a.count);
383
+ return {
384
+ byLevel,
385
+ byPath,
386
+ total: entries.length
387
+ };
388
+ };
389
+ const LevelToggle = (t0) => {
390
+ const $ = c(11);
391
+ const {
392
+ level,
393
+ onToggle,
394
+ selected
395
+ } = t0;
396
+ let t1;
397
+ if ($[0] !== level || $[1] !== onToggle) {
398
+ t1 = () => {
399
+ onToggle(level);
400
+ };
401
+ $[0] = level;
402
+ $[1] = onToggle;
403
+ $[2] = t1;
404
+ } else {
405
+ t1 = $[2];
406
+ }
407
+ const onClick = t1;
408
+ const t2 = selected ? "border-border bg-muted font-medium" : "border-input text-muted-foreground";
409
+ let t3;
410
+ if ($[3] !== t2) {
411
+ t3 = cn("rounded-md border px-2 py-1 text-xs", t2);
412
+ $[3] = t2;
413
+ $[4] = t3;
414
+ } else {
415
+ t3 = $[4];
416
+ }
417
+ const t4 = `logs-level-${level}`;
418
+ let t5;
419
+ if ($[5] !== level || $[6] !== onClick || $[7] !== selected || $[8] !== t3 || $[9] !== t4) {
420
+ t5 = /* @__PURE__ */ jsxDEV("button", {
421
+ "aria-pressed": selected,
422
+ className: t3,
423
+ "data-testid": t4,
424
+ onClick,
425
+ type: "button",
426
+ children: level
427
+ }, void 0, false);
428
+ $[5] = level;
429
+ $[6] = onClick;
430
+ $[7] = selected;
431
+ $[8] = t3;
432
+ $[9] = t4;
433
+ $[10] = t5;
434
+ } else {
435
+ t5 = $[10];
436
+ }
437
+ return t5;
438
+ };
439
+ const SummaryBucketRow = (t0) => {
440
+ const $ = c(7);
441
+ const {
442
+ bucket
443
+ } = t0;
444
+ let t1;
445
+ if ($[0] !== bucket.key) {
446
+ t1 = /* @__PURE__ */ jsxDEV("span", {
447
+ className: "truncate text-muted-foreground",
448
+ role: "gridcell",
449
+ children: bucket.key
450
+ }, void 0, false);
451
+ $[0] = bucket.key;
452
+ $[1] = t1;
453
+ } else {
454
+ t1 = $[1];
455
+ }
456
+ let t2;
457
+ if ($[2] !== bucket.count) {
458
+ t2 = /* @__PURE__ */ jsxDEV("span", {
459
+ className: "shrink-0 tabular-nums",
460
+ role: "gridcell",
461
+ children: bucket.count
462
+ }, void 0, false);
463
+ $[2] = bucket.count;
464
+ $[3] = t2;
465
+ } else {
466
+ t2 = $[3];
467
+ }
468
+ let t3;
469
+ if ($[4] !== t1 || $[5] !== t2) {
470
+ t3 = /* @__PURE__ */ jsxDEV("div", {
471
+ className: "flex items-center justify-between gap-4 px-3 py-1 font-mono text-xs",
472
+ "data-testid": "logs-summary-row",
473
+ role: "row",
474
+ children: [t1, t2]
475
+ }, void 0, true);
476
+ $[4] = t1;
477
+ $[5] = t2;
478
+ $[6] = t3;
479
+ } else {
480
+ t3 = $[6];
481
+ }
482
+ return t3;
483
+ };
484
+ const LogsPanel = ({
485
+ initialShardKey
486
+ }) => {
487
+ const t = useT();
488
+ const [view, setView] = useState("requests");
489
+ const [shardKey, setShardKey] = useState(initialShardKey ?? "");
490
+ const [search, setSearch] = useState("");
491
+ const [levelFilter, setLevelFilter] = useState(() => /* @__PURE__ */ new Set());
492
+ const [pathFilter, setPathFilter] = useState("");
493
+ const [timeRange, setTimeRange] = useState("all");
494
+ const [showSummary, setShowSummary] = useState(false);
495
+ const [pathPrefix, setPathPrefix] = useState("");
496
+ const [userIdFilter, setUserIdFilter] = useState("");
497
+ const [tableFilter, setTableFilter] = useState("");
498
+ const [outcomeFilter, setOutcomeFilter] = useState("all");
499
+ const debouncedShard = useDebounced(shardKey.trim(), 400);
500
+ const requestQuery = buildRequestQuery({
501
+ functionPathPrefix: pathPrefix,
502
+ outcome: outcomeFilter,
503
+ tableTouched: tableFilter,
504
+ userId: userIdFilter
505
+ });
506
+ const requestsQuery = useAdminQuery(ADMIN_FUNCTIONS.getRequestLog, requestQuery, {
507
+ enabled: view === "requests",
508
+ live: true,
509
+ shardKey: debouncedShard
510
+ });
511
+ const errorsQuery = useAdminQuery(ADMIN_FUNCTIONS.getLogs, {}, {
512
+ enabled: view === "errors",
513
+ live: true,
514
+ shardKey: debouncedShard
515
+ });
516
+ const activeQuery = view === "requests" ? requestsQuery : errorsQuery;
517
+ const {
518
+ error,
519
+ isLoading: activeLoading,
520
+ liveError
521
+ } = activeQuery;
522
+ const entries = entriesOf(errorsQuery.data);
523
+ const requests = entriesOf(requestsQuery.data);
524
+ useEffect(() => {
525
+ if (activeQuery.data !== void 0) {
526
+ recordShard(debouncedShard);
527
+ }
528
+ }, [activeQuery.data, debouncedShard]);
529
+ const filtered = filterLogs(entries, {
530
+ levels: levelFilter,
531
+ now: Date.now(),
532
+ path: pathFilter,
533
+ range: timeRange,
534
+ search
535
+ });
536
+ const summary = summarizeLogs(filtered);
537
+ const activeCount = view === "requests" ? requests.length : filtered.length;
538
+ const summaryVisible = view === "errors" && showSummary;
539
+ const readout = useMemo(() => {
540
+ if (error !== null) {
541
+ return "error";
542
+ }
543
+ if (activeCount === 0) {
544
+ if (activeLoading) {
545
+ return summaryVisible ? "summary" : "list";
546
+ }
547
+ return "empty";
548
+ }
549
+ return summaryVisible ? "summary" : "list";
550
+ }, [activeCount, activeLoading, error, summaryVisible]);
551
+ const scrollRef = useRef(null);
552
+ const virtualizer = useVirtualizer({
553
+ count: activeCount,
554
+ estimateSize: () => ROW_HEIGHT,
555
+ getScrollElement: () => scrollRef.current,
556
+ initialRect: {
557
+ height: SCROLL_HEIGHT,
558
+ width: 800
559
+ },
560
+ observeElementRect: (instance, callback) => flooredRectObserver(instance, callback, SCROLL_HEIGHT),
561
+ overscan: 8
562
+ });
563
+ const virtualRows = virtualizer.getVirtualItems();
564
+ const totalSize = virtualizer.getTotalSize();
565
+ const gridStyle = {
566
+ height: totalSize,
567
+ position: "relative",
568
+ width: "100%"
569
+ };
570
+ const onSearchChange = (event) => {
571
+ setSearch(event.target.value);
572
+ };
573
+ const toggleLevel = (level) => {
574
+ setLevelFilter((previous) => {
575
+ const next = new Set(previous);
576
+ if (next.has(level)) {
577
+ next.delete(level);
578
+ } else {
579
+ next.add(level);
580
+ }
581
+ return next;
582
+ });
583
+ };
584
+ const onLogPathChange = (event_0) => {
585
+ setPathFilter(event_0.target.value);
586
+ };
587
+ const onTimeRangeChange = (event_1) => {
588
+ setTimeRange(event_1.target.value);
589
+ };
590
+ const toggleSummary = () => {
591
+ setShowSummary((previous_0) => !previous_0);
592
+ };
593
+ const onPathChange = (event_2) => {
594
+ setPathPrefix(event_2.target.value);
595
+ };
596
+ const onUserChange = (event_3) => {
597
+ setUserIdFilter(event_3.target.value);
598
+ };
599
+ const onTableChange = (event_4) => {
600
+ setTableFilter(event_4.target.value);
601
+ };
602
+ const onOutcomeChange = (event_5) => {
603
+ setOutcomeFilter(event_5.target.value);
604
+ };
605
+ const showRequests = () => {
606
+ setView("requests");
607
+ setShowSummary(false);
608
+ };
609
+ const showErrors = () => {
610
+ setView("errors");
611
+ setShowSummary(false);
612
+ };
613
+ return /* @__PURE__ */ jsxDEV("div", {
614
+ className: "flex flex-col gap-4",
615
+ "data-testid": "lunora-logs",
616
+ children: [/* @__PURE__ */ jsxDEV("div", {
617
+ className: "flex flex-wrap items-center gap-2",
618
+ children: [/* @__PURE__ */ jsxDEV("div", {
619
+ className: "inline-flex overflow-hidden rounded-lg border border-border",
620
+ role: "tablist",
621
+ children: [/* @__PURE__ */ jsxDEV("button", {
622
+ "aria-selected": view === "requests",
623
+ className: cn("px-3 py-1 text-sm", view === "requests" ? "bg-muted font-medium" : "text-muted-foreground"),
624
+ "data-testid": "lg-view-requests",
625
+ onClick: showRequests,
626
+ role: "tab",
627
+ type: "button",
628
+ children: t("Requests")
629
+ }, void 0, false), /* @__PURE__ */ jsxDEV("button", {
630
+ "aria-selected": view === "errors",
631
+ className: cn("px-3 py-1 text-sm", view === "errors" ? "bg-muted font-medium" : "text-muted-foreground"),
632
+ "data-testid": "lg-view-errors",
633
+ onClick: showErrors,
634
+ role: "tab",
635
+ type: "button",
636
+ children: t("Errors")
637
+ }, void 0, false)]
638
+ }, void 0, true), /* @__PURE__ */ jsxDEV(ShardInput, {
639
+ onChange: setShardKey,
640
+ testId: "lg-shard-input",
641
+ value: shardKey
642
+ }, void 0, false), /* @__PURE__ */ jsxDEV(LiveError, {
643
+ message: liveError,
644
+ prefix: "lg"
645
+ }, void 0, false), /* @__PURE__ */ jsxDEV("a", {
646
+ className: "text-sm text-primary underline-offset-4 hover:underline",
647
+ "data-testid": "lg-cf-link",
648
+ href: CLOUDFLARE_OBSERVABILITY_URL,
649
+ rel: "noreferrer",
650
+ target: "_blank",
651
+ children: t("Open in Cloudflare")
652
+ }, void 0, false)]
653
+ }, void 0, true), view === "errors" && /* @__PURE__ */ jsxDEV("div", {
654
+ className: "flex flex-wrap items-center gap-2",
655
+ children: [/* @__PURE__ */ jsxDEV(Input, {
656
+ "aria-label": t("Search messages"),
657
+ className: "h-8 w-48",
658
+ "data-testid": "lg-search",
659
+ onChange: onSearchChange,
660
+ placeholder: t("search message"),
661
+ value: search
662
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
663
+ "aria-label": t("Function path"),
664
+ className: "h-8 w-40",
665
+ "data-testid": "logs-path-filter",
666
+ onChange: onLogPathChange,
667
+ placeholder: t("filter path"),
668
+ value: pathFilter
669
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
670
+ "aria-label": t("Level filter"),
671
+ className: "inline-flex items-center gap-1",
672
+ "data-testid": "logs-level-filter",
673
+ role: "group",
674
+ children: LOG_LEVELS.map((level_0) => /* @__PURE__ */ jsxDEV(LevelToggle, {
675
+ level: level_0,
676
+ onToggle: toggleLevel,
677
+ selected: levelFilter.has(level_0)
678
+ }, level_0, false))
679
+ }, void 0, false), /* @__PURE__ */ jsxDEV("select", {
680
+ "aria-label": t("Time range"),
681
+ className: "h-8 rounded-md border border-input bg-transparent px-2 text-sm shadow-xs focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none",
682
+ "data-testid": "logs-time-range",
683
+ onChange: onTimeRangeChange,
684
+ value: timeRange,
685
+ children: [/* @__PURE__ */ jsxDEV("option", {
686
+ value: "all",
687
+ children: t("All time")
688
+ }, void 0, false), /* @__PURE__ */ jsxDEV("option", {
689
+ value: "5m",
690
+ children: t("Last 5m")
691
+ }, void 0, false), /* @__PURE__ */ jsxDEV("option", {
692
+ value: "15m",
693
+ children: t("Last 15m")
694
+ }, void 0, false), /* @__PURE__ */ jsxDEV("option", {
695
+ value: "1h",
696
+ children: t("Last hour")
697
+ }, void 0, false)]
698
+ }, void 0, true), /* @__PURE__ */ jsxDEV("button", {
699
+ "aria-pressed": showSummary,
700
+ className: cn("h-8 rounded-md border px-3 text-sm", showSummary ? "border-border bg-muted font-medium" : "border-input text-muted-foreground"),
701
+ "data-testid": "logs-summary-toggle",
702
+ onClick: toggleSummary,
703
+ type: "button",
704
+ children: showSummary ? t("List") : t("Summary")
705
+ }, void 0, false)]
706
+ }, void 0, true), view === "requests" && /* @__PURE__ */ jsxDEV("div", {
707
+ className: "flex flex-wrap items-center gap-2",
708
+ children: [/* @__PURE__ */ jsxDEV(Input, {
709
+ "aria-label": t("Function path"),
710
+ className: "h-8 w-44",
711
+ "data-testid": "lg-req-path",
712
+ onChange: onPathChange,
713
+ placeholder: t("file:function"),
714
+ value: pathPrefix
715
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
716
+ "aria-label": t("User id"),
717
+ className: "h-8 w-32",
718
+ "data-testid": "lg-req-user",
719
+ onChange: onUserChange,
720
+ placeholder: t("userId"),
721
+ value: userIdFilter
722
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
723
+ "aria-label": t("Table touched"),
724
+ className: "h-8 w-32",
725
+ "data-testid": "lg-req-table",
726
+ onChange: onTableChange,
727
+ placeholder: t("table"),
728
+ value: tableFilter
729
+ }, void 0, false), /* @__PURE__ */ jsxDEV("select", {
730
+ "aria-label": t("Outcome filter"),
731
+ className: "h-8 rounded-md border border-input bg-transparent px-2 text-sm shadow-xs focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none",
732
+ "data-testid": "lg-req-outcome",
733
+ onChange: onOutcomeChange,
734
+ value: outcomeFilter,
735
+ children: [/* @__PURE__ */ jsxDEV("option", {
736
+ value: "all",
737
+ children: t("all")
738
+ }, void 0, false), /* @__PURE__ */ jsxDEV("option", {
739
+ value: "ok",
740
+ children: t("ok")
741
+ }, void 0, false), /* @__PURE__ */ jsxDEV("option", {
742
+ value: "error",
743
+ children: t("error")
744
+ }, void 0, false)]
745
+ }, void 0, true)]
746
+ }, void 0, true), readout === "error" && /* @__PURE__ */ jsxDEV("p", {
747
+ className: "text-sm text-destructive",
748
+ "data-testid": "lg-error",
749
+ role: "alert",
750
+ children: error
751
+ }, void 0, false), readout === "empty" && /* @__PURE__ */ jsxDEV(EmptyState, {
752
+ description: t("Function and request logs for this shard show up here as your app handles traffic."),
753
+ icon: /* @__PURE__ */ jsxDEV("svg", {
754
+ "aria-hidden": "true",
755
+ fill: "none",
756
+ stroke: "currentColor",
757
+ strokeLinecap: "round",
758
+ strokeLinejoin: "round",
759
+ strokeWidth: 1.6,
760
+ viewBox: "0 0 24 24",
761
+ children: /* @__PURE__ */ jsxDEV("path", {
762
+ d: "M4 5h16M4 12h16M4 19h10"
763
+ }, void 0, false)
764
+ }, void 0, false),
765
+ testId: "lg-empty",
766
+ title: t("No logs.")
767
+ }, void 0, false), readout === "summary" && /* @__PURE__ */ jsxDEV("div", {
768
+ className: "flex flex-col gap-4 rounded-xl border border-border bg-card p-4 shadow-xs",
769
+ "data-testid": "logs-summary",
770
+ children: [/* @__PURE__ */ jsxDEV("p", {
771
+ className: "text-xs text-muted-foreground",
772
+ "data-testid": "logs-summary-total",
773
+ children: t("{count} entries", {
774
+ count: summary.total
775
+ })
776
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
777
+ children: [/* @__PURE__ */ jsxDEV("h4", {
778
+ className: "mb-1 font-mono text-[11px] tracking-wide text-muted-foreground uppercase",
779
+ children: t("By level")
780
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
781
+ className: "overflow-hidden rounded-lg border border-border",
782
+ "data-testid": "logs-summary-levels",
783
+ role: "grid",
784
+ children: summary.byLevel.map((bucket) => /* @__PURE__ */ jsxDEV(SummaryBucketRow, {
785
+ bucket
786
+ }, bucket.key, false))
787
+ }, void 0, false)]
788
+ }, void 0, true), /* @__PURE__ */ jsxDEV("div", {
789
+ children: [/* @__PURE__ */ jsxDEV("h4", {
790
+ className: "mb-1 font-mono text-[11px] tracking-wide text-muted-foreground uppercase",
791
+ children: t("By function")
792
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
793
+ className: "overflow-hidden rounded-lg border border-border",
794
+ "data-testid": "logs-summary-paths",
795
+ role: "grid",
796
+ children: summary.byPath.map((bucket_0) => /* @__PURE__ */ jsxDEV(SummaryBucketRow, {
797
+ bucket: bucket_0
798
+ }, bucket_0.key, false))
799
+ }, void 0, false)]
800
+ }, void 0, true)]
801
+ }, void 0, true), readout === "list" && /* @__PURE__ */ jsxDEV("div", {
802
+ className: "rounded-xl border border-border shadow-xs",
803
+ "data-testid": "lg-scroll",
804
+ ref: scrollRef,
805
+ style: SCROLL_STYLE,
806
+ children: /* @__PURE__ */ jsxDEV("div", {
807
+ "aria-label": t("Recent logs"),
808
+ "data-testid": "lg-table",
809
+ role: "grid",
810
+ style: gridStyle,
811
+ children: virtualRows.map((virtualRow) => view === "requests" ? /* @__PURE__ */ jsxDEV(RequestRow, {
812
+ entry: requests[virtualRow.index],
813
+ index: virtualRow.index,
814
+ measureRef: virtualizer.measureElement,
815
+ start: virtualRow.start
816
+ }, virtualRow.key, false) : /* @__PURE__ */ jsxDEV(LogRow, {
817
+ entry: filtered[virtualRow.index],
818
+ index: virtualRow.index,
819
+ measureRef: virtualizer.measureElement,
820
+ start: virtualRow.start
821
+ }, virtualRow.key, false))
822
+ }, void 0, false)
823
+ }, void 0, false)]
824
+ }, void 0, true);
825
+ };
826
+
827
+ export { LogsPanel, filterLogs, summarizeLogs };