@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,375 @@
1
+ import { c } from 'react/compiler-runtime';
2
+ import { useState } from 'react';
3
+ import { L as LiveError } from './live-status-FZ0psC1z.js';
4
+ import { ShardInput } from './ShardInput-gx_DZXXn.js';
5
+ import { B as Badge } from './badge-B2PKA1-5.js';
6
+ import { B as Button } from './button-BhsN2uZH.js';
7
+ import { C as Card, a as CardContent } from './card-DURq3ElK.js';
8
+ import { E as EmptyState } from './empty-state-DY_oe0k6.js';
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 useAdminQuery } from './use-admin-query-4JQW2rr1.js';
11
+ import useDebounced from './useDebounced-Dxncpg6z.js';
12
+ import { useT } from './DEFAULT_LOCALE-CiX4W7Kr.js';
13
+ import { ADMIN_FUNCTIONS } from './ADMIN_FUNCTIONS-DjXgvJb6.js';
14
+ import { b as formatTimestamp } from './internal-rXJEAhXS.js';
15
+ import { c as cn } from './utils-B05Dmz_H.js';
16
+ import { jsxDEV } from 'react/jsx-dev-runtime';
17
+
18
+ const KIND_VARIANT = {
19
+ action: "outline",
20
+ mutation: "default",
21
+ query: "secondary"
22
+ };
23
+ const formatMs = (ms) => {
24
+ if (ms < 1e3) {
25
+ return `${ms < 10 ? ms.toFixed(1) : Math.round(ms).toString()}ms`;
26
+ }
27
+ return `${(ms / 1e3).toFixed(2)}s`;
28
+ };
29
+ const errorCell = (stat) => {
30
+ if (stat.calls === 0) {
31
+ return "—";
32
+ }
33
+ if (stat.errors === 0) {
34
+ return "0";
35
+ }
36
+ return `${stat.errors.toString()} (${(stat.errors / stat.calls * 100).toFixed(1)}%)`;
37
+ };
38
+ const conflictCell = (stat) => {
39
+ const conflicts = stat.conflicts ?? 0;
40
+ if (stat.calls === 0) {
41
+ return "—";
42
+ }
43
+ if (conflicts === 0) {
44
+ return "0";
45
+ }
46
+ return `${conflicts.toString()} (${(conflicts / stat.calls * 100).toFixed(1)}%)`;
47
+ };
48
+ const SORTERS = {
49
+ calls: (a, b) => b.calls - a.calls,
50
+ recent: (a, b) => b.lastCalledAt - a.lastCalledAt,
51
+ slowest: (a, b) => b.maxDurationMs - a.maxDurationMs
52
+ };
53
+ const FunctionStatsPanel = (t0) => {
54
+ const $ = c(45);
55
+ const {
56
+ functions,
57
+ initialShardKey
58
+ } = t0;
59
+ const t = useT();
60
+ const [shardKey, setShardKey] = useState(initialShardKey ?? "");
61
+ const [sortKey, setSortKey] = useState("recent");
62
+ let t1;
63
+ if ($[0] !== shardKey) {
64
+ t1 = shardKey.trim();
65
+ $[0] = shardKey;
66
+ $[1] = t1;
67
+ } else {
68
+ t1 = $[1];
69
+ }
70
+ const debouncedShard = useDebounced(t1, 400);
71
+ let t2;
72
+ if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
73
+ t2 = {};
74
+ $[2] = t2;
75
+ } else {
76
+ t2 = $[2];
77
+ }
78
+ let t3;
79
+ if ($[3] !== debouncedShard) {
80
+ t3 = {
81
+ live: true,
82
+ shardKey: debouncedShard
83
+ };
84
+ $[3] = debouncedShard;
85
+ $[4] = t3;
86
+ } else {
87
+ t3 = $[4];
88
+ }
89
+ const {
90
+ data,
91
+ error,
92
+ liveError
93
+ } = useAdminQuery(ADMIN_FUNCTIONS.getFunctionStats, t2, t3);
94
+ const stats = data?.functions ?? null;
95
+ let t4;
96
+ if ($[5] !== functions) {
97
+ t4 = new Map((functions ?? []).map(_temp));
98
+ $[5] = functions;
99
+ $[6] = t4;
100
+ } else {
101
+ t4 = $[6];
102
+ }
103
+ const kindByPath = t4;
104
+ let t5;
105
+ if ($[7] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
106
+ t5 = (event) => {
107
+ setSortKey(event.currentTarget.dataset.sort);
108
+ };
109
+ $[7] = t5;
110
+ } else {
111
+ t5 = $[7];
112
+ }
113
+ const selectSort = t5;
114
+ let t6;
115
+ if ($[8] !== error || $[9] !== kindByPath || $[10] !== liveError || $[11] !== shardKey || $[12] !== sortKey || $[13] !== stats || $[14] !== t) {
116
+ const sorted = stats === null ? [] : stats.toSorted(SORTERS[sortKey]);
117
+ let t7;
118
+ if ($[16] !== t) {
119
+ t7 = t("Recent");
120
+ $[16] = t;
121
+ $[17] = t7;
122
+ } else {
123
+ t7 = $[17];
124
+ }
125
+ let t8;
126
+ if ($[18] !== t7) {
127
+ t8 = {
128
+ key: "recent",
129
+ label: t7
130
+ };
131
+ $[18] = t7;
132
+ $[19] = t8;
133
+ } else {
134
+ t8 = $[19];
135
+ }
136
+ let t9;
137
+ if ($[20] !== t) {
138
+ t9 = t("Most called");
139
+ $[20] = t;
140
+ $[21] = t9;
141
+ } else {
142
+ t9 = $[21];
143
+ }
144
+ let t10;
145
+ if ($[22] !== t9) {
146
+ t10 = {
147
+ key: "calls",
148
+ label: t9
149
+ };
150
+ $[22] = t9;
151
+ $[23] = t10;
152
+ } else {
153
+ t10 = $[23];
154
+ }
155
+ let t11;
156
+ if ($[24] !== t) {
157
+ t11 = t("Slowest");
158
+ $[24] = t;
159
+ $[25] = t11;
160
+ } else {
161
+ t11 = $[25];
162
+ }
163
+ let t12;
164
+ if ($[26] !== t11) {
165
+ t12 = {
166
+ key: "slowest",
167
+ label: t11
168
+ };
169
+ $[26] = t11;
170
+ $[27] = t12;
171
+ } else {
172
+ t12 = $[27];
173
+ }
174
+ let t13;
175
+ if ($[28] !== t10 || $[29] !== t12 || $[30] !== t8) {
176
+ t13 = [t8, t10, t12];
177
+ $[28] = t10;
178
+ $[29] = t12;
179
+ $[30] = t8;
180
+ $[31] = t13;
181
+ } else {
182
+ t13 = $[31];
183
+ }
184
+ const sortButtons = t13;
185
+ let t14;
186
+ if ($[32] !== shardKey) {
187
+ t14 = /* @__PURE__ */ jsxDEV(ShardInput, {
188
+ onChange: setShardKey,
189
+ testId: "fs-shard-input",
190
+ value: shardKey
191
+ }, void 0, false);
192
+ $[32] = shardKey;
193
+ $[33] = t14;
194
+ } else {
195
+ t14 = $[33];
196
+ }
197
+ let t15;
198
+ if ($[34] !== liveError) {
199
+ t15 = /* @__PURE__ */ jsxDEV(LiveError, {
200
+ message: liveError,
201
+ prefix: "fs"
202
+ }, void 0, false);
203
+ $[34] = liveError;
204
+ $[35] = t15;
205
+ } else {
206
+ t15 = $[35];
207
+ }
208
+ let t16;
209
+ if ($[36] !== sortButtons || $[37] !== sortKey) {
210
+ t16 = /* @__PURE__ */ jsxDEV("div", {
211
+ className: "ml-auto flex items-center gap-1",
212
+ role: "group",
213
+ children: sortButtons.map((option) => /* @__PURE__ */ jsxDEV(Button, {
214
+ "data-sort": option.key,
215
+ "data-testid": `fs-sort-${option.key}`,
216
+ onClick: selectSort,
217
+ size: "sm",
218
+ type: "button",
219
+ variant: sortKey === option.key ? "secondary" : "ghost",
220
+ children: option.label
221
+ }, option.key, false))
222
+ }, void 0, false);
223
+ $[36] = sortButtons;
224
+ $[37] = sortKey;
225
+ $[38] = t16;
226
+ } else {
227
+ t16 = $[38];
228
+ }
229
+ let t17;
230
+ if ($[39] !== t14 || $[40] !== t15 || $[41] !== t16) {
231
+ t17 = /* @__PURE__ */ jsxDEV("div", {
232
+ className: "flex flex-wrap items-center gap-2",
233
+ children: [t14, t15, t16]
234
+ }, void 0, true);
235
+ $[39] = t14;
236
+ $[40] = t15;
237
+ $[41] = t16;
238
+ $[42] = t17;
239
+ } else {
240
+ t17 = $[42];
241
+ }
242
+ let t18;
243
+ if ($[43] !== error) {
244
+ t18 = error !== null && /* @__PURE__ */ jsxDEV("p", {
245
+ className: "text-sm text-destructive",
246
+ "data-testid": "fs-error",
247
+ role: "alert",
248
+ children: error
249
+ }, void 0, false);
250
+ $[43] = error;
251
+ $[44] = t18;
252
+ } else {
253
+ t18 = $[44];
254
+ }
255
+ t6 = /* @__PURE__ */ jsxDEV("div", {
256
+ className: "flex flex-col gap-4",
257
+ "data-testid": "lunora-function-stats",
258
+ children: [t17, t18, stats !== null && sorted.length === 0 && error === null && /* @__PURE__ */ jsxDEV(EmptyState, {
259
+ description: t("Per-function call counts, error rates, and latency land here after your first request."),
260
+ icon: /* @__PURE__ */ jsxDEV("svg", {
261
+ "aria-hidden": "true",
262
+ fill: "none",
263
+ stroke: "currentColor",
264
+ strokeLinecap: "round",
265
+ strokeLinejoin: "round",
266
+ strokeWidth: 1.6,
267
+ viewBox: "0 0 24 24",
268
+ children: [/* @__PURE__ */ jsxDEV("path", {
269
+ d: "M3 3v18h18"
270
+ }, void 0, false), /* @__PURE__ */ jsxDEV("path", {
271
+ d: "M7 14l4-4 3 3 5-6"
272
+ }, void 0, false)]
273
+ }, void 0, true),
274
+ testId: "fs-empty",
275
+ title: t("No functions have run on this shard yet.")
276
+ }, void 0, false), sorted.length > 0 && /* @__PURE__ */ jsxDEV(Card, {
277
+ className: "overflow-hidden py-0",
278
+ children: /* @__PURE__ */ jsxDEV(CardContent, {
279
+ className: "px-0",
280
+ children: /* @__PURE__ */ jsxDEV(Table, {
281
+ "data-testid": "fs-table",
282
+ children: [/* @__PURE__ */ jsxDEV(TableHeader, {
283
+ children: /* @__PURE__ */ jsxDEV(TableRow, {
284
+ children: [/* @__PURE__ */ jsxDEV(TableHead, {
285
+ children: t("function")
286
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
287
+ children: t("kind")
288
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
289
+ className: "text-right",
290
+ children: t("calls")
291
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
292
+ className: "text-right",
293
+ children: t("errors")
294
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
295
+ className: "text-right",
296
+ children: t("conflicts")
297
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
298
+ className: "text-right",
299
+ children: t("avg")
300
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
301
+ className: "text-right",
302
+ children: t("max")
303
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
304
+ children: t("last run")
305
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
306
+ children: t("last error")
307
+ }, void 0, false)]
308
+ }, void 0, true)
309
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableBody, {
310
+ children: sorted.map((stat) => {
311
+ const kind = kindByPath.get(stat.path);
312
+ return /* @__PURE__ */ jsxDEV(TableRow, {
313
+ "data-testid": `fs-row-${stat.path}`,
314
+ children: [/* @__PURE__ */ jsxDEV(TableCell, {
315
+ className: "font-medium",
316
+ children: stat.path
317
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
318
+ children: kind === void 0 ? /* @__PURE__ */ jsxDEV("span", {
319
+ className: "text-muted-foreground",
320
+ children: "—"
321
+ }, void 0, false) : /* @__PURE__ */ jsxDEV(Badge, {
322
+ variant: KIND_VARIANT[kind],
323
+ children: kind
324
+ }, void 0, false)
325
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
326
+ className: "text-right tabular-nums",
327
+ children: stat.calls
328
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
329
+ className: cn("text-right tabular-nums", stat.errors > 0 && "text-destructive"),
330
+ children: errorCell(stat)
331
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
332
+ className: cn("text-right tabular-nums", (stat.conflicts ?? 0) > 0 && "text-warning"),
333
+ children: conflictCell(stat)
334
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
335
+ className: "text-right tabular-nums",
336
+ children: stat.calls === 0 ? "—" : formatMs(stat.totalDurationMs / stat.calls)
337
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
338
+ className: "text-right tabular-nums",
339
+ children: stat.calls === 0 ? "—" : formatMs(stat.maxDurationMs)
340
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
341
+ className: "text-muted-foreground",
342
+ children: formatTimestamp(stat.lastCalledAt, "—")
343
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
344
+ className: "max-w-[24ch] truncate text-destructive",
345
+ title: stat.lastErrorMessage ?? void 0,
346
+ children: stat.lastErrorMessage ?? /* @__PURE__ */ jsxDEV("span", {
347
+ className: "text-muted-foreground",
348
+ children: "—"
349
+ }, void 0, false)
350
+ }, void 0, false)]
351
+ }, stat.path, true);
352
+ })
353
+ }, void 0, false)]
354
+ }, void 0, true)
355
+ }, void 0, false)
356
+ }, void 0, false)]
357
+ }, void 0, true);
358
+ $[8] = error;
359
+ $[9] = kindByPath;
360
+ $[10] = liveError;
361
+ $[11] = shardKey;
362
+ $[12] = sortKey;
363
+ $[13] = stats;
364
+ $[14] = t;
365
+ $[15] = t6;
366
+ } else {
367
+ t6 = $[15];
368
+ }
369
+ return t6;
370
+ };
371
+ function _temp(descriptor) {
372
+ return [descriptor.path, descriptor.kind];
373
+ }
374
+
375
+ export { FunctionStatsPanel };
@@ -0,0 +1,289 @@
1
+ import { useLunora } from '@lunora/react';
2
+ import { useState, useRef, useCallback, useEffect } from 'react';
3
+ import { E as EmptyState } from './empty-state-DY_oe0k6.js';
4
+ import { T as Table, a as TableHeader, b as TableRow, c as TableHead, d as TableBody, e as TableCell } from './table-_RzNvy3R.js';
5
+ import { b as useClientQuery } from './use-admin-query-4JQW2rr1.js';
6
+ import { useAutoRefresh } from './DEFAULT_AUTO_REFRESH_MS-Vxwaxx51.js';
7
+ import { useT } from './DEFAULT_LOCALE-CiX4W7Kr.js';
8
+ import { b as CLOUDFLARE_D1_URL } from './cf-links-2cktDedA.js';
9
+ import { u as useFacets, T as TableListSidebar, G as GridPagination, D as DataFacets } from './table-list-sidebar-Bg7uzbvC.js';
10
+ import { G as GridContainer, C as CellValue } from './data-grid-B5CaV3wS.js';
11
+ import { jsxDEV } from 'react/jsx-dev-runtime';
12
+
13
+ const DEFAULT_PAGE_SIZE = 50;
14
+ const NO_TABLES = [];
15
+ const NO_COLUMNS = [];
16
+ const rowKey = (row, index) => {
17
+ const id = row["_id"];
18
+ return typeof id === "string" || typeof id === "number" ? String(id) : `row-${index.toString()}`;
19
+ };
20
+ const chipValue = (value) => {
21
+ if (value === null || value === void 0) {
22
+ return "∅";
23
+ }
24
+ if (value === "") {
25
+ return "(empty)";
26
+ }
27
+ if (typeof value === "object") {
28
+ return JSON.stringify(value);
29
+ }
30
+ return String(value);
31
+ };
32
+ const GlobalDataBrowser = ({
33
+ initialTable,
34
+ onSelectTable,
35
+ pageSize: initialPageSize = DEFAULT_PAGE_SIZE,
36
+ schemaSwitch
37
+ } = {}) => {
38
+ const client = useLunora();
39
+ const t = useT();
40
+ const [selectedTable, setSelectedTable] = useState(initialTable ?? null);
41
+ const [offset, setOffset] = useState(0);
42
+ const [pageSize, setPageSize] = useState(initialPageSize);
43
+ const [filters, setFilters] = useState([]);
44
+ const filtersRef = useRef(filters);
45
+ filtersRef.current = filters;
46
+ const {
47
+ clearFacets,
48
+ facets,
49
+ refetchFacets: refetchFacetsViaHook,
50
+ toggleFacet
51
+ } = useFacets();
52
+ const tablesQuery = useClientQuery(["lunora-global-tables"], () => client.listGlobalTables());
53
+ const tables = tablesQuery.data ?? null;
54
+ const tablesError = tablesQuery.error;
55
+ const pageQuery = useClientQuery(["lunora-global-page", selectedTable ?? "", offset, pageSize, JSON.stringify(filters)], () => client.readGlobalTablePage({
56
+ filters,
57
+ limit: pageSize,
58
+ offset,
59
+ table: selectedTable ?? ""
60
+ }), {
61
+ enabled: selectedTable !== null,
62
+ keepPreviousData: false
63
+ });
64
+ const page = pageQuery.data ?? null;
65
+ const pageError = pageQuery.error;
66
+ const facetFetcher = (table, activeFilters) => (column) => client.facetGlobalColumn({
67
+ column,
68
+ filters: activeFilters,
69
+ table
70
+ });
71
+ const refetchFacets = (table_0, activeFilters_0) => {
72
+ refetchFacetsViaHook(facetFetcher(table_0, activeFilters_0));
73
+ };
74
+ useAutoRefresh(() => {
75
+ tablesQuery.refetch();
76
+ if (selectedTable !== null) {
77
+ pageQuery.refetch();
78
+ refetchFacets(selectedTable, filtersRef.current);
79
+ }
80
+ }, true);
81
+ const appliedInitialTable = useRef(void 0);
82
+ const selectTable = useCallback((table_1) => {
83
+ setSelectedTable(table_1);
84
+ appliedInitialTable.current = table_1;
85
+ setFilters([]);
86
+ filtersRef.current = [];
87
+ clearFacets();
88
+ setOffset(0);
89
+ onSelectTable?.(table_1);
90
+ }, [clearFacets, onSelectTable]);
91
+ const onToggleFacet = (column_0) => {
92
+ toggleFacet(column_0, selectedTable === null ? null : facetFetcher(selectedTable, filtersRef.current));
93
+ };
94
+ const applyFilters = (next) => {
95
+ setFilters(next);
96
+ filtersRef.current = next;
97
+ setOffset(0);
98
+ if (selectedTable !== null) {
99
+ refetchFacets(selectedTable, next);
100
+ }
101
+ };
102
+ const onFacetFilter = (column_1, value) => {
103
+ applyFilters([...filtersRef.current.filter((clause) => clause.column !== column_1), {
104
+ column: column_1,
105
+ value
106
+ }]);
107
+ };
108
+ const removeFilter = (event) => {
109
+ const index = Number(event.currentTarget.dataset["index"]);
110
+ applyFilters(filtersRef.current.filter((_, position) => position !== index));
111
+ };
112
+ useEffect(() => {
113
+ if (initialTable === void 0 || initialTable === "" || appliedInitialTable.current === initialTable) {
114
+ return;
115
+ }
116
+ appliedInitialTable.current = initialTable;
117
+ const target = initialTable;
118
+ queueMicrotask(() => {
119
+ selectTable(target);
120
+ });
121
+ }, [initialTable, selectTable]);
122
+ const goToPage = (nextOffset) => {
123
+ if (selectedTable === null) {
124
+ return;
125
+ }
126
+ setOffset(Math.max(0, nextOffset));
127
+ };
128
+ const total = page?.total ?? 0;
129
+ const hasPrevious = offset > 0;
130
+ const hasNext = page !== null && offset + page.rows.length < total;
131
+ const rangeStart = page === null || page.rows.length === 0 ? 0 : offset + 1;
132
+ const rangeEnd = page === null ? 0 : offset + page.rows.length;
133
+ const reloadTables = () => {
134
+ tablesQuery.refetch();
135
+ };
136
+ const goPrevious = () => {
137
+ goToPage(offset - pageSize);
138
+ };
139
+ const goNext = () => {
140
+ goToPage(offset + pageSize);
141
+ };
142
+ const jumpToPage = (targetPage) => {
143
+ goToPage(Math.max(0, (targetPage - 1) * pageSize));
144
+ };
145
+ const changePageSize = (size) => {
146
+ setPageSize(size);
147
+ setOffset(0);
148
+ };
149
+ return /* @__PURE__ */ jsxDEV("div", {
150
+ className: "flex h-full min-w-0",
151
+ "data-testid": "lunora-global-data-browser",
152
+ children: [/* @__PURE__ */ jsxDEV(TableListSidebar, {
153
+ header: /* @__PURE__ */ jsxDEV("div", {
154
+ className: "flex shrink-0 flex-col items-stretch gap-2 border-b border-border p-3",
155
+ children: [schemaSwitch, /* @__PURE__ */ jsxDEV("a", {
156
+ className: "text-sm text-primary underline-offset-4 hover:underline",
157
+ "data-testid": "gdb-cf-link",
158
+ href: CLOUDFLARE_D1_URL,
159
+ rel: "noreferrer",
160
+ target: "_blank",
161
+ children: t("Open in Cloudflare")
162
+ }, void 0, false)]
163
+ }, void 0, true),
164
+ onReload: reloadTables,
165
+ onSelect: selectTable,
166
+ prefix: "gdb",
167
+ selected: selectedTable,
168
+ tables: tables ?? NO_TABLES
169
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
170
+ className: "flex min-w-0 flex-1 flex-col overflow-hidden",
171
+ children: [tablesError !== null && /* @__PURE__ */ jsxDEV("p", {
172
+ className: "shrink-0 border-b border-border px-4 py-2 text-sm text-destructive",
173
+ "data-testid": "gdb-tables-error",
174
+ role: "alert",
175
+ children: tablesError
176
+ }, void 0, false), tables !== null && tables.length === 0 && /* @__PURE__ */ jsxDEV("div", {
177
+ className: "flex flex-1 items-center justify-center p-6",
178
+ children: /* @__PURE__ */ jsxDEV(EmptyState, {
179
+ description: t("Tables marked .global() (D1-backed, region-replicated) will appear here."),
180
+ icon: /* @__PURE__ */ jsxDEV("svg", {
181
+ "aria-hidden": "true",
182
+ fill: "none",
183
+ stroke: "currentColor",
184
+ strokeLinecap: "round",
185
+ strokeLinejoin: "round",
186
+ strokeWidth: 1.6,
187
+ viewBox: "0 0 24 24",
188
+ children: [/* @__PURE__ */ jsxDEV("circle", {
189
+ cx: "12",
190
+ cy: "12",
191
+ r: "9"
192
+ }, void 0, false), /* @__PURE__ */ jsxDEV("path", {
193
+ d: "M3 12h18M12 3a14 14 0 0 1 0 18 14 14 0 0 1 0-18Z"
194
+ }, void 0, false)]
195
+ }, void 0, true),
196
+ testId: "gdb-empty",
197
+ title: t("No global tables.")
198
+ }, void 0, false)
199
+ }, void 0, false), pageError !== null && /* @__PURE__ */ jsxDEV("p", {
200
+ className: "shrink-0 border-b border-border px-4 py-2 text-sm text-destructive",
201
+ "data-testid": "gdb-page-error",
202
+ role: "alert",
203
+ children: pageError
204
+ }, void 0, false), tables !== null && tables.length > 0 && page === null && pageError === null && /* @__PURE__ */ jsxDEV("div", {
205
+ className: "flex flex-1 items-center justify-center p-6",
206
+ children: /* @__PURE__ */ jsxDEV("p", {
207
+ className: "text-sm text-muted-foreground",
208
+ children: t("Select a table to browse its rows.")
209
+ }, void 0, false)
210
+ }, void 0, false), page !== null && /* @__PURE__ */ jsxDEV("div", {
211
+ className: "flex min-h-0 flex-1 flex-col",
212
+ "data-testid": "gdb-page",
213
+ children: [filters.length > 0 && /* @__PURE__ */ jsxDEV("div", {
214
+ className: "flex shrink-0 flex-wrap items-center gap-1.5 border-b border-border px-4 py-2 text-xs",
215
+ "data-testid": "gdb-filters",
216
+ children: filters.map((filter, index_0) => /* @__PURE__ */ jsxDEV("span", {
217
+ className: "inline-flex items-center gap-1 rounded border border-border bg-muted px-1.5 py-0.5",
218
+ "data-testid": "gdb-filter-chip",
219
+ children: [/* @__PURE__ */ jsxDEV("span", {
220
+ className: "font-medium text-foreground",
221
+ children: filter.column
222
+ }, void 0, false), /* @__PURE__ */ jsxDEV("span", {
223
+ className: "text-muted-foreground",
224
+ children: "="
225
+ }, void 0, false), /* @__PURE__ */ jsxDEV("span", {
226
+ className: "font-mono text-foreground",
227
+ children: chipValue(filter.value)
228
+ }, void 0, false), /* @__PURE__ */ jsxDEV("button", {
229
+ "aria-label": t("Remove filter"),
230
+ className: "ml-0.5 text-muted-foreground hover:text-foreground",
231
+ "data-index": index_0,
232
+ "data-testid": "gdb-filter-remove",
233
+ onClick: removeFilter,
234
+ type: "button",
235
+ children: "✕"
236
+ }, void 0, false)]
237
+ }, `${filter.column}:${chipValue(filter.value)}`, true))
238
+ }, void 0, false), /* @__PURE__ */ jsxDEV(GridContainer, {
239
+ layout: "fill",
240
+ children: /* @__PURE__ */ jsxDEV("div", {
241
+ className: "min-h-0 flex-1 overflow-auto",
242
+ children: /* @__PURE__ */ jsxDEV(Table, {
243
+ "data-testid": "gdb-rows",
244
+ children: [/* @__PURE__ */ jsxDEV(TableHeader, {
245
+ children: /* @__PURE__ */ jsxDEV(TableRow, {
246
+ children: page.columns.map((column_2) => /* @__PURE__ */ jsxDEV(TableHead, {
247
+ children: column_2
248
+ }, column_2, false))
249
+ }, void 0, false)
250
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableBody, {
251
+ children: page.rows.map((row, rowIndex) => /* @__PURE__ */ jsxDEV(TableRow, {
252
+ "data-testid": "gdb-row",
253
+ children: page.columns.map((column_3) => /* @__PURE__ */ jsxDEV(TableCell, {
254
+ className: "max-w-xs truncate font-mono text-xs",
255
+ children: /* @__PURE__ */ jsxDEV(CellValue, {
256
+ value: row[column_3]
257
+ }, void 0, false)
258
+ }, column_3, false))
259
+ }, rowKey(row, rowIndex), false))
260
+ }, void 0, false)]
261
+ }, void 0, true)
262
+ }, void 0, false)
263
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
264
+ className: "flex shrink-0 items-center border-t border-border px-4 py-2",
265
+ children: /* @__PURE__ */ jsxDEV(GridPagination, {
266
+ hasNext,
267
+ hasPrevious,
268
+ onJumpToPage: jumpToPage,
269
+ onNext: goNext,
270
+ onPageSizeChange: changePageSize,
271
+ onPrevious: goPrevious,
272
+ pageSize,
273
+ prefix: "gdb",
274
+ rangeEnd,
275
+ rangeStart,
276
+ total
277
+ }, void 0, false)
278
+ }, void 0, false)]
279
+ }, void 0, true)]
280
+ }, void 0, true), /* @__PURE__ */ jsxDEV(DataFacets, {
281
+ columns: page?.columns ?? NO_COLUMNS,
282
+ facets,
283
+ onFacetFilter,
284
+ onToggleFacet
285
+ }, void 0, false)]
286
+ }, void 0, true);
287
+ };
288
+
289
+ export { GlobalDataBrowser };