@lunora/studio 0.0.0 → 1.0.0-alpha.2

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