@lunora/studio 1.0.0-alpha.2 → 1.0.0-alpha.21
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/__assets__/package-og.svg +1 -1
- package/dist/index.d.ts +23 -17
- package/dist/index.js +34 -34
- package/dist/mount.d.ts +1 -1
- package/dist/mount.js +1 -1
- package/dist/packem_shared/{ADMIN_FUNCTION_PREFIX-DmBqMZ-z.js → ADMIN_FUNCTIONS-CdaxLGkE.js} +3 -0
- package/dist/packem_shared/{ApiDocsPanel-DpRjJhG5.js → ApiDocsPanel-BgrN4kml.js} +3 -3
- package/dist/packem_shared/{ApiReferencePanel-DMIUp-kK.js → ApiReferencePanel-1vubqG5s.js} +2 -2
- package/dist/packem_shared/{ApiTab-DURGU15e.js → ApiTab-CHkX49o_.js} +4 -4
- package/dist/packem_shared/{AuditPanel-BC59Nhst.js → AuditPanel-CP9HM9nq.js} +19 -31
- package/dist/packem_shared/{CommandPalette-Dx_CoB9i.js → CommandPalette-BtgkBzZI.js} +2 -2
- package/dist/packem_shared/{ConfirmButton-WQVUoGFb.js → ConfirmButton-C2eVi2Qv.js} +1 -1
- package/dist/packem_shared/{ConnectionBadge-Bxagrip8.js → ConnectionBadge-B2DDM2Tn.js} +1 -1
- package/dist/packem_shared/{createStudioI18n-CgvlmDkN.js → DEFAULT_LOCALE-CiX4W7Kr.js} +1 -1
- package/dist/packem_shared/{DataBrowser-Coz6jJE6.js → DataBrowser-Bb1gTQ6c.js} +182 -249
- package/dist/packem_shared/{ExportImportPanel-WO34fJxy.js → ExportImportPanel-Dj87tRa5.js} +9 -5
- package/dist/packem_shared/{FileBrowser-Zcr-Qgxo.js → FileBrowser-CWvxp_is.js} +7 -7
- package/dist/packem_shared/{FunctionRunner-j0Rd5m9t.js → FunctionRunner-DlWApMCH.js} +4 -4
- package/dist/packem_shared/{FunctionStatsPanel-DboBl-XL.js → FunctionStatsPanel-CuO2Tmw-.js} +141 -198
- package/dist/packem_shared/{GlobalDataBrowser-9MhPEfgN.js → GlobalDataBrowser-3X9uRfdO.js} +37 -66
- package/dist/packem_shared/{HealthPanel-DOIgbUtx.js → HealthPanel-Dj0GRo5P.js} +31 -23
- package/dist/packem_shared/{HomePanel-bdOCNA-p.js → HomePanel-BewOCuag.js} +450 -389
- package/dist/packem_shared/{InsightsPanel-DaZPnSgt.js → InsightsPanel-y_Q-xdPY.js} +104 -66
- package/dist/packem_shared/{LogsPanel-CWdqAGpQ.js → LogsPanel-C7crgYoS.js} +35 -47
- package/dist/packem_shared/{MailPanel-D_EGtDnS.js → MailPanel-PILidruz.js} +31 -38
- package/dist/packem_shared/{MetricsPanel-E4Gv6wTO.js → MetricsPanel-DioILzgf.js} +34 -48
- package/dist/packem_shared/{MigrationsPanel-DQdPY9io.js → MigrationsPanel-DkIcRoE5.js} +31 -38
- package/dist/packem_shared/{OpenRpcReferencePanel-j2p3HB0s.js → OpenRpcReferencePanel-vbcYbv3V.js} +2 -2
- package/dist/packem_shared/{PitrPanel-BbBkQR6t.js → PitrPanel-yKJ1JPPk.js} +4 -4
- package/dist/packem_shared/{ScheduledJobs-Ok1CYYwI.js → ScheduledJobs-BWNwIN3I.js} +164 -61
- package/dist/packem_shared/{SchemaViewer-D8XGnp-X.js → SchemaViewer-CNr8Wame.js} +4 -4
- package/dist/packem_shared/{SecurityAdvisorPanel-Cdm2IxLW.js → SecurityAdvisorPanel-BBzv7L3c.js} +44 -31
- package/dist/packem_shared/SettingsPanel-BKSKl8tl.js +390 -0
- package/dist/packem_shared/{ShardInput-DNCsT1KW.js → ShardInput-gx_DZXXn.js} +1 -1
- package/dist/packem_shared/SqlEditorPanel-CkemCQvy.js +13 -0
- package/dist/packem_shared/Studio-QkOjc2NS.js +33 -0
- package/dist/packem_shared/{StudioApp-dvywkJ8I.js → StudioApp-TEElspKL.js} +278 -4
- package/dist/packem_shared/{StudioI18nProvider-Dcajsznk.js → StudioI18nProvider-B-tDCZIi.js} +1 -1
- package/dist/packem_shared/TableEditor-JS3sXYgS.js +11 -0
- package/dist/packem_shared/{advisor-view-DBlzJi6C.js → advisor-view-CGcfMiPq.js} +1 -1
- package/dist/packem_shared/{app.d-CCmwDEVs.d.ts → app.d-Cnij4Zp5.d.ts} +1 -1
- package/dist/packem_shared/{cf-links-BZfRdxSE.js → cf-links-2cktDedA.js} +1 -1
- package/dist/packem_shared/{data-grid-CCh2Couo.js → data-grid-B5CaV3wS.js} +2 -2
- package/dist/packem_shared/{grid-features-DTjG6Sex.js → grid-features-CzUfqgsM.js} +3 -3
- package/dist/packem_shared/{internal-BBZYexre.js → internal-rXJEAhXS.js} +1 -1
- package/dist/packem_shared/{live-status-bPff1O7Y.js → live-status-FZ0psC1z.js} +1 -1
- package/dist/packem_shared/{reference-view-BCKIoai7.js → reference-view-B4E49Lip.js} +3 -3
- package/dist/packem_shared/{sql-editor-panel-CW2y2x9h.js → sql-editor-panel-CUJoYF1Z.js} +6 -6
- package/dist/packem_shared/{studio-BDVd7rIV.js → studio-c5Vf7kSh.js} +2897 -1668
- package/dist/packem_shared/{TableEditor-DIVDk3vT.js → table-editor-9fhJ4hMJ.js} +43 -7
- package/dist/packem_shared/{table-list-sidebar-aZHLq70w.js → table-list-sidebar-Bg7uzbvC.js} +2 -2
- package/dist/packem_shared/use-admin-query-BkbdNFNz.js +105 -0
- package/dist/standalone/studio.js +198 -176
- package/dist/styles.css +1 -1
- package/package.json +13 -12
- package/dist/packem_shared/SettingsPanel-D3WF2mBU.js +0 -176
- package/dist/packem_shared/SqlEditorPanel-BuQ7f2Hs.js +0 -13
- package/dist/packem_shared/Studio-D36od9Oz.js +0 -33
- package/dist/packem_shared/use-live-admin-D1h1Fzsd.js +0 -73
- package/dist/packem_shared/use-live-shard-seed-B74RYcOy.js +0 -76
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { c } from 'react/compiler-runtime';
|
|
2
2
|
import { useLunora } from '@lunora/react';
|
|
3
3
|
import { useNavigate } from '@tanstack/react-router';
|
|
4
|
-
import { useState, useEffect, useMemo } from 'react';
|
|
5
|
-
import { ShardInput } from './ShardInput-
|
|
4
|
+
import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
|
|
5
|
+
import { ShardInput } from './ShardInput-gx_DZXXn.js';
|
|
6
6
|
import { B as Button } from './button-BhsN2uZH.js';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
7
|
+
import { u as useAdminQuery } from './use-admin-query-BkbdNFNz.js';
|
|
8
|
+
import useDebounced from './useDebounced-Dxncpg6z.js';
|
|
9
|
+
import { useT } from './DEFAULT_LOCALE-CiX4W7Kr.js';
|
|
10
|
+
import { ADMIN_FUNCTIONS } from './ADMIN_FUNCTIONS-CdaxLGkE.js';
|
|
11
|
+
import { f as fireAndForget, c as callOptions, a as adminRef } from './internal-rXJEAhXS.js';
|
|
10
12
|
import { r as recordShard } from './shard-history-DyebH1R5.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { ConfirmButton } from './ConfirmButton-WQVUoGFb.js';
|
|
13
|
+
import { a as advisoryRow, A as AdvisorView } from './advisor-view-CGcfMiPq.js';
|
|
14
|
+
import { ConfirmButton } from './ConfirmButton-C2eVi2Qv.js';
|
|
14
15
|
import { jsxDEV } from 'react/jsx-dev-runtime';
|
|
15
16
|
import { deriveInsights } from './DEFAULT_INSIGHT_THRESHOLDS-DjF0h-gA.js';
|
|
16
17
|
import { runAdvisor, RUNTIME_LINTS } from '@lunora/advisor';
|
|
@@ -185,9 +186,6 @@ const deriveRuntimeAdvisories = (inputs) => {
|
|
|
185
186
|
return visible.map((finding) => advisoryRow(finding));
|
|
186
187
|
};
|
|
187
188
|
|
|
188
|
-
const GET_ADVISORIES = adminRef(ADMIN_FUNCTIONS.getAdvisories);
|
|
189
|
-
const GET_FUNCTION_STATS = adminRef(ADMIN_FUNCTIONS.getFunctionStats);
|
|
190
|
-
const GET_METRICS = adminRef(ADMIN_FUNCTIONS.getMetrics);
|
|
191
189
|
const LIST_TABLES = adminRef(ADMIN_FUNCTIONS.listTables);
|
|
192
190
|
const LIST_TABLE_INDEXES = adminRef(ADMIN_FUNCTIONS.listTableIndexes);
|
|
193
191
|
const percent = (rate) => `${(rate * 100).toFixed(1)}%`;
|
|
@@ -286,46 +284,41 @@ const InsightsPanel = ({
|
|
|
286
284
|
const navigate = useNavigate();
|
|
287
285
|
const t = useT();
|
|
288
286
|
const [shardKey, setShardKey] = useState(initialShardKey ?? "");
|
|
289
|
-
const [metrics, setMetrics] = useState(null);
|
|
290
|
-
const [functions, setFunctions] = useState(null);
|
|
291
|
-
const [advisories, setAdvisories] = useState(null);
|
|
292
|
-
const [indexHits, setIndexHits] = useState(null);
|
|
293
287
|
const [declaredIndexes, setDeclaredIndexes] = useState(null);
|
|
294
288
|
const [shardTraffic, setShardTraffic] = useState(null);
|
|
295
|
-
const
|
|
289
|
+
const debouncedShard = useDebounced(shardKey.trim(), 400);
|
|
290
|
+
const latestEnumeratedShard = useRef("");
|
|
291
|
+
const metricsQuery = useAdminQuery(ADMIN_FUNCTIONS.getMetrics, {}, {
|
|
292
|
+
live: true,
|
|
293
|
+
shardKey: debouncedShard
|
|
294
|
+
});
|
|
295
|
+
const functionsQuery = useAdminQuery(ADMIN_FUNCTIONS.getFunctionStats, {}, {
|
|
296
|
+
live: true,
|
|
297
|
+
shardKey: debouncedShard
|
|
298
|
+
});
|
|
299
|
+
const advisoriesQuery = useAdminQuery(ADMIN_FUNCTIONS.getAdvisories, {}, {
|
|
300
|
+
live: true,
|
|
301
|
+
shardKey: debouncedShard
|
|
302
|
+
});
|
|
303
|
+
const metrics = metricsQuery.data ?? null;
|
|
304
|
+
const functions = functionsQuery.data?.functions ?? null;
|
|
305
|
+
const advisories = advisoriesQuery.data?.advisories ?? null;
|
|
306
|
+
const indexHits = metricsQuery.data?.indexHits ?? null;
|
|
307
|
+
const error = metricsQuery.error !== null && functionsQuery.error !== null ? metricsQuery.error : null;
|
|
296
308
|
const fanShardTraffic = loadShardTraffic ?? ((table) => client.shardTraffic(table));
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
setError(null);
|
|
303
|
-
recordShard(shard);
|
|
304
|
-
}
|
|
305
|
-
setMetrics(snapshot.status === "fulfilled" ? snapshot.value : null);
|
|
306
|
-
setFunctions(stats.status === "fulfilled" ? stats.value.functions : null);
|
|
307
|
-
setAdvisories(advisorySnapshot.status === "fulfilled" ? advisorySnapshot.value.advisories : null);
|
|
308
|
-
setIndexHits(snapshot.status === "fulfilled" ? snapshot.value.indexHits ?? null : null);
|
|
309
|
-
let tableNames = [];
|
|
310
|
-
try {
|
|
311
|
-
const tables = await client.query(LIST_TABLES, {}, callOptions(shard));
|
|
312
|
-
tableNames = tables.map((table_0) => table_0.name);
|
|
313
|
-
const indexResults = await Promise.allSettled(tables.map(async (table_1) => [table_1.name, await client.query(LIST_TABLE_INDEXES, {
|
|
314
|
-
table: table_1.name
|
|
315
|
-
}, callOptions(shard))]));
|
|
316
|
-
const declared = [];
|
|
317
|
-
for (const result of indexResults) {
|
|
318
|
-
if (result.status === "fulfilled") {
|
|
319
|
-
const [name, payload] = result.value;
|
|
320
|
-
declared.push(...declaredIndexesFor(name, payload.indexes));
|
|
321
|
-
}
|
|
309
|
+
const loadShardTrafficFeed = async (tableNames, shard) => {
|
|
310
|
+
const seedTable = tableNames[0];
|
|
311
|
+
if (seedTable === void 0 || seedTable === "") {
|
|
312
|
+
if (latestEnumeratedShard.current === shard) {
|
|
313
|
+
setShardTraffic(null);
|
|
322
314
|
}
|
|
323
|
-
|
|
324
|
-
} catch {
|
|
325
|
-
setDeclaredIndexes(null);
|
|
315
|
+
return;
|
|
326
316
|
}
|
|
327
317
|
try {
|
|
328
|
-
const traffic = await fanShardTraffic(
|
|
318
|
+
const traffic = await fanShardTraffic(seedTable);
|
|
319
|
+
if (latestEnumeratedShard.current !== shard) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
329
322
|
const byShard = /* @__PURE__ */ new Map();
|
|
330
323
|
for (const entry of traffic.shards) {
|
|
331
324
|
if (!byShard.has(entry.shardKey)) {
|
|
@@ -337,31 +330,76 @@ const InsightsPanel = ({
|
|
|
337
330
|
}
|
|
338
331
|
setShardTraffic([...byShard.values()]);
|
|
339
332
|
} catch {
|
|
340
|
-
|
|
333
|
+
if (latestEnumeratedShard.current === shard) {
|
|
334
|
+
setShardTraffic(null);
|
|
335
|
+
}
|
|
341
336
|
}
|
|
342
337
|
};
|
|
343
|
-
|
|
338
|
+
const enumerateShard = useCallback(
|
|
339
|
+
async (shard) => {
|
|
340
|
+
latestEnumeratedShard.current = shard;
|
|
341
|
+
let tableNames = [];
|
|
342
|
+
try {
|
|
343
|
+
const tables = await client.query(LIST_TABLES, {}, callOptions(shard));
|
|
344
|
+
if (latestEnumeratedShard.current !== shard) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
recordShard(shard);
|
|
348
|
+
tableNames = tables.map((table) => table.name);
|
|
349
|
+
const indexResults = await Promise.allSettled(tables.map(async (table) => [table.name, await client.query(LIST_TABLE_INDEXES, {
|
|
350
|
+
table: table.name
|
|
351
|
+
}, callOptions(shard))]));
|
|
352
|
+
if (latestEnumeratedShard.current !== shard) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const declared = [];
|
|
356
|
+
for (const result of indexResults) {
|
|
357
|
+
if (result.status === "fulfilled") {
|
|
358
|
+
const [name, payload] = result.value;
|
|
359
|
+
declared.push(...declaredIndexesFor(name, payload.indexes));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
setDeclaredIndexes(declared);
|
|
363
|
+
} catch {
|
|
364
|
+
if (latestEnumeratedShard.current === shard) {
|
|
365
|
+
setDeclaredIndexes(null);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
await loadShardTrafficFeed(tableNames, shard);
|
|
369
|
+
},
|
|
370
|
+
// `fanShardTraffic`/`loadShardTrafficFeed` are render-fresh closures; the
|
|
371
|
+
// enumeration only needs to re-run on a shard change (or a manual visibility
|
|
372
|
+
// refresh), so they're intentionally excluded from the cache key.
|
|
373
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
374
|
+
[client]
|
|
375
|
+
);
|
|
376
|
+
useEffect(() => {
|
|
377
|
+
fireAndForget(enumerateShard(debouncedShard));
|
|
378
|
+
}, [debouncedShard, enumerateShard]);
|
|
344
379
|
useEffect(() => {
|
|
345
380
|
const onVisible = () => {
|
|
346
381
|
if (document.visibilityState === "visible") {
|
|
347
|
-
|
|
382
|
+
metricsQuery.refetch();
|
|
383
|
+
functionsQuery.refetch();
|
|
384
|
+
advisoriesQuery.refetch();
|
|
385
|
+
fireAndForget(enumerateShard(debouncedShard));
|
|
348
386
|
}
|
|
349
387
|
};
|
|
350
388
|
document.addEventListener("visibilitychange", onVisible);
|
|
351
389
|
return () => {
|
|
352
390
|
document.removeEventListener("visibilitychange", onVisible);
|
|
353
391
|
};
|
|
354
|
-
}, [
|
|
355
|
-
const jumpToSchemaIndex = (
|
|
392
|
+
}, [advisoriesQuery, debouncedShard, enumerateShard, functionsQuery, metricsQuery]);
|
|
393
|
+
const jumpToSchemaIndex = useCallback((table) => {
|
|
356
394
|
fireAndForget(navigate({
|
|
357
395
|
search: {
|
|
358
|
-
table
|
|
396
|
+
table
|
|
359
397
|
},
|
|
360
398
|
to: "/schema"
|
|
361
399
|
}));
|
|
362
|
-
};
|
|
400
|
+
}, [navigate]);
|
|
363
401
|
const insights = deriveInsights(metrics, functions);
|
|
364
|
-
const missingIndexTables = new Set(insights.filter((insight) => insight.kind === "missing-index").flatMap((
|
|
402
|
+
const missingIndexTables = new Set(insights.filter((insight) => insight.kind === "missing-index").flatMap((insight) => insight.tables ?? []));
|
|
365
403
|
const runtimeRows = deriveRuntimeAdvisories({
|
|
366
404
|
declaredIndexes: declaredIndexes ?? [],
|
|
367
405
|
functions,
|
|
@@ -370,18 +408,18 @@ const InsightsPanel = ({
|
|
|
370
408
|
suppressHotScanTables: missingIndexTables
|
|
371
409
|
});
|
|
372
410
|
const rows = useMemo(() => {
|
|
373
|
-
const insightRows = insights.map((
|
|
374
|
-
const
|
|
411
|
+
const insightRows = insights.map((insight) => {
|
|
412
|
+
const tables = insight.kind === "missing-index" ? insight.tables ?? [] : [];
|
|
375
413
|
return {
|
|
376
|
-
action:
|
|
414
|
+
action: tables.length > 0 ? tables.map((table) => /* @__PURE__ */ jsxDEV(AddIndexButton, {
|
|
377
415
|
onJump: jumpToSchemaIndex,
|
|
378
|
-
table
|
|
379
|
-
},
|
|
380
|
-
description:
|
|
381
|
-
entity:
|
|
382
|
-
issueType: insightTitle(t,
|
|
383
|
-
key: `${
|
|
384
|
-
level:
|
|
416
|
+
table
|
|
417
|
+
}, table, false)) : void 0,
|
|
418
|
+
description: insight.message === void 0 ? insightDetail(t, insight) : `${insightDetail(t, insight)} — ${insight.message}`,
|
|
419
|
+
entity: insight.fn,
|
|
420
|
+
issueType: insightTitle(t, insight),
|
|
421
|
+
key: `${insight.kind}:${insight.fn ?? ""}`,
|
|
422
|
+
level: insight.severity
|
|
385
423
|
};
|
|
386
424
|
});
|
|
387
425
|
const indexAdvisoryLints = /* @__PURE__ */ new Set(["unindexed_foreign_key", "unindexed_relation_target"]);
|
|
@@ -389,16 +427,16 @@ const InsightsPanel = ({
|
|
|
389
427
|
const base = advisoryRow(finding);
|
|
390
428
|
if (indexAdvisoryLints.has(finding.name) && hasIndexMetadata(finding.metadata)) {
|
|
391
429
|
const {
|
|
392
|
-
table
|
|
430
|
+
table,
|
|
393
431
|
suggestedIndex
|
|
394
432
|
} = finding.metadata;
|
|
395
|
-
const testId = `in-apply-index-${
|
|
433
|
+
const testId = `in-apply-index-${table}-${suggestedIndex.name}`;
|
|
396
434
|
return {
|
|
397
435
|
...base,
|
|
398
436
|
action: /* @__PURE__ */ jsxDEV(ApplyIndexButton, {
|
|
399
437
|
fields: suggestedIndex.fields,
|
|
400
438
|
indexName: suggestedIndex.name,
|
|
401
|
-
table
|
|
439
|
+
table,
|
|
402
440
|
testId
|
|
403
441
|
}, void 0, false)
|
|
404
442
|
};
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { c } from 'react/compiler-runtime';
|
|
2
|
-
import { useLunora } from '@lunora/react';
|
|
3
2
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
4
|
-
import { useState, useMemo, useRef } from 'react';
|
|
5
|
-
import { L as LiveError } from './live-status-
|
|
6
|
-
import { ShardInput } from './ShardInput-
|
|
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';
|
|
7
6
|
import { B as Badge } from './badge-B2PKA1-5.js';
|
|
8
7
|
import { E as EmptyState } from './empty-state-DY_oe0k6.js';
|
|
9
8
|
import { I as Input } from './input-XH4r1Pt1.js';
|
|
10
|
-
import { u as
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
9
|
+
import { u as useAdminQuery } from './use-admin-query-BkbdNFNz.js';
|
|
10
|
+
import useDebounced from './useDebounced-Dxncpg6z.js';
|
|
11
|
+
import { useT } from './DEFAULT_LOCALE-CiX4W7Kr.js';
|
|
12
|
+
import { ADMIN_FUNCTIONS } from './ADMIN_FUNCTIONS-CdaxLGkE.js';
|
|
13
|
+
import { C as CLOUDFLARE_OBSERVABILITY_URL } from './cf-links-2cktDedA.js';
|
|
15
14
|
import { r as recordShard } from './shard-history-DyebH1R5.js';
|
|
16
15
|
import { c as cn } from './utils-B05Dmz_H.js';
|
|
17
16
|
import { f as flooredRectObserver } from './virtual-rect-CVMUskSm.js';
|
|
18
|
-
import { u as useLiveShardSeed } from './use-live-shard-seed-B74RYcOy.js';
|
|
19
17
|
import { jsxDEV } from 'react/jsx-dev-runtime';
|
|
20
18
|
|
|
21
19
|
const SCROLL_HEIGHT = 400;
|
|
@@ -316,8 +314,6 @@ const RequestRow = (t0) => {
|
|
|
316
314
|
}
|
|
317
315
|
return t17;
|
|
318
316
|
};
|
|
319
|
-
const GET_LOGS = adminRef(ADMIN_FUNCTIONS.getLogs);
|
|
320
|
-
const GET_REQUEST_LOG = adminRef(ADMIN_FUNCTIONS.getRequestLog);
|
|
321
317
|
const entriesOf = (result) => {
|
|
322
318
|
const {
|
|
323
319
|
entries
|
|
@@ -488,13 +484,9 @@ const SummaryBucketRow = (t0) => {
|
|
|
488
484
|
const LogsPanel = ({
|
|
489
485
|
initialShardKey
|
|
490
486
|
}) => {
|
|
491
|
-
const client = useLunora();
|
|
492
487
|
const t = useT();
|
|
493
488
|
const [view, setView] = useState("requests");
|
|
494
489
|
const [shardKey, setShardKey] = useState(initialShardKey ?? "");
|
|
495
|
-
const [entries, setEntries] = useState([]);
|
|
496
|
-
const [requests, setRequests] = useState([]);
|
|
497
|
-
const [error, setError] = useState(null);
|
|
498
490
|
const [search, setSearch] = useState("");
|
|
499
491
|
const [levelFilter, setLevelFilter] = useState(() => /* @__PURE__ */ new Set());
|
|
500
492
|
const [pathFilter, setPathFilter] = useState("");
|
|
@@ -504,43 +496,36 @@ const LogsPanel = ({
|
|
|
504
496
|
const [userIdFilter, setUserIdFilter] = useState("");
|
|
505
497
|
const [tableFilter, setTableFilter] = useState("");
|
|
506
498
|
const [outcomeFilter, setOutcomeFilter] = useState("all");
|
|
507
|
-
const
|
|
499
|
+
const debouncedShard = useDebounced(shardKey.trim(), 400);
|
|
508
500
|
const requestQuery = buildRequestQuery({
|
|
509
501
|
functionPathPrefix: pathPrefix,
|
|
510
502
|
outcome: outcomeFilter,
|
|
511
503
|
tableTouched: tableFilter,
|
|
512
504
|
userId: userIdFilter
|
|
513
505
|
});
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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);
|
|
531
527
|
}
|
|
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);
|
|
528
|
+
}, [activeQuery.data, debouncedShard]);
|
|
544
529
|
const filtered = filterLogs(entries, {
|
|
545
530
|
levels: levelFilter,
|
|
546
531
|
now: Date.now(),
|
|
@@ -556,10 +541,13 @@ const LogsPanel = ({
|
|
|
556
541
|
return "error";
|
|
557
542
|
}
|
|
558
543
|
if (activeCount === 0) {
|
|
544
|
+
if (activeLoading) {
|
|
545
|
+
return summaryVisible ? "summary" : "list";
|
|
546
|
+
}
|
|
559
547
|
return "empty";
|
|
560
548
|
}
|
|
561
549
|
return summaryVisible ? "summary" : "list";
|
|
562
|
-
}, [activeCount, error, summaryVisible]);
|
|
550
|
+
}, [activeCount, activeLoading, error, summaryVisible]);
|
|
563
551
|
const scrollRef = useRef(null);
|
|
564
552
|
const virtualizer = useVirtualizer({
|
|
565
553
|
count: activeCount,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useLunora } from '@lunora/react';
|
|
3
|
-
import { useState,
|
|
3
|
+
import { useState, useMemo } from 'react';
|
|
4
4
|
import { B as Badge } from './badge-B2PKA1-5.js';
|
|
5
5
|
import { B as Button } from './button-BhsN2uZH.js';
|
|
6
6
|
import { E as EmptyState } from './empty-state-DY_oe0k6.js';
|
|
@@ -10,10 +10,11 @@ import { ScrollArea as ScrollArea$1 } from '@base-ui/react/scroll-area';
|
|
|
10
10
|
import { c as cn } from './utils-B05Dmz_H.js';
|
|
11
11
|
import { jsxDEV, Fragment } from 'react/jsx-dev-runtime';
|
|
12
12
|
import { Separator as Separator$1 } from '@base-ui/react/separator';
|
|
13
|
+
import { u as useAdminQuery } from './use-admin-query-BkbdNFNz.js';
|
|
13
14
|
import { useAutoRefresh } from './DEFAULT_AUTO_REFRESH_MS-Vxwaxx51.js';
|
|
14
|
-
import { useT } from './
|
|
15
|
-
import { ADMIN_FUNCTIONS } from './
|
|
16
|
-
import { f as fireAndForget,
|
|
15
|
+
import { useT } from './DEFAULT_LOCALE-CiX4W7Kr.js';
|
|
16
|
+
import { ADMIN_FUNCTIONS } from './ADMIN_FUNCTIONS-CdaxLGkE.js';
|
|
17
|
+
import { b as formatTimestamp, f as fireAndForget, g as copyToClipboard, c as callOptions, e as errorMessage, a as adminRef } from './internal-rXJEAhXS.js';
|
|
17
18
|
|
|
18
19
|
function Separator({
|
|
19
20
|
className,
|
|
@@ -114,7 +115,6 @@ function ScrollBar({
|
|
|
114
115
|
}, void 0, false);
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
const GET_CAPTURED_MAIL = adminRef(ADMIN_FUNCTIONS.getCapturedMail);
|
|
118
118
|
const CLEAR_CAPTURED_MAIL = adminRef(ADMIN_FUNCTIONS.clearCapturedMail);
|
|
119
119
|
const SEND_TEST_MAIL = adminRef(ADMIN_FUNCTIONS.sendTestMail);
|
|
120
120
|
const LINK_PATTERN = /https?:\/\/[^\s"'<>)]+/i;
|
|
@@ -144,49 +144,42 @@ const MailPanel = ({
|
|
|
144
144
|
}) => {
|
|
145
145
|
const client = useLunora();
|
|
146
146
|
const t = useT();
|
|
147
|
-
const
|
|
148
|
-
|
|
147
|
+
const {
|
|
148
|
+
data,
|
|
149
|
+
error: readError,
|
|
150
|
+
isLoading,
|
|
151
|
+
refetch
|
|
152
|
+
} = useAdminQuery(ADMIN_FUNCTIONS.getCapturedMail, {
|
|
153
|
+
limit
|
|
154
|
+
});
|
|
155
|
+
const [actionError, setActionError] = useState(null);
|
|
149
156
|
const [selectedId, setSelectedId] = useState(null);
|
|
150
157
|
const [tab, setTab] = useState("html");
|
|
151
158
|
const [filter, setFilter] = useState("");
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
const next = await client.query(GET_CAPTURED_MAIL, {
|
|
156
|
-
limit
|
|
157
|
-
}, callOptions(""));
|
|
158
|
-
setEntries(next.entries);
|
|
159
|
-
} catch (error_) {
|
|
160
|
-
setEntries([]);
|
|
161
|
-
setError(errorMessage(error_));
|
|
162
|
-
}
|
|
163
|
-
};
|
|
159
|
+
const entries = useMemo(() => data?.entries ?? [], [data]);
|
|
160
|
+
const error = readError ?? actionError;
|
|
164
161
|
const clearInbox = async () => {
|
|
165
|
-
|
|
162
|
+
setActionError(null);
|
|
166
163
|
try {
|
|
167
164
|
await client.query(CLEAR_CAPTURED_MAIL, {}, callOptions(""));
|
|
168
165
|
setSelectedId(null);
|
|
169
|
-
|
|
170
|
-
} catch (
|
|
171
|
-
|
|
166
|
+
refetch();
|
|
167
|
+
} catch (error_) {
|
|
168
|
+
setActionError(errorMessage(error_));
|
|
172
169
|
}
|
|
173
170
|
};
|
|
174
171
|
const sendTest = async () => {
|
|
175
|
-
|
|
172
|
+
setActionError(null);
|
|
176
173
|
try {
|
|
177
174
|
await client.query(SEND_TEST_MAIL, {}, callOptions(""));
|
|
178
|
-
|
|
179
|
-
} catch (
|
|
180
|
-
|
|
175
|
+
refetch();
|
|
176
|
+
} catch (error__0) {
|
|
177
|
+
setActionError(errorMessage(error__0));
|
|
181
178
|
}
|
|
182
179
|
};
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
},
|
|
186
|
-
const reload = () => {
|
|
187
|
-
fireAndForget(refresh());
|
|
188
|
-
};
|
|
189
|
-
useAutoRefresh(reload, true);
|
|
180
|
+
useAutoRefresh(() => {
|
|
181
|
+
refetch();
|
|
182
|
+
}, true);
|
|
190
183
|
const onClear = () => {
|
|
191
184
|
fireAndForget(clearInbox());
|
|
192
185
|
};
|
|
@@ -205,9 +198,9 @@ const MailPanel = ({
|
|
|
205
198
|
}
|
|
206
199
|
};
|
|
207
200
|
const onSelectTab = (event_1) => {
|
|
208
|
-
const
|
|
209
|
-
if (
|
|
210
|
-
setTab(
|
|
201
|
+
const next = event_1.currentTarget.dataset["tab"];
|
|
202
|
+
if (next === "headers" || next === "html" || next === "text") {
|
|
203
|
+
setTab(next);
|
|
211
204
|
}
|
|
212
205
|
};
|
|
213
206
|
const tabTitle = (value) => {
|
|
@@ -276,7 +269,7 @@ const MailPanel = ({
|
|
|
276
269
|
"data-testid": "mail-error",
|
|
277
270
|
role: "alert",
|
|
278
271
|
children: error
|
|
279
|
-
}, void 0, false), error === null && entries.length === 0 && /* @__PURE__ */ jsxDEV(EmptyState, {
|
|
272
|
+
}, void 0, false), error === null && !isLoading && entries.length === 0 && /* @__PURE__ */ jsxDEV(EmptyState, {
|
|
280
273
|
description: t("Email your app sends in dev is captured here — nothing is delivered."),
|
|
281
274
|
icon: /* @__PURE__ */ jsxDEV("svg", {
|
|
282
275
|
"aria-hidden": "true",
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
import { c } from 'react/compiler-runtime';
|
|
3
3
|
import { useLunora } from '@lunora/react';
|
|
4
4
|
import { useState, useRef, useEffect, useMemo } from 'react';
|
|
5
|
-
import { L as LiveError } from './live-status-
|
|
6
|
-
import { ShardInput } from './ShardInput-
|
|
5
|
+
import { L as LiveError } from './live-status-FZ0psC1z.js';
|
|
6
|
+
import { ShardInput } from './ShardInput-gx_DZXXn.js';
|
|
7
7
|
import { B as Button } from './button-BhsN2uZH.js';
|
|
8
8
|
import { a as CardContent, C as Card, b as CardHeader, c as CardTitle, d as CardDescription } from './card-DURq3ElK.js';
|
|
9
9
|
import { T as Table, a as TableHeader, b as TableRow, c as TableHead, d as TableBody, e as TableCell } from './table-_RzNvy3R.js';
|
|
10
|
-
import { u as
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
10
|
+
import { u as useAdminQuery } from './use-admin-query-BkbdNFNz.js';
|
|
11
|
+
import useDebounced from './useDebounced-Dxncpg6z.js';
|
|
12
|
+
import { useT } from './DEFAULT_LOCALE-CiX4W7Kr.js';
|
|
13
|
+
import { ADMIN_FUNCTIONS } from './ADMIN_FUNCTIONS-CdaxLGkE.js';
|
|
14
|
+
import { c as CLOUDFLARE_DURABLE_OBJECTS_URL } from './cf-links-2cktDedA.js';
|
|
15
|
+
import { d as formatBytes, f as fireAndForget, c as callOptions, e as errorMessage, a as adminRef } from './internal-rXJEAhXS.js';
|
|
16
|
+
import { r as recordShard, l as loadRecentShards } from './shard-history-DyebH1R5.js';
|
|
17
17
|
import { aggregateMetrics, computeLatencyPercentiles, enrichQueryStats, shardsToAggregate } from './aggregateMetrics-D4nUHEKU.js';
|
|
18
18
|
import { B as Badge } from './badge-B2PKA1-5.js';
|
|
19
19
|
import { Select as Select$1 } from '@base-ui/react/select';
|
|
@@ -1205,6 +1205,7 @@ const formatDuration = (ms) => {
|
|
|
1205
1205
|
}
|
|
1206
1206
|
return `${seconds.toString()}s`;
|
|
1207
1207
|
};
|
|
1208
|
+
const resolveTab = (hasQueryStats, activeTab) => hasQueryStats ? activeTab : "overview";
|
|
1208
1209
|
const hitRate = (hits, misses) => {
|
|
1209
1210
|
const total = hits + misses;
|
|
1210
1211
|
return total === 0 ? "—" : `${(hits / total * 100).toFixed(1)}%`;
|
|
@@ -1297,24 +1298,33 @@ const MetricsPanel = ({
|
|
|
1297
1298
|
const client = useLunora();
|
|
1298
1299
|
const t = useT();
|
|
1299
1300
|
const [shardKey, setShardKey] = useState(initialShardKey ?? "");
|
|
1300
|
-
const [metrics, setMetrics] = useState(null);
|
|
1301
|
-
const [error, setError] = useState(null);
|
|
1302
1301
|
const [activeTab, setActiveTab] = useState("overview");
|
|
1303
|
-
const [liveError, setLiveError] = useState(void 0);
|
|
1304
1302
|
const [history, setHistory] = useState([]);
|
|
1305
|
-
const mountedRef = useRef(true);
|
|
1306
1303
|
const lastRequestsRef = useRef(null);
|
|
1307
1304
|
const lastShardRef = useRef(null);
|
|
1305
|
+
const mountedRef = useRef(true);
|
|
1308
1306
|
useEffect(() => {
|
|
1309
1307
|
mountedRef.current = true;
|
|
1310
1308
|
return () => {
|
|
1311
1309
|
mountedRef.current = false;
|
|
1312
1310
|
};
|
|
1313
1311
|
}, []);
|
|
1314
|
-
const
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1312
|
+
const debouncedShard = useDebounced(shardKey.trim(), 400);
|
|
1313
|
+
const {
|
|
1314
|
+
data,
|
|
1315
|
+
error,
|
|
1316
|
+
liveError
|
|
1317
|
+
} = useAdminQuery(ADMIN_FUNCTIONS.getMetrics, {}, {
|
|
1318
|
+
live: true,
|
|
1319
|
+
shardKey: debouncedShard
|
|
1320
|
+
});
|
|
1321
|
+
const metrics = data ?? null;
|
|
1322
|
+
useEffect(() => {
|
|
1323
|
+
if (data === void 0) {
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
recordShard(debouncedShard);
|
|
1327
|
+
const next = data;
|
|
1318
1328
|
const previous = lastRequestsRef.current;
|
|
1319
1329
|
const shardChanged = lastShardRef.current !== null && lastShardRef.current !== next.shard;
|
|
1320
1330
|
lastShardRef.current = next.shard;
|
|
@@ -1326,32 +1336,7 @@ const MetricsPanel = ({
|
|
|
1326
1336
|
if (previous !== null && next.requests >= previous) {
|
|
1327
1337
|
setHistory((prior) => [...prior, next.requests - previous].slice(-30));
|
|
1328
1338
|
}
|
|
1329
|
-
};
|
|
1330
|
-
const refresh = async (shard) => {
|
|
1331
|
-
try {
|
|
1332
|
-
const next_0 = await client.query(GET_METRICS, {}, callOptions(shard));
|
|
1333
|
-
recordShard(shard);
|
|
1334
|
-
if (mountedRef.current) {
|
|
1335
|
-
applySample(next_0);
|
|
1336
|
-
}
|
|
1337
|
-
} catch (error_) {
|
|
1338
|
-
if (mountedRef.current) {
|
|
1339
|
-
setMetrics(null);
|
|
1340
|
-
setError(errorMessage(error_));
|
|
1341
|
-
}
|
|
1342
|
-
throw error_;
|
|
1343
|
-
}
|
|
1344
|
-
};
|
|
1345
|
-
const committedShard = useLiveShardSeed(shardKey, refresh);
|
|
1346
|
-
useLiveAdmin(ADMIN_FUNCTIONS.getMetrics, {}, committedShard ?? "", (next_1) => {
|
|
1347
|
-
if (mountedRef.current) {
|
|
1348
|
-
applySample(next_1);
|
|
1349
|
-
}
|
|
1350
|
-
}, committedShard !== void 0, (message) => {
|
|
1351
|
-
if (mountedRef.current) {
|
|
1352
|
-
setLiveError(message);
|
|
1353
|
-
}
|
|
1354
|
-
});
|
|
1339
|
+
}, [data, debouncedShard]);
|
|
1355
1340
|
const [shardResults, setShardResults] = useState(null);
|
|
1356
1341
|
const [aggregating, setAggregating] = useState(false);
|
|
1357
1342
|
const aggregateAll = async () => {
|
|
@@ -1412,6 +1397,7 @@ const MetricsPanel = ({
|
|
|
1412
1397
|
}
|
|
1413
1398
|
return enrichQueryStats(snapQs);
|
|
1414
1399
|
}, [metrics]);
|
|
1400
|
+
const effectiveTab = resolveTab(queryStats !== void 0, activeTab);
|
|
1415
1401
|
const runAggregate = () => {
|
|
1416
1402
|
fireAndForget(aggregateAll());
|
|
1417
1403
|
};
|
|
@@ -1468,13 +1454,13 @@ const MetricsPanel = ({
|
|
|
1468
1454
|
className: "flex gap-2 border-b",
|
|
1469
1455
|
"data-testid": "mt-tabs",
|
|
1470
1456
|
children: [/* @__PURE__ */ jsxDEV("button", {
|
|
1471
|
-
className: `pb-2 text-sm font-medium transition-colors ${
|
|
1457
|
+
className: `pb-2 text-sm font-medium transition-colors ${effectiveTab === "overview" ? "border-b-2 border-primary text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
1472
1458
|
"data-testid": "mt-tab-overview",
|
|
1473
1459
|
onClick: switchToOverview,
|
|
1474
1460
|
type: "button",
|
|
1475
1461
|
children: t("Overview")
|
|
1476
1462
|
}, void 0, false), /* @__PURE__ */ jsxDEV("button", {
|
|
1477
|
-
className: `pb-2 text-sm font-medium transition-colors ${
|
|
1463
|
+
className: `pb-2 text-sm font-medium transition-colors ${effectiveTab === "query-insights" ? "border-b-2 border-primary text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
1478
1464
|
"data-testid": "mt-tab-query-insights",
|
|
1479
1465
|
onClick: switchToQueryInsights,
|
|
1480
1466
|
type: "button",
|
|
@@ -1483,9 +1469,9 @@ const MetricsPanel = ({
|
|
|
1483
1469
|
children: queryStats.length
|
|
1484
1470
|
}, void 0, false)]
|
|
1485
1471
|
}, void 0, true)]
|
|
1486
|
-
}, void 0, true),
|
|
1472
|
+
}, void 0, true), effectiveTab === "query-insights" && queryStats !== void 0 && /* @__PURE__ */ jsxDEV(QueryInsights, {
|
|
1487
1473
|
queryStats
|
|
1488
|
-
}, void 0, false),
|
|
1474
|
+
}, void 0, false), effectiveTab === "overview" && metrics !== null && /* @__PURE__ */ jsxDEV("dl", {
|
|
1489
1475
|
className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
|
|
1490
1476
|
"data-testid": "mt-stats",
|
|
1491
1477
|
children: [/* @__PURE__ */ jsxDEV(StatCard, {
|
|
@@ -1542,7 +1528,7 @@ const MetricsPanel = ({
|
|
|
1542
1528
|
rate: hitRate(metrics.cache.hits, metrics.cache.misses)
|
|
1543
1529
|
})
|
|
1544
1530
|
}, void 0, false)]
|
|
1545
|
-
}, void 0, true),
|
|
1531
|
+
}, void 0, true), effectiveTab === "overview" && metrics === null && null, effectiveTab === "overview" && aggregate !== null && shardResults !== null && /* @__PURE__ */ jsxDEV("div", {
|
|
1546
1532
|
className: "flex flex-col gap-4",
|
|
1547
1533
|
"data-testid": "mt-aggregate-view",
|
|
1548
1534
|
children: [/* @__PURE__ */ jsxDEV("dl", {
|