@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,246 @@
1
+ import { useLunora } from '@lunora/react';
2
+ import { useState } from 'react';
3
+ import { ConfirmButton } from './ConfirmButton-WQVUoGFb.js';
4
+ import { L as LiveError } from './live-status-bPff1O7Y.js';
5
+ import { ShardInput } from './ShardInput-DNCsT1KW.js';
6
+ import { B as Badge } from './badge-B2PKA1-5.js';
7
+ import { B as Button } from './button-BhsN2uZH.js';
8
+ import { C as Card, a as CardContent } from './card-DURq3ElK.js';
9
+ import { E as EmptyState } from './empty-state-DY_oe0k6.js';
10
+ import { I as Input } from './input-XH4r1Pt1.js';
11
+ import { L as Label } from './label-D8ykjn5J.js';
12
+ import { T as Table, a as TableHeader, b as TableRow, c as TableHead, d as TableBody, e as TableCell } from './table-_RzNvy3R.js';
13
+ import { u as useLiveAdmin } from './use-live-admin-D1h1Fzsd.js';
14
+ import { useT } from './createStudioI18n-CgvlmDkN.js';
15
+ import { ADMIN_FUNCTIONS } from './ADMIN_FUNCTION_PREFIX-DmBqMZ-z.js';
16
+ import { d as formatTimestamp, f as fireAndForget, c as callOptions, e as errorMessage, a as adminRef } from './internal-BBZYexre.js';
17
+ import { r as recordShard } from './shard-history-DyebH1R5.js';
18
+ import { u as useLiveShardSeed } from './use-live-shard-seed-B74RYcOy.js';
19
+ import { jsxDEV } from 'react/jsx-dev-runtime';
20
+
21
+ const MIGRATION_STATUS = adminRef(ADMIN_FUNCTIONS.migrationStatus);
22
+ const RUN_MIGRATION = adminRef(ADMIN_FUNCTIONS.runMigration);
23
+ const MigrationsPanel = ({
24
+ initialShardKey
25
+ }) => {
26
+ const client = useLunora();
27
+ const t = useT();
28
+ const [shardKey, setShardKey] = useState(initialShardKey ?? "");
29
+ const [rows, setRows] = useState(null);
30
+ const [statusError, setStatusError] = useState(null);
31
+ const [liveError, setLiveError] = useState(void 0);
32
+ const [migrationId, setMigrationId] = useState("");
33
+ const [direction, setDirection] = useState("up");
34
+ const [dryRun, setDryRun] = useState(true);
35
+ const [running, setRunning] = useState(false);
36
+ const [runResult, setRunResult] = useState(null);
37
+ const [runError, setRunError] = useState(null);
38
+ const refresh = async (shard) => {
39
+ setStatusError(null);
40
+ try {
41
+ const result = await client.query(MIGRATION_STATUS, {}, callOptions(shard));
42
+ recordShard(shard);
43
+ setRows(result.migrations);
44
+ } catch (error) {
45
+ setRows(null);
46
+ setStatusError(errorMessage(error));
47
+ throw error;
48
+ }
49
+ };
50
+ const committedShard = useLiveShardSeed(shardKey, refresh);
51
+ useLiveAdmin(ADMIN_FUNCTIONS.migrationStatus, {}, committedShard ?? "", (result_0) => {
52
+ setStatusError(null);
53
+ setLiveError(void 0);
54
+ setRows(result_0.migrations);
55
+ }, committedShard !== void 0, setLiveError);
56
+ const run = async () => {
57
+ const id = migrationId.trim();
58
+ if (id === "") {
59
+ setRunError(t("Enter a migration id"));
60
+ setRunResult(null);
61
+ return;
62
+ }
63
+ setRunning(true);
64
+ setRunError(null);
65
+ try {
66
+ const result_1 = await client.query(RUN_MIGRATION, {
67
+ direction,
68
+ dryRun,
69
+ id
70
+ }, callOptions(shardKey));
71
+ setRunResult(result_1);
72
+ await refresh(shardKey).catch(() => {
73
+ });
74
+ } catch (error_0) {
75
+ setRunResult(null);
76
+ setRunError(errorMessage(error_0));
77
+ } finally {
78
+ setRunning(false);
79
+ }
80
+ };
81
+ const runMigration = () => {
82
+ fireAndForget(run());
83
+ };
84
+ const onIdChange = (event) => {
85
+ setMigrationId(event.target.value);
86
+ };
87
+ const onDirectionChange = (event_0) => {
88
+ setDirection(event_0.target.value === "down" ? "down" : "up");
89
+ };
90
+ const onDryRunChange = (event_1) => {
91
+ setDryRun(event_1.target.checked);
92
+ };
93
+ return /* @__PURE__ */ jsxDEV("div", {
94
+ className: "flex flex-col gap-3",
95
+ "data-testid": "lunora-migrations",
96
+ children: [/* @__PURE__ */ jsxDEV("div", {
97
+ className: "flex flex-wrap items-center gap-2",
98
+ children: [/* @__PURE__ */ jsxDEV(ShardInput, {
99
+ onChange: setShardKey,
100
+ testId: "mg-shard-input",
101
+ value: shardKey
102
+ }, void 0, false), /* @__PURE__ */ jsxDEV(LiveError, {
103
+ message: liveError,
104
+ prefix: "mg"
105
+ }, void 0, false)]
106
+ }, void 0, true), statusError !== null && /* @__PURE__ */ jsxDEV("p", {
107
+ className: "text-sm text-destructive",
108
+ "data-testid": "mg-status-error",
109
+ role: "alert",
110
+ children: statusError
111
+ }, void 0, false), rows !== null && rows.length === 0 && /* @__PURE__ */ jsxDEV(EmptyState, {
112
+ description: t("Data migrations you run against this shard will be tracked here."),
113
+ icon: /* @__PURE__ */ jsxDEV("svg", {
114
+ "aria-hidden": "true",
115
+ fill: "none",
116
+ stroke: "currentColor",
117
+ strokeLinecap: "round",
118
+ strokeLinejoin: "round",
119
+ strokeWidth: 1.6,
120
+ viewBox: "0 0 24 24",
121
+ children: /* @__PURE__ */ jsxDEV("path", {
122
+ d: "M4 12a8 8 0 0 1 13.7-5.6L20 8M20 4v4h-4M20 12a8 8 0 0 1-13.7 5.6L4 16m0 4v-4h4"
123
+ }, void 0, false)
124
+ }, void 0, false),
125
+ testId: "mg-empty",
126
+ title: t("No migrations have run on this shard.")
127
+ }, void 0, false), rows !== null && rows.length > 0 && /* @__PURE__ */ jsxDEV(Card, {
128
+ className: "overflow-hidden py-0",
129
+ children: /* @__PURE__ */ jsxDEV(CardContent, {
130
+ className: "px-0",
131
+ children: /* @__PURE__ */ jsxDEV(Table, {
132
+ "data-testid": "mg-table",
133
+ children: [/* @__PURE__ */ jsxDEV(TableHeader, {
134
+ children: /* @__PURE__ */ jsxDEV(TableRow, {
135
+ children: [/* @__PURE__ */ jsxDEV(TableHead, {
136
+ children: t("id")
137
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
138
+ children: t("direction")
139
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
140
+ children: t("status")
141
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
142
+ children: t("processed")
143
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
144
+ children: t("changed")
145
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
146
+ children: t("updated")
147
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableHead, {
148
+ children: t("error")
149
+ }, void 0, false)]
150
+ }, void 0, true)
151
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableBody, {
152
+ children: rows.map((row) => /* @__PURE__ */ jsxDEV(TableRow, {
153
+ "data-testid": `mg-row-${row.id}`,
154
+ children: [/* @__PURE__ */ jsxDEV(TableCell, {
155
+ children: row.id
156
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
157
+ children: row.direction
158
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
159
+ children: /* @__PURE__ */ jsxDEV(Badge, {
160
+ variant: "secondary",
161
+ children: row.status
162
+ }, void 0, false)
163
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
164
+ children: row.processed
165
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
166
+ children: row.changed
167
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
168
+ children: formatTimestamp(row.updatedAt)
169
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableCell, {
170
+ children: row.error ?? ""
171
+ }, void 0, false)]
172
+ }, row.id, true))
173
+ }, void 0, false)]
174
+ }, void 0, true)
175
+ }, void 0, false)
176
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
177
+ className: "flex flex-col gap-2 rounded-xl border border-border bg-card p-3 shadow-xs",
178
+ children: [/* @__PURE__ */ jsxDEV(Label, {
179
+ children: t("Run migration")
180
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
181
+ className: "flex flex-wrap items-center gap-2",
182
+ children: [/* @__PURE__ */ jsxDEV(Input, {
183
+ "aria-label": t("Migration id"),
184
+ className: "w-auto",
185
+ "data-testid": "mg-id-input",
186
+ onChange: onIdChange,
187
+ placeholder: t("migration id"),
188
+ value: migrationId
189
+ }, void 0, false), /* @__PURE__ */ jsxDEV("select", {
190
+ "aria-label": t("Direction"),
191
+ className: "flex h-9 rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
192
+ "data-testid": "mg-direction",
193
+ onChange: onDirectionChange,
194
+ value: direction,
195
+ children: [/* @__PURE__ */ jsxDEV("option", {
196
+ value: "up",
197
+ children: t("up")
198
+ }, void 0, false), /* @__PURE__ */ jsxDEV("option", {
199
+ value: "down",
200
+ children: t("down")
201
+ }, void 0, false)]
202
+ }, void 0, true), /* @__PURE__ */ jsxDEV(Label, {
203
+ className: "flex items-center gap-1.5",
204
+ htmlFor: "mg-dry-run",
205
+ children: [/* @__PURE__ */ jsxDEV("input", {
206
+ checked: dryRun,
207
+ className: "size-4 accent-primary",
208
+ "data-testid": "mg-dry-run",
209
+ id: "mg-dry-run",
210
+ onChange: onDryRunChange,
211
+ type: "checkbox"
212
+ }, void 0, false), t("Dry run")]
213
+ }, void 0, true), dryRun ? /* @__PURE__ */ jsxDEV(Button, {
214
+ "data-testid": "mg-run",
215
+ disabled: running,
216
+ onClick: runMigration,
217
+ size: "sm",
218
+ type: "button",
219
+ children: running ? t("Running…") : t("Run")
220
+ }, void 0, false) : (
221
+ // A real (non-dry-run) migration mutates rows — guard it.
222
+ /* @__PURE__ */ jsxDEV(ConfirmButton, {
223
+ confirmLabel: running ? t("Running…") : t("Run migration?"),
224
+ disabled: running,
225
+ onConfirm: runMigration,
226
+ testId: "mg-run",
227
+ children: t("Run")
228
+ }, void 0, false)
229
+ )]
230
+ }, void 0, true)]
231
+ }, void 0, true), runError !== null && /* @__PURE__ */ jsxDEV("pre", {
232
+ className: "text-sm text-destructive whitespace-pre-wrap",
233
+ "data-testid": "mg-run-error",
234
+ role: "alert",
235
+ children: runError
236
+ }, void 0, false), runResult !== null && /* @__PURE__ */ jsxDEV("p", {
237
+ className: "text-sm text-muted-foreground",
238
+ "data-testid": "mg-run-result",
239
+ children: [runResult.dryRun ? t("Dry run: ") : "", t("{status} — processed", {
240
+ status: runResult.status
241
+ }), runResult.processed, t(", changed"), runResult.changed]
242
+ }, void 0, true)]
243
+ }, void 0, true);
244
+ };
245
+
246
+ export { MigrationsPanel };
@@ -0,0 +1,191 @@
1
+ import { c } from 'react/compiler-runtime';
2
+ import { useLunora } from '@lunora/react';
3
+ import { E as EmptyState } from './empty-state-DY_oe0k6.js';
4
+ import { u as useAdminSpec, S as Skeleton, R as ReferenceView } from './reference-view-BCKIoai7.js';
5
+ import { useT } from './createStudioI18n-CgvlmDkN.js';
6
+ import { jsxDEV } from 'react/jsx-dev-runtime';
7
+
8
+ const argsParameterOf = (method) => (method.params ?? []).find((parameter) => parameter.name === "args") ?? (method.params ?? [])[0];
9
+ const namespaceOf = (method) => method["x-tags"]?.[0]?.name ?? (method.name.includes(":") ? method.name.slice(0, method.name.indexOf(":")) : "");
10
+ const toOperation = (method) => {
11
+ const argsSchema = argsParameterOf(method)?.schema;
12
+ return {
13
+ argsSchema,
14
+ description: method.description,
15
+ functionPath: method.name,
16
+ httpPath: "/_lunora/rpc",
17
+ key: method.name,
18
+ kind: method["x-lunora-function-kind"],
19
+ method: "POST",
20
+ operationId: method.name,
21
+ requestSchema: argsSchema,
22
+ responses: [{
23
+ description: method.result?.name,
24
+ schema: method.result?.schema,
25
+ status: "200"
26
+ }],
27
+ summary: method.summary ?? method.name,
28
+ tags: [namespaceOf(method)],
29
+ title: method.summary ?? method.name
30
+ };
31
+ };
32
+ const parseOpenRpc = (raw) => {
33
+ const document = raw ?? {};
34
+ const operations = (document.methods ?? []).map((method) => toOperation(method));
35
+ const byNamespace = /* @__PURE__ */ new Map();
36
+ for (const operation of operations) {
37
+ const namespace = operation.tags[0] ?? "";
38
+ const bucket = byNamespace.get(namespace) ?? [];
39
+ bucket.push(operation);
40
+ byNamespace.set(namespace, bucket);
41
+ }
42
+ const groups = [...byNamespace.entries()].map(([name, members]) => {
43
+ return {
44
+ name,
45
+ operations: members.toSorted((a, b) => a.operationId.localeCompare(b.operationId))
46
+ };
47
+ }).toSorted((a, b) => a.name.localeCompare(b.name));
48
+ return {
49
+ groups,
50
+ operationByKey: new Map(operations.map((operation) => [operation.key, operation])),
51
+ title: document.info?.title ?? "API reference",
52
+ version: document.info?.version
53
+ };
54
+ };
55
+
56
+ const isEmptyDocument = (document) => document.methods === void 0 || document.methods.length === 0;
57
+ const classifyDocument = (spec) => {
58
+ const document = spec;
59
+ return isEmptyDocument(document) ? {
60
+ kind: "empty"
61
+ } : {
62
+ kind: "ready",
63
+ spec: document
64
+ };
65
+ };
66
+ const OpenRpcReferencePanel = (t0) => {
67
+ const $ = c(23);
68
+ const {
69
+ spec: inlineSpec
70
+ } = t0;
71
+ const t = useT();
72
+ const client = useLunora();
73
+ let t1;
74
+ if ($[0] !== client) {
75
+ t1 = () => client.fetchOpenRpc();
76
+ $[0] = client;
77
+ $[1] = t1;
78
+ } else {
79
+ t1 = $[1];
80
+ }
81
+ const fetchOpenRpc = t1;
82
+ const state = useAdminSpec(inlineSpec, fetchOpenRpc, classifyDocument);
83
+ let t2;
84
+ if ($[2] !== state.kind || $[3] !== state.spec) {
85
+ t2 = state.kind === "ready" ? parseOpenRpc(state.spec) : void 0;
86
+ $[2] = state.kind;
87
+ $[3] = state.spec;
88
+ $[4] = t2;
89
+ } else {
90
+ t2 = $[4];
91
+ }
92
+ const model = t2;
93
+ if (state.kind === "loading") {
94
+ let t32;
95
+ if ($[5] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
96
+ t32 = /* @__PURE__ */ jsxDEV("div", {
97
+ className: "flex flex-col gap-4",
98
+ "data-testid": "openrpc-reference-loading",
99
+ children: [/* @__PURE__ */ jsxDEV(Skeleton, {
100
+ className: "h-8 w-48"
101
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Skeleton, {
102
+ className: "h-64 w-full"
103
+ }, void 0, false)]
104
+ }, void 0, true);
105
+ $[5] = t32;
106
+ } else {
107
+ t32 = $[5];
108
+ }
109
+ return t32;
110
+ }
111
+ if (state.kind === "error") {
112
+ let t32;
113
+ if ($[6] !== state.message || $[7] !== t) {
114
+ t32 = t("Couldn't load the OpenRPC spec: {message}", {
115
+ message: state.message
116
+ });
117
+ $[6] = state.message;
118
+ $[7] = t;
119
+ $[8] = t32;
120
+ } else {
121
+ t32 = $[8];
122
+ }
123
+ let t4;
124
+ if ($[9] !== t) {
125
+ t4 = t("API reference unavailable");
126
+ $[9] = t;
127
+ $[10] = t4;
128
+ } else {
129
+ t4 = $[10];
130
+ }
131
+ let t5;
132
+ if ($[11] !== t32 || $[12] !== t4) {
133
+ t5 = /* @__PURE__ */ jsxDEV(EmptyState, {
134
+ description: t32,
135
+ testId: "openrpc-reference-error",
136
+ title: t4
137
+ }, void 0, false);
138
+ $[11] = t32;
139
+ $[12] = t4;
140
+ $[13] = t5;
141
+ } else {
142
+ t5 = $[13];
143
+ }
144
+ return t5;
145
+ }
146
+ if (state.kind === "empty" || model === void 0) {
147
+ let t32;
148
+ if ($[14] !== t) {
149
+ t32 = t("Run `lunora codegen --api-spec openrpc` and wire `_generated/openrpc.json` to the worker to render the OpenRPC reference here.");
150
+ $[14] = t;
151
+ $[15] = t32;
152
+ } else {
153
+ t32 = $[15];
154
+ }
155
+ let t4;
156
+ if ($[16] !== t) {
157
+ t4 = t("No OpenRPC spec configured");
158
+ $[16] = t;
159
+ $[17] = t4;
160
+ } else {
161
+ t4 = $[17];
162
+ }
163
+ let t5;
164
+ if ($[18] !== t32 || $[19] !== t4) {
165
+ t5 = /* @__PURE__ */ jsxDEV(EmptyState, {
166
+ description: t32,
167
+ testId: "openrpc-reference-empty",
168
+ title: t4
169
+ }, void 0, false);
170
+ $[18] = t32;
171
+ $[19] = t4;
172
+ $[20] = t5;
173
+ } else {
174
+ t5 = $[20];
175
+ }
176
+ return t5;
177
+ }
178
+ let t3;
179
+ if ($[21] !== model) {
180
+ t3 = /* @__PURE__ */ jsxDEV(ReferenceView, {
181
+ model
182
+ }, void 0, false);
183
+ $[21] = model;
184
+ $[22] = t3;
185
+ } else {
186
+ t3 = $[22];
187
+ }
188
+ return t3;
189
+ };
190
+
191
+ export { OpenRpcReferencePanel as default };
@@ -0,0 +1,252 @@
1
+ import { useLunora } from '@lunora/react';
2
+ import { useState, useCallback, useEffect } from 'react';
3
+ import { ConfirmButton } from './ConfirmButton-WQVUoGFb.js';
4
+ import { B as Badge } from './badge-B2PKA1-5.js';
5
+ import { B as Button } from './button-BhsN2uZH.js';
6
+ import { I as Input } from './input-XH4r1Pt1.js';
7
+ import { L as Label } from './label-D8ykjn5J.js';
8
+ import { useAutoRefresh } from './DEFAULT_AUTO_REFRESH_MS-Vxwaxx51.js';
9
+ import { useT } from './createStudioI18n-CgvlmDkN.js';
10
+ import { ADMIN_FUNCTIONS } from './ADMIN_FUNCTION_PREFIX-DmBqMZ-z.js';
11
+ import { c as callOptions, e as errorMessage, f as fireAndForget, a as adminRef } from './internal-BBZYexre.js';
12
+ import { jsxDEV } from 'react/jsx-dev-runtime';
13
+
14
+ const GET_BOOKMARK = adminRef(ADMIN_FUNCTIONS.getPitrBookmark);
15
+ const PITR_RESTORE = adminRef(ADMIN_FUNCTIONS.pitrRestore);
16
+ const PitrPanel = ({
17
+ initialShardKey
18
+ }) => {
19
+ const client = useLunora();
20
+ const t = useT();
21
+ const shardKey = initialShardKey ?? "";
22
+ const [current, setCurrent] = useState(null);
23
+ const [time, setTime] = useState("");
24
+ const [preview, setPreview] = useState(null);
25
+ const [bookmark, setBookmark] = useState("");
26
+ const [restart, setRestart] = useState(false);
27
+ const [restored, setRestored] = useState(null);
28
+ const [error, setError] = useState(null);
29
+ const [busy, setBusy] = useState(false);
30
+ const refresh = useCallback(async () => {
31
+ setBusy(true);
32
+ setError(null);
33
+ try {
34
+ const result = await client.query(GET_BOOKMARK, {}, callOptions(shardKey));
35
+ setCurrent(result.current);
36
+ } catch (error_) {
37
+ setError(errorMessage(error_));
38
+ } finally {
39
+ setBusy(false);
40
+ }
41
+ }, [client, shardKey]);
42
+ useEffect(() => {
43
+ fireAndForget(refresh());
44
+ }, [refresh]);
45
+ useAutoRefresh(() => {
46
+ fireAndForget((async () => {
47
+ try {
48
+ const result_0 = await client.query(GET_BOOKMARK, {}, callOptions(shardKey));
49
+ setCurrent(result_0.current);
50
+ } catch {
51
+ }
52
+ })());
53
+ }, true);
54
+ const onTimeChange = (event) => {
55
+ setTime(event.target.value);
56
+ };
57
+ const onBookmarkChange = (event_0) => {
58
+ setBookmark(event_0.target.value);
59
+ };
60
+ const onRestartChange = (event_1) => {
61
+ setRestart(event_1.target.checked);
62
+ };
63
+ const onPreview = () => {
64
+ if (time.trim() === "") {
65
+ return;
66
+ }
67
+ fireAndForget((async () => {
68
+ setBusy(true);
69
+ setError(null);
70
+ try {
71
+ const result_1 = await client.query(GET_BOOKMARK, {
72
+ time: time.trim()
73
+ }, callOptions(shardKey));
74
+ setPreview(result_1.forTime ?? null);
75
+ setCurrent(result_1.current);
76
+ } catch (error__0) {
77
+ setPreview(null);
78
+ setError(errorMessage(error__0));
79
+ } finally {
80
+ setBusy(false);
81
+ }
82
+ })());
83
+ };
84
+ const runRestore = (args) => {
85
+ fireAndForget((async () => {
86
+ setBusy(true);
87
+ setError(null);
88
+ try {
89
+ const result_2 = await client.mutation(PITR_RESTORE, args, callOptions(shardKey));
90
+ setRestored(result_2);
91
+ await refresh();
92
+ } catch (error__1) {
93
+ setError(errorMessage(error__1));
94
+ } finally {
95
+ setBusy(false);
96
+ }
97
+ })());
98
+ };
99
+ const canRestore = bookmark.trim() !== "" || time.trim() !== "";
100
+ const onConfirmRestore = () => {
101
+ const args_0 = restart ? {
102
+ restart: true
103
+ } : {};
104
+ if (bookmark.trim() === "") {
105
+ args_0.time = time.trim();
106
+ } else {
107
+ args_0.bookmark = bookmark.trim();
108
+ }
109
+ runRestore(args_0);
110
+ };
111
+ const onUndo = () => {
112
+ if (restored === null) {
113
+ return;
114
+ }
115
+ runRestore(restart ? {
116
+ bookmark: restored.undoBookmark,
117
+ restart: true
118
+ } : {
119
+ bookmark: restored.undoBookmark
120
+ });
121
+ };
122
+ return /* @__PURE__ */ jsxDEV("div", {
123
+ className: "flex flex-col gap-4",
124
+ "data-testid": "lunora-pitr",
125
+ children: [/* @__PURE__ */ jsxDEV("div", {
126
+ className: "flex flex-wrap items-center gap-3",
127
+ children: /* @__PURE__ */ jsxDEV("span", {
128
+ className: "text-xs text-muted-foreground",
129
+ children: [t("Shard"), ": ", /* @__PURE__ */ jsxDEV("span", {
130
+ className: "font-mono",
131
+ children: shardKey === "" ? t("root") : shardKey
132
+ }, void 0, false)]
133
+ }, void 0, true)
134
+ }, void 0, false), /* @__PURE__ */ jsxDEV("p", {
135
+ className: "text-sm text-muted-foreground",
136
+ "data-testid": "pitr-note",
137
+ children: t("In-place recovery to any moment in the last 30 days. For older or portable recovery, use the snapshot backup tier.")
138
+ }, void 0, false), error !== null && /* @__PURE__ */ jsxDEV("p", {
139
+ className: "text-sm text-destructive",
140
+ "data-testid": "pitr-error",
141
+ role: "alert",
142
+ children: error
143
+ }, void 0, false), /* @__PURE__ */ jsxDEV("section", {
144
+ className: "rounded-xl border border-border bg-card p-3 shadow-xs",
145
+ children: [/* @__PURE__ */ jsxDEV("h3", {
146
+ className: "mb-2 text-sm font-semibold",
147
+ children: t("Current bookmark")
148
+ }, void 0, false), /* @__PURE__ */ jsxDEV("p", {
149
+ className: "font-mono text-xs break-all text-muted-foreground",
150
+ "data-testid": "pitr-current",
151
+ children: current ?? /* @__PURE__ */ jsxDEV("span", {
152
+ className: "text-muted-foreground",
153
+ children: "—"
154
+ }, void 0, false)
155
+ }, void 0, false)]
156
+ }, void 0, true), /* @__PURE__ */ jsxDEV("section", {
157
+ className: "flex flex-col gap-3 rounded-xl border border-border bg-card p-3 shadow-xs",
158
+ children: [/* @__PURE__ */ jsxDEV("h3", {
159
+ className: "text-sm font-semibold",
160
+ children: t("Restore")
161
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
162
+ className: "flex flex-col gap-1",
163
+ children: [/* @__PURE__ */ jsxDEV(Label, {
164
+ htmlFor: "pitr-time",
165
+ children: t("Time (ISO or epoch-ms, last 30 days)")
166
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
167
+ className: "flex items-center gap-2",
168
+ children: [/* @__PURE__ */ jsxDEV(Input, {
169
+ "data-testid": "pitr-time",
170
+ id: "pitr-time",
171
+ onChange: onTimeChange,
172
+ placeholder: "2026-06-01T00:00:00.000Z",
173
+ value: time
174
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Button, {
175
+ "data-testid": "pitr-preview",
176
+ disabled: busy || time.trim() === "",
177
+ onClick: onPreview,
178
+ size: "sm",
179
+ type: "button",
180
+ variant: "outline",
181
+ children: t("Preview")
182
+ }, void 0, false)]
183
+ }, void 0, true), preview !== null && /* @__PURE__ */ jsxDEV("p", {
184
+ className: "font-mono text-xs break-all text-muted-foreground",
185
+ "data-testid": "pitr-preview-bookmark",
186
+ children: [t("Bookmark for that time"), ": ", preview]
187
+ }, void 0, true)]
188
+ }, void 0, true), /* @__PURE__ */ jsxDEV("div", {
189
+ className: "flex flex-col gap-1",
190
+ children: [/* @__PURE__ */ jsxDEV(Label, {
191
+ htmlFor: "pitr-bookmark",
192
+ children: t("Or an explicit bookmark (wins over time)")
193
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
194
+ "data-testid": "pitr-bookmark",
195
+ id: "pitr-bookmark",
196
+ onChange: onBookmarkChange,
197
+ placeholder: t("bookmark string"),
198
+ value: bookmark
199
+ }, void 0, false)]
200
+ }, void 0, true), /* @__PURE__ */ jsxDEV(Label, {
201
+ className: "flex items-center gap-2 text-xs font-normal text-muted-foreground",
202
+ htmlFor: "pitr-restart",
203
+ children: [/* @__PURE__ */ jsxDEV("input", {
204
+ checked: restart,
205
+ className: "size-4 accent-primary",
206
+ "data-testid": "pitr-restart",
207
+ id: "pitr-restart",
208
+ onChange: onRestartChange,
209
+ type: "checkbox"
210
+ }, void 0, false), t("Restart the shard now so recovery applies immediately")]
211
+ }, void 0, true), /* @__PURE__ */ jsxDEV("div", {
212
+ children: /* @__PURE__ */ jsxDEV(ConfirmButton, {
213
+ confirmLabel: t("Confirm restore"),
214
+ disabled: busy || !canRestore,
215
+ onConfirm: onConfirmRestore,
216
+ testId: "pitr-restore",
217
+ children: t("Restore")
218
+ }, void 0, false)
219
+ }, void 0, false)]
220
+ }, void 0, true), restored !== null && /* @__PURE__ */ jsxDEV("section", {
221
+ className: "flex flex-col gap-2 rounded-xl border border-border bg-card p-3 shadow-xs",
222
+ "data-testid": "pitr-result",
223
+ children: [/* @__PURE__ */ jsxDEV("div", {
224
+ className: "flex items-center gap-2",
225
+ children: [/* @__PURE__ */ jsxDEV("h3", {
226
+ className: "text-sm font-semibold",
227
+ children: t("Restore armed")
228
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Badge, {
229
+ variant: restored.restarted ? "destructive" : "secondary",
230
+ children: restored.restarted ? t("restarted now") : t("on next restart")
231
+ }, void 0, false)]
232
+ }, void 0, true), /* @__PURE__ */ jsxDEV("p", {
233
+ className: "font-mono text-xs break-all text-muted-foreground",
234
+ children: [t("Restored to"), ": ", restored.restoredTo]
235
+ }, void 0, true), /* @__PURE__ */ jsxDEV("p", {
236
+ className: "font-mono text-xs break-all text-muted-foreground",
237
+ "data-testid": "pitr-undo-bookmark",
238
+ children: [t("Undo bookmark"), ": ", restored.undoBookmark]
239
+ }, void 0, true), /* @__PURE__ */ jsxDEV("div", {
240
+ children: /* @__PURE__ */ jsxDEV(ConfirmButton, {
241
+ confirmLabel: t("Confirm undo"),
242
+ disabled: busy,
243
+ onConfirm: onUndo,
244
+ testId: "pitr-undo",
245
+ children: t("Undo restore")
246
+ }, void 0, false)
247
+ }, void 0, false)]
248
+ }, void 0, true)]
249
+ }, void 0, true);
250
+ };
251
+
252
+ export { PitrPanel };
@@ -0,0 +1,3 @@
1
+ const STUDIO_ROOT_CLASS = "lunora-studio-root";
2
+
3
+ export { STUDIO_ROOT_CLASS as default };