@lunora/studio 1.0.0-alpha.3 → 1.0.0-alpha.30

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 (174) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/index.d.ts +29 -18
  3. package/dist/index.js +29 -29
  4. package/dist/mount.d.ts +1 -1
  5. package/dist/mount.js +1 -1
  6. package/dist/{packem_shared/ApiTab-W3no0KOj.js → packem_chunks/ApiTab.js} +5 -5
  7. package/dist/{packem_shared/AuditPanel-Bq4o_IqT.js → packem_chunks/AuditPanel.js} +24 -36
  8. package/dist/{packem_shared/ExportImportPanel-dxY56TDF.js → packem_chunks/ExportImportPanel.js} +13 -9
  9. package/dist/{packem_shared/FileBrowser-Dhy4zES8.js → packem_chunks/FileBrowser.js} +15 -15
  10. package/dist/{packem_shared/FunctionRunner--fQJRPAD.js → packem_chunks/FunctionRunner.js} +10 -10
  11. package/dist/{packem_shared/FunctionStatsPanel-BjRUA_G3.js → packem_chunks/FunctionStatsPanel.js} +147 -204
  12. package/dist/{packem_shared/HealthPanel-PxclMWmA.js → packem_chunks/HealthPanel.js} +37 -29
  13. package/dist/{packem_shared/InsightsPanel-CWS_C-xW.js → packem_chunks/InsightsPanel.js} +107 -69
  14. package/dist/{packem_shared/LogsPanel-CTW8CFcM.js → packem_chunks/LogsPanel.js} +41 -53
  15. package/dist/{packem_shared/MailPanel-BwkvfIHF.js → packem_chunks/MailPanel.js} +37 -44
  16. package/dist/{packem_shared/MetricsPanel-BLuOhZQ4.js → packem_chunks/MetricsPanel.js} +41 -322
  17. package/dist/{packem_shared/MigrationsPanel-DT3lnwHF.js → packem_chunks/MigrationsPanel.js} +39 -46
  18. package/dist/{packem_shared/PitrPanel-CdErL7EJ.js → packem_chunks/PitrPanel.js} +9 -9
  19. package/dist/{packem_shared/SchemaViewer-LJDcmNIO.js → packem_chunks/SchemaViewer.js} +15 -15
  20. package/dist/{packem_shared/SecurityAdvisorPanel-C_ETsPih.js → packem_chunks/SecurityAdvisorPanel.js} +44 -31
  21. package/dist/packem_chunks/SettingsPanel.js +390 -0
  22. package/dist/{packem_shared/sql-editor-panel-BFRpzj6r.js → packem_chunks/SqlEditorPanel.js} +12 -252
  23. package/dist/{packem_shared/TableEditor-ChfUOZec.js → packem_chunks/TableEditor.js} +10 -68
  24. package/dist/packem_chunks/analytics-panel.js +261 -0
  25. package/dist/packem_chunks/auth-config-panel.js +179 -0
  26. package/dist/packem_chunks/auth-sessions-panel.js +175 -0
  27. package/dist/packem_chunks/dashboards-panel.js +779 -0
  28. package/dist/packem_chunks/fanout-panel.js +481 -0
  29. package/dist/packem_chunks/flags-panel.js +176 -0
  30. package/dist/packem_chunks/kv-browser.js +1896 -0
  31. package/dist/packem_chunks/log-drains-panel.js +273 -0
  32. package/dist/packem_chunks/organizations-panel.js +670 -0
  33. package/dist/packem_chunks/payments-panel.js +185 -0
  34. package/dist/packem_chunks/permissions-panel.js +804 -0
  35. package/dist/packem_chunks/queues-panel.js +147 -0
  36. package/dist/packem_chunks/rls-panel.js +313 -0
  37. package/dist/packem_chunks/schedule-panel.js +831 -0
  38. package/dist/packem_chunks/sparkline.js +79 -0
  39. package/dist/packem_chunks/storage-rules-panel.js +138 -0
  40. package/dist/packem_chunks/subscriptions-panel.js +236 -0
  41. package/dist/packem_chunks/users-panel.js +1265 -0
  42. package/dist/packem_chunks/vector-browser.js +281 -0
  43. package/dist/packem_chunks/workflows-panel.js +562 -0
  44. package/dist/packem_shared/{ADMIN_FUNCTIONS-DmBqMZ-z.js → ADMIN_FUNCTIONS-CdaxLGkE.js} +3 -0
  45. package/dist/packem_shared/{ApiDocsPanel-C5-yJ1p7.js → ApiDocsPanel-B6u_wnz-.js} +2 -2
  46. package/dist/packem_shared/{ApiReferencePanel-1vubqG5s.js → ApiReferencePanel-Drz4UO0a.js} +2 -1
  47. package/dist/packem_shared/{CommandPalette-BtgkBzZI.js → CommandPalette-B5BertvU.js} +1 -1
  48. package/dist/packem_shared/{DataBrowser-CtBe1So9.js → DataBrowser-ANTHLWWh.js} +198 -247
  49. package/dist/packem_shared/{GlobalDataBrowser-BTEmGrph.js → GlobalDataBrowser-CjgTG_wa.js} +37 -66
  50. package/dist/packem_shared/HomePanel-c2FodE9t.js +14 -0
  51. package/dist/packem_shared/{OpenRpcReferencePanel-vbcYbv3V.js → OpenRpcReferencePanel-CspFdbUT.js} +2 -1
  52. package/dist/packem_shared/{ScheduledJobs-CabfKbyM.js → ScheduledJobs-BEJXtsBn.js} +163 -60
  53. package/dist/packem_shared/Studio-CVTtA_sB.js +16 -0
  54. package/dist/packem_shared/{StudioApp-CHOZA-z-.js → StudioApp-BSYV2fz6.js} +277 -3
  55. package/dist/packem_shared/{advisor-view-CGcfMiPq.js → advisor-view-DzqyvUU3.js} +1 -1
  56. package/dist/packem_shared/{app.d-CCmwDEVs.d.ts → app.d-B6MyY_al.d.ts} +1 -1
  57. package/dist/packem_shared/{bar-chart-CzJAgqkp.js → bar-chart-Bb__F5-u.js} +1 -1
  58. package/dist/packem_shared/browser-storage-d03UsV7v.js +249 -0
  59. package/dist/packem_shared/{cf-links-2cktDedA.js → cf-links-BZfRdxSE.js} +1 -1
  60. package/dist/packem_shared/{data-grid-B5CaV3wS.js → data-grid-Dsg6nRjo.js} +1 -1
  61. package/dist/packem_shared/data-view-params-Cd6yRVbv.js +96 -0
  62. package/dist/packem_shared/{grid-features-CzUfqgsM.js → grid-features-D7nNzBYm.js} +4 -99
  63. package/dist/packem_shared/{HomePanel-Cw16stjq.js → home-panel-aOR_drVv.js} +833 -469
  64. package/dist/packem_shared/{internal-rXJEAhXS.js → internal-FcNZ3beb.js} +1 -1
  65. package/dist/packem_shared/modal-shell-hyaJvIJ6.js +100 -0
  66. package/dist/packem_shared/{reference-view-B4E49Lip.js → reference-view-Doy1XlQq.js} +4 -44
  67. package/dist/packem_shared/select-UkNYCT3s.js +273 -0
  68. package/dist/packem_shared/skeleton-CGhL0p20.js +45 -0
  69. package/dist/packem_shared/{sparkline-10dG-_f0.js → sparkline-KgMPHS4u.js} +1 -1
  70. package/dist/packem_shared/studio-dMTJ-kRR.js +3987 -0
  71. package/dist/packem_shared/{table-_RzNvy3R.js → table-DsCa9Qp9.js} +1 -1
  72. package/dist/packem_shared/{table-list-sidebar-Bg7uzbvC.js → table-list-sidebar-VMo0ftxD.js} +1 -1
  73. package/dist/packem_shared/use-admin-query-BViPA3US.js +105 -0
  74. package/dist/packem_shared/use-auth-capabilities-BHkoBnbZ.js +32 -0
  75. package/dist/standalone/chunk-2DS7JLWL.js +13 -0
  76. package/dist/standalone/chunk-2K47A3P6.js +1 -0
  77. package/dist/standalone/chunk-2NGUNNGW.js +1 -0
  78. package/dist/standalone/chunk-2PDQ4KZ6.js +1 -0
  79. package/dist/standalone/chunk-3BP4BTLR.js +4 -0
  80. package/dist/standalone/chunk-3OA7PECS.js +6 -0
  81. package/dist/standalone/chunk-4QHKBC77.js +1 -0
  82. package/dist/standalone/chunk-57LYXHXH.js +1 -0
  83. package/dist/standalone/chunk-5NSZVXCS.js +1 -0
  84. package/dist/standalone/chunk-5OIBXYZM.js +1 -0
  85. package/dist/standalone/chunk-63XKJQTQ.js +1 -0
  86. package/dist/standalone/chunk-6RC6L33K.js +4 -0
  87. package/dist/standalone/chunk-6VTGE3WJ.js +1 -0
  88. package/dist/standalone/chunk-7PBZKXYA.js +1 -0
  89. package/dist/standalone/chunk-7POIIWC5.js +3 -0
  90. package/dist/standalone/chunk-7RYIQKZA.js +1 -0
  91. package/dist/standalone/chunk-7TJJXVSE.js +1 -0
  92. package/dist/standalone/chunk-AB6QGMBF.js +1 -0
  93. package/dist/standalone/chunk-AHILM3ZP.js +1 -0
  94. package/dist/standalone/chunk-AQGOX5DK.js +1 -0
  95. package/dist/standalone/chunk-ASLGNHAX.js +1 -0
  96. package/dist/standalone/chunk-BHTPZS7L.js +4 -0
  97. package/dist/standalone/chunk-BJGBHGK3.js +1 -0
  98. package/dist/standalone/chunk-D7HKX4DI.js +1 -0
  99. package/dist/standalone/chunk-DGWTR4G7.js +1 -0
  100. package/dist/standalone/chunk-DSX53PBM.js +1 -0
  101. package/dist/standalone/chunk-DV2MNASX.js +1 -0
  102. package/dist/standalone/chunk-DWDILV2E.js +1 -0
  103. package/dist/standalone/chunk-E7AVZZKO.js +1 -0
  104. package/dist/standalone/chunk-EGJX34WM.js +1 -0
  105. package/dist/standalone/chunk-FCR3HKFV.js +1 -0
  106. package/dist/standalone/chunk-FKO3HO6I.js +4 -0
  107. package/dist/standalone/chunk-FQKNJSRA.js +76 -0
  108. package/dist/standalone/chunk-G2TFEWEQ.js +4 -0
  109. package/dist/standalone/chunk-GABMIICA.js +1 -0
  110. package/dist/standalone/chunk-GTM65MVX.js +1 -0
  111. package/dist/standalone/chunk-GZXPPJVK.js +1 -0
  112. package/dist/standalone/chunk-HFBRE65A.js +1 -0
  113. package/dist/standalone/chunk-HFMQNUBC.js +1 -0
  114. package/dist/standalone/chunk-HG7Q5B3H.js +1 -0
  115. package/dist/standalone/chunk-HTQL7AXU.js +1 -0
  116. package/dist/standalone/chunk-HV7G6DFP.js +5 -0
  117. package/dist/standalone/chunk-ITLLHEVG.js +1 -0
  118. package/dist/standalone/chunk-J6J54MBP.js +1 -0
  119. package/dist/standalone/chunk-JSEXCW5Q.js +1 -0
  120. package/dist/standalone/chunk-KEGAWN6Y.js +1 -0
  121. package/dist/standalone/chunk-KG3QTFWE.js +1 -0
  122. package/dist/standalone/chunk-KGS7KM4A.js +2 -0
  123. package/dist/standalone/chunk-KODWGUAB.js +1 -0
  124. package/dist/standalone/chunk-KZ6INM5M.js +1 -0
  125. package/dist/standalone/chunk-MGFEVTRI.js +1 -0
  126. package/dist/standalone/chunk-N2QDG3MW.js +1 -0
  127. package/dist/standalone/chunk-N4JI6VQD.js +1 -0
  128. package/dist/standalone/chunk-NNISYDNI.js +1 -0
  129. package/dist/standalone/chunk-O4PXDNR3.js +1 -0
  130. package/dist/standalone/chunk-O77VNUFD.js +1 -0
  131. package/dist/standalone/chunk-OD2POGPJ.js +1 -0
  132. package/dist/standalone/chunk-OXCK3ZUG.js +1 -0
  133. package/dist/standalone/chunk-P7DSVO4U.js +1 -0
  134. package/dist/standalone/chunk-PKAM3N5C.js +1 -0
  135. package/dist/standalone/chunk-QGXGBHQO.js +1 -0
  136. package/dist/standalone/chunk-QHA3TAEJ.js +1 -0
  137. package/dist/standalone/chunk-QQWC62X7.js +1 -0
  138. package/dist/standalone/chunk-RMYA7NAH.js +1 -0
  139. package/dist/standalone/chunk-RRCPZMC2.js +22 -0
  140. package/dist/standalone/chunk-S53RUBW5.js +1 -0
  141. package/dist/standalone/chunk-SB4GT6PK.js +1 -0
  142. package/dist/standalone/chunk-SEU5V7I6.js +1 -0
  143. package/dist/standalone/chunk-SHIOUDOT.js +1 -0
  144. package/dist/standalone/chunk-SIDRSX7Y.js +1 -0
  145. package/dist/standalone/chunk-SVR644RF.js +1 -0
  146. package/dist/standalone/chunk-TAD43CG5.js +1 -0
  147. package/dist/standalone/chunk-TBMYIZIO.js +1 -0
  148. package/dist/standalone/chunk-TICVNLPM.js +1 -0
  149. package/dist/standalone/chunk-TQ36RWOP.js +1 -0
  150. package/dist/standalone/chunk-UGMRUEBU.js +1 -0
  151. package/dist/standalone/chunk-UII66UVF.js +1 -0
  152. package/dist/standalone/chunk-VJPQUBN6.js +8 -0
  153. package/dist/standalone/chunk-VLXV72QE.js +1 -0
  154. package/dist/standalone/chunk-VMJKJWG6.js +1 -0
  155. package/dist/standalone/chunk-VPDPN5RR.js +2 -0
  156. package/dist/standalone/chunk-VSEGZA3J.js +1 -0
  157. package/dist/standalone/chunk-VXBZQIGY.js +4 -0
  158. package/dist/standalone/chunk-W7PCVQTE.js +1 -0
  159. package/dist/standalone/chunk-XLILSHPE.js +1 -0
  160. package/dist/standalone/chunk-XURNGQSS.js +1 -0
  161. package/dist/standalone/chunk-YJGANMU7.js +18 -0
  162. package/dist/standalone/chunk-YQQJ4N6U.js +1 -0
  163. package/dist/standalone/chunk-ZH7UES4T.js +2 -0
  164. package/dist/standalone/chunk-ZPUO7FNI.js +1 -0
  165. package/dist/standalone/chunk-ZQCVDXDL.js +1 -0
  166. package/dist/standalone/studio.js +104 -246
  167. package/dist/styles.css +1 -1
  168. package/package.json +13 -12
  169. package/dist/packem_shared/SettingsPanel-C0uLRf5P.js +0 -176
  170. package/dist/packem_shared/SqlEditorPanel-CguV7otm.js +0 -13
  171. package/dist/packem_shared/Studio-DFWAlcuf.js +0 -33
  172. package/dist/packem_shared/studio-Bj8psK1c.js +0 -10303
  173. package/dist/packem_shared/use-live-admin-Dq6g3daN.js +0 -73
  174. package/dist/packem_shared/use-live-shard-seed-Cuk3dnH0.js +0 -76
@@ -0,0 +1,804 @@
1
+ import { useState, useRef, useEffect } from 'react';
2
+ import { C as Card, b as CardHeader, c as CardTitle, d as CardDescription, a as CardContent } from '../packem_shared/card-DURq3ElK.js';
3
+ import { useT } from '../packem_shared/DEFAULT_LOCALE-CiX4W7Kr.js';
4
+ import { c } from 'react/compiler-runtime';
5
+ import { L as LiveError } from '../packem_shared/live-status-FZ0psC1z.js';
6
+ import { B as Badge } from '../packem_shared/badge-B2PKA1-5.js';
7
+ import { B as Button } from '../packem_shared/button-BhsN2uZH.js';
8
+ import { E as EmptyState } from '../packem_shared/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 '../packem_shared/table-DsCa9Qp9.js';
10
+ import { u as useAdminQuery } from '../packem_shared/use-admin-query-BViPA3US.js';
11
+ import { ADMIN_FUNCTIONS } from '../packem_shared/ADMIN_FUNCTIONS-CdaxLGkE.js';
12
+ import { jsxDEV } from 'react/jsx-dev-runtime';
13
+ import { useLunora } from '@lunora/react';
14
+ import { ShardInput } from '../packem_shared/ShardInput-gx_DZXXn.js';
15
+ import { I as Input } from '../packem_shared/input-XH4r1Pt1.js';
16
+ import { L as Label } from '../packem_shared/label-D8ykjn5J.js';
17
+ import { T as Textarea } from '../packem_shared/textarea-D3gaCU_-.js';
18
+ import { c as callOptions, a as adminRef, e as errorMessage, f as fireAndForget } from '../packem_shared/internal-FcNZ3beb.js';
19
+
20
+ const OPERATIONS = ["read", "insert", "update", "delete"];
21
+ const operationLabel = (t, operation) => ({
22
+ delete: t("Delete"),
23
+ insert: t("Insert"),
24
+ read: t("Read"),
25
+ update: t("Update")
26
+ })[operation];
27
+ const uncoveredTables = (advisories) => {
28
+ const set = /* @__PURE__ */ new Set();
29
+ for (const advisory of advisories?.advisories ?? []) {
30
+ if (advisory.name === "rls_uncovered_table") {
31
+ const {
32
+ table
33
+ } = advisory.metadata;
34
+ if (typeof table === "string" && table !== "") {
35
+ set.add(table);
36
+ }
37
+ }
38
+ }
39
+ return set;
40
+ };
41
+ const buildRows = (policies, maskColumns, uncovered) => {
42
+ const byTable = /* @__PURE__ */ new Map();
43
+ const ensure = (table) => {
44
+ const existing = byTable.get(table);
45
+ if (existing !== void 0) {
46
+ return existing;
47
+ }
48
+ const row = {
49
+ cells: {
50
+ delete: {},
51
+ insert: {},
52
+ read: {},
53
+ update: {}
54
+ },
55
+ maskedColumns: [],
56
+ table,
57
+ uncovered: uncovered.has(table)
58
+ };
59
+ byTable.set(table, row);
60
+ return row;
61
+ };
62
+ for (const policy of policies) {
63
+ ensure(policy.table).cells[policy.on] = {
64
+ procedure: policy.procedure
65
+ };
66
+ }
67
+ for (const column of maskColumns) {
68
+ const row = ensure(column.table);
69
+ if (!row.maskedColumns.includes(column.column)) {
70
+ row.maskedColumns.push(column.column);
71
+ }
72
+ }
73
+ for (const table of uncovered) {
74
+ ensure(table);
75
+ }
76
+ return [...byTable.values()].toSorted((a, b) => a.table.localeCompare(b.table));
77
+ };
78
+ const PermissionsMatrix = (t0) => {
79
+ const $ = c(16);
80
+ let t1;
81
+ if ($[0] !== t0) {
82
+ t1 = t0 === void 0 ? {} : t0;
83
+ $[0] = t0;
84
+ $[1] = t1;
85
+ } else {
86
+ t1 = $[1];
87
+ }
88
+ const {
89
+ onProbe
90
+ } = t1;
91
+ const t = useT();
92
+ let t2;
93
+ let t3;
94
+ if ($[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
95
+ t2 = {};
96
+ t3 = {
97
+ live: true
98
+ };
99
+ $[2] = t2;
100
+ $[3] = t3;
101
+ } else {
102
+ t2 = $[2];
103
+ t3 = $[3];
104
+ }
105
+ const policiesQuery = useAdminQuery(ADMIN_FUNCTIONS.rlsPolicies, t2, t3);
106
+ let t4;
107
+ if ($[4] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
108
+ t4 = {};
109
+ $[4] = t4;
110
+ } else {
111
+ t4 = $[4];
112
+ }
113
+ const masksQuery = useAdminQuery(ADMIN_FUNCTIONS.maskPolicies, t4);
114
+ let t5;
115
+ if ($[5] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
116
+ t5 = {};
117
+ $[5] = t5;
118
+ } else {
119
+ t5 = $[5];
120
+ }
121
+ const advisoriesQuery = useAdminQuery(ADMIN_FUNCTIONS.getAdvisories, t5);
122
+ let t6;
123
+ if ($[6] !== advisoriesQuery.data || $[7] !== masksQuery.data || $[8] !== onProbe || $[9] !== policiesQuery || $[10] !== t) {
124
+ let policies = null;
125
+ if (policiesQuery.data !== void 0) {
126
+ policies = Array.isArray(policiesQuery.data.policies) ? policiesQuery.data.policies : [];
127
+ }
128
+ const maskColumns = Array.isArray(masksQuery.data?.columns) ? masksQuery.data.columns : [];
129
+ const advisories = advisoriesQuery.data ?? null;
130
+ const {
131
+ error,
132
+ liveError
133
+ } = policiesQuery;
134
+ const rows = policies === null ? [] : buildRows(policies, maskColumns, uncoveredTables(advisories));
135
+ let t7;
136
+ if ($[12] !== liveError) {
137
+ t7 = /* @__PURE__ */ jsxDEV("div", {
138
+ className: "flex items-center justify-end",
139
+ children: /* @__PURE__ */ jsxDEV(LiveError, {
140
+ message: liveError,
141
+ prefix: "pm"
142
+ }, void 0, false)
143
+ }, void 0, false);
144
+ $[12] = liveError;
145
+ $[13] = t7;
146
+ } else {
147
+ t7 = $[13];
148
+ }
149
+ let t8;
150
+ if ($[14] !== error) {
151
+ t8 = error !== null && /* @__PURE__ */ jsxDEV("p", {
152
+ className: "text-sm text-destructive",
153
+ "data-testid": "pm-error",
154
+ role: "alert",
155
+ children: error
156
+ }, void 0, false);
157
+ $[14] = error;
158
+ $[15] = t8;
159
+ } else {
160
+ t8 = $[15];
161
+ }
162
+ t6 = /* @__PURE__ */ jsxDEV("div", {
163
+ className: "flex flex-col gap-3",
164
+ "data-testid": "lunora-permissions-matrix",
165
+ children: [t7, t8, policies !== null && rows.length === 0 ? /* @__PURE__ */ jsxDEV(EmptyState, {
166
+ description: t("Add a `definePolicy` and wire it through `.use(rls(...))` to populate this matrix."),
167
+ testId: "pm-empty",
168
+ title: t("No policy")
169
+ }, void 0, false) : /* @__PURE__ */ jsxDEV(Table, {
170
+ "data-testid": "pm-table",
171
+ children: [/* @__PURE__ */ jsxDEV(TableHeader, {
172
+ children: /* @__PURE__ */ jsxDEV(TableRow, {
173
+ children: [/* @__PURE__ */ jsxDEV(TableHead, {
174
+ children: t("Table")
175
+ }, void 0, false), OPERATIONS.map((operation) => /* @__PURE__ */ jsxDEV(TableHead, {
176
+ children: operationLabel(t, operation)
177
+ }, operation, false)), /* @__PURE__ */ jsxDEV(TableHead, {
178
+ children: t("Masked columns")
179
+ }, void 0, false)]
180
+ }, void 0, true)
181
+ }, void 0, false), /* @__PURE__ */ jsxDEV(TableBody, {
182
+ children: rows.map((row) => /* @__PURE__ */ jsxDEV(TableRow, {
183
+ "data-testid": `pm-row-${row.table}`,
184
+ children: [/* @__PURE__ */ jsxDEV(TableCell, {
185
+ className: "font-mono text-xs",
186
+ children: /* @__PURE__ */ jsxDEV("span", {
187
+ className: "flex items-center gap-1.5",
188
+ children: [row.table, row.uncovered && /* @__PURE__ */ jsxDEV(Badge, {
189
+ "data-testid": `pm-uncovered-${row.table}`,
190
+ title: t("Uncovered — reachable without a policy"),
191
+ variant: "destructive",
192
+ children: t("Uncovered — reachable without a policy")
193
+ }, void 0, false)]
194
+ }, void 0, true)
195
+ }, void 0, false), OPERATIONS.map((operation_0) => {
196
+ const cell = row.cells[operation_0];
197
+ return /* @__PURE__ */ jsxDEV(TableCell, {
198
+ "data-testid": `pm-cell-${row.table}-${operation_0}`,
199
+ children: cell.procedure === void 0 ? /* @__PURE__ */ jsxDEV("span", {
200
+ className: "text-xs text-muted-foreground",
201
+ children: t("No policy")
202
+ }, void 0, false) : /* @__PURE__ */ jsxDEV("span", {
203
+ className: "flex flex-col gap-1",
204
+ children: [/* @__PURE__ */ jsxDEV(Badge, {
205
+ variant: "secondary",
206
+ children: t("Covered by {procedure}", {
207
+ procedure: cell.procedure
208
+ })
209
+ }, void 0, false), onProbe !== void 0 && /* @__PURE__ */ jsxDEV(Button, {
210
+ className: "h-auto justify-start p-0 text-[11px]",
211
+ "data-testid": `pm-probe-${row.table}-${operation_0}`,
212
+ onClick: () => {
213
+ onProbe(row.table, operation_0, cell.procedure ?? "");
214
+ },
215
+ size: "xs",
216
+ type: "button",
217
+ variant: "ghost",
218
+ children: t("Probe this")
219
+ }, void 0, false)]
220
+ }, void 0, true)
221
+ }, operation_0, false);
222
+ }), /* @__PURE__ */ jsxDEV(TableCell, {
223
+ children: row.maskedColumns.length === 0 ? /* @__PURE__ */ jsxDEV("span", {
224
+ "aria-hidden": "true",
225
+ className: "text-muted-foreground",
226
+ children: "—"
227
+ }, void 0, false) : /* @__PURE__ */ jsxDEV("span", {
228
+ className: "flex flex-wrap gap-1",
229
+ children: row.maskedColumns.map(_temp)
230
+ }, void 0, false)
231
+ }, void 0, false)]
232
+ }, row.table, true))
233
+ }, void 0, false)]
234
+ }, void 0, true)]
235
+ }, void 0, true);
236
+ $[6] = advisoriesQuery.data;
237
+ $[7] = masksQuery.data;
238
+ $[8] = onProbe;
239
+ $[9] = policiesQuery;
240
+ $[10] = t;
241
+ $[11] = t6;
242
+ } else {
243
+ t6 = $[11];
244
+ }
245
+ return t6;
246
+ };
247
+ function _temp(column) {
248
+ return /* @__PURE__ */ jsxDEV(Badge, {
249
+ variant: "outline",
250
+ children: column
251
+ }, column, false);
252
+ }
253
+
254
+ const RUN_AS = adminRef(ADMIN_FUNCTIONS.runAs);
255
+ const useRunAsProbe = () => {
256
+ const client = useLunora();
257
+ return async ({
258
+ args,
259
+ functionPath,
260
+ shardKey = "",
261
+ userId
262
+ }) => {
263
+ const reference = RUN_AS;
264
+ try {
265
+ const value = await client.query(reference, {
266
+ args,
267
+ functionPath,
268
+ userId
269
+ }, callOptions(shardKey));
270
+ return {
271
+ kind: "allowed",
272
+ value
273
+ };
274
+ } catch (error) {
275
+ return {
276
+ kind: "denied",
277
+ message: error instanceof Error ? error.message : String(error)
278
+ };
279
+ }
280
+ };
281
+ };
282
+
283
+ const formatValue = (value) => value === void 0 ? "undefined" : JSON.stringify(value, null, 2);
284
+ const PermissionsPlayground = ({
285
+ functions: functionsProp,
286
+ prefill,
287
+ runAsIdentity = false
288
+ } = {}) => {
289
+ const client = useLunora();
290
+ const t = useT();
291
+ const probe = useRunAsProbe();
292
+ const [discovered, setDiscovered] = useState(null);
293
+ const [discoverError, setDiscoverError] = useState(null);
294
+ const functions = functionsProp ?? discovered ?? [];
295
+ const [selectedPath, setSelectedPath] = useState("");
296
+ const [argsText, setArgsText] = useState("{}");
297
+ const [shardKey, setShardKey] = useState("");
298
+ const [runAsUserId, setRunAsUserId] = useState("");
299
+ const [running, setRunning] = useState(false);
300
+ const [outcome, setOutcome] = useState(null);
301
+ const [argsError, setArgsError] = useState(null);
302
+ const appliedNonce = useRef(-1);
303
+ useEffect(() => {
304
+ if (functionsProp !== void 0) {
305
+ return void 0;
306
+ }
307
+ let cancelled = false;
308
+ client.listFunctions().then((list) => {
309
+ if (!cancelled) {
310
+ setDiscovered(list);
311
+ }
312
+ return list;
313
+ }).catch((error_) => {
314
+ if (!cancelled) {
315
+ setDiscoverError(errorMessage(error_));
316
+ }
317
+ });
318
+ return () => {
319
+ cancelled = true;
320
+ };
321
+ }, [client, functionsProp]);
322
+ useEffect(() => {
323
+ if (prefill === void 0 || appliedNonce.current === prefill.nonce) {
324
+ return;
325
+ }
326
+ appliedNonce.current = prefill.nonce;
327
+ const target = prefill.functionPath;
328
+ queueMicrotask(() => {
329
+ setSelectedPath(target);
330
+ setOutcome(null);
331
+ });
332
+ }, [prefill]);
333
+ const effectivePath = selectedPath === "" ? functions[0]?.path ?? "" : selectedPath;
334
+ const run = async () => {
335
+ if (effectivePath === "") {
336
+ return;
337
+ }
338
+ let parsedArgs;
339
+ try {
340
+ parsedArgs = argsText.trim() === "" ? {} : JSON.parse(argsText);
341
+ } catch (parseError) {
342
+ setArgsError(t("Invalid JSON args: {message}", {
343
+ message: parseError.message
344
+ }));
345
+ setOutcome(null);
346
+ return;
347
+ }
348
+ setArgsError(null);
349
+ setRunning(true);
350
+ try {
351
+ const result = await probe({
352
+ args: parsedArgs,
353
+ functionPath: effectivePath,
354
+ shardKey: shardKey.trim(),
355
+ userId: runAsUserId.trim()
356
+ });
357
+ setOutcome(result);
358
+ } finally {
359
+ setRunning(false);
360
+ }
361
+ };
362
+ const runOnce = () => {
363
+ fireAndForget(run());
364
+ };
365
+ const onSelectChange = (event) => {
366
+ setSelectedPath(event.target.value);
367
+ };
368
+ const onArgsChange = (event_0) => {
369
+ setArgsText(event_0.target.value);
370
+ };
371
+ const onUserIdChange = (event_1) => {
372
+ setRunAsUserId(event_1.target.value);
373
+ };
374
+ return /* @__PURE__ */ jsxDEV("div", {
375
+ className: "flex flex-col gap-3",
376
+ "data-testid": "lunora-permissions-playground",
377
+ children: [discoverError !== null && /* @__PURE__ */ jsxDEV("p", {
378
+ className: "text-sm text-destructive",
379
+ "data-testid": "pp-discover-error",
380
+ role: "alert",
381
+ children: discoverError
382
+ }, void 0, false), !runAsIdentity && /* @__PURE__ */ jsxDEV(EmptyState, {
383
+ description: t("Dev only: runs the selected function as this user over the admin gate so you can test auth and RLS."),
384
+ testId: "pp-gate",
385
+ title: t("Set `runAsIdentity` to forge an identity and probe access.")
386
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
387
+ className: "flex flex-col gap-3 rounded-md border border-border p-3",
388
+ children: [/* @__PURE__ */ jsxDEV("div", {
389
+ className: "flex flex-col gap-1.5",
390
+ children: [/* @__PURE__ */ jsxDEV(Label, {
391
+ htmlFor: "pp-function",
392
+ children: t("Function")
393
+ }, void 0, false), /* @__PURE__ */ jsxDEV("select", {
394
+ "aria-label": t("Function"),
395
+ className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
396
+ "data-testid": "pp-function",
397
+ id: "pp-function",
398
+ onChange: onSelectChange,
399
+ value: effectivePath,
400
+ children: functions.map((descriptor) => /* @__PURE__ */ jsxDEV("option", {
401
+ value: descriptor.path,
402
+ children: [descriptor.path, " (", descriptor.kind, ")"]
403
+ }, descriptor.path, true))
404
+ }, void 0, false)]
405
+ }, void 0, true), /* @__PURE__ */ jsxDEV("div", {
406
+ className: "flex flex-col gap-1.5",
407
+ children: [/* @__PURE__ */ jsxDEV(Label, {
408
+ htmlFor: "pp-args",
409
+ children: t("Arguments")
410
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Textarea, {
411
+ "aria-label": t("Arguments"),
412
+ className: "font-mono text-xs",
413
+ "data-testid": "pp-args",
414
+ id: "pp-args",
415
+ onChange: onArgsChange,
416
+ value: argsText
417
+ }, void 0, false)]
418
+ }, void 0, true), /* @__PURE__ */ jsxDEV(ShardInput, {
419
+ onChange: setShardKey,
420
+ testId: "pp-shard",
421
+ value: shardKey
422
+ }, void 0, false), /* @__PURE__ */ jsxDEV("div", {
423
+ className: "flex flex-col gap-1.5",
424
+ children: [/* @__PURE__ */ jsxDEV(Label, {
425
+ htmlFor: "pp-user",
426
+ children: t("Run as (userId)")
427
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
428
+ "aria-label": t("Identity (userId) to run as"),
429
+ className: "font-mono text-xs",
430
+ "data-testid": "pp-user",
431
+ id: "pp-user",
432
+ onChange: onUserIdChange,
433
+ placeholder: t("Leave empty to run as admin"),
434
+ value: runAsUserId
435
+ }, void 0, false)]
436
+ }, void 0, true), /* @__PURE__ */ jsxDEV("div", {
437
+ className: "flex items-center gap-2",
438
+ children: [/* @__PURE__ */ jsxDEV(Button, {
439
+ "data-testid": "pp-run",
440
+ disabled: !runAsIdentity || running || effectivePath === "",
441
+ onClick: runOnce,
442
+ type: "button",
443
+ children: running ? t("Probing…") : t("Run probe")
444
+ }, void 0, false), runAsUserId.trim() !== "" && /* @__PURE__ */ jsxDEV(Badge, {
445
+ "data-testid": "pp-as-badge",
446
+ variant: "secondary",
447
+ children: t("as {userId}", {
448
+ userId: runAsUserId.trim()
449
+ })
450
+ }, void 0, false)]
451
+ }, void 0, true)]
452
+ }, void 0, true), argsError !== null && /* @__PURE__ */ jsxDEV("p", {
453
+ className: "text-sm text-destructive",
454
+ "data-testid": "pp-args-error",
455
+ role: "alert",
456
+ children: argsError
457
+ }, void 0, false), outcome !== null && outcome.kind === "allowed" && /* @__PURE__ */ jsxDEV("div", {
458
+ className: "flex flex-col gap-2",
459
+ "data-testid": "pp-outcome-allowed",
460
+ children: [/* @__PURE__ */ jsxDEV(Badge, {
461
+ variant: "secondary",
462
+ children: t("Allowed")
463
+ }, void 0, false), /* @__PURE__ */ jsxDEV("pre", {
464
+ className: "overflow-auto rounded-md border border-border bg-muted/50 p-3 font-mono text-xs",
465
+ "data-testid": "pp-result",
466
+ children: formatValue(outcome.value)
467
+ }, void 0, false)]
468
+ }, void 0, true), outcome !== null && outcome.kind === "denied" && /* @__PURE__ */ jsxDEV("div", {
469
+ className: "flex flex-col gap-2",
470
+ "data-testid": "pp-outcome-denied",
471
+ children: [/* @__PURE__ */ jsxDEV(Badge, {
472
+ variant: "destructive",
473
+ children: t("Denied")
474
+ }, void 0, false), /* @__PURE__ */ jsxDEV("pre", {
475
+ className: "overflow-auto rounded-md border border-border bg-muted/50 p-3 font-mono text-xs text-destructive",
476
+ "data-testid": "pp-denied",
477
+ role: "alert",
478
+ children: outcome.message
479
+ }, void 0, false)]
480
+ }, void 0, true)]
481
+ }, void 0, true);
482
+ };
483
+
484
+ const POLICY_SCAFFOLD_ENDPOINT = "/__lunora/policy-scaffold";
485
+ const FAILURE_MESSAGES = {
486
+ "already-wired": "That procedure already has an rls(...) policy wired in.",
487
+ "file-exists": "A policy file with that name already exists — pick another name or edit it by hand.",
488
+ "invalid-identifier": "Name, table, and policy-set must be plain identifiers (letters, digits, _ or $).",
489
+ "unknown-procedure": "No exported procedure was found at that file path.",
490
+ "unsupported-procedure-shape": "That procedure uses the bare query({…}) form — convert it to the builder form (c.use(…).query(…)) before wiring RLS."
491
+ };
492
+ const applyPolicyScaffold = async (request) => {
493
+ const response = await fetch(POLICY_SCAFFOLD_ENDPOINT, {
494
+ body: JSON.stringify(request),
495
+ headers: {
496
+ "Content-Type": "application/json"
497
+ },
498
+ method: "POST"
499
+ });
500
+ const body = await response.json();
501
+ if (body.needsManualEdit === true) {
502
+ return {
503
+ kind: "needs-manual-edit",
504
+ message: body.message ?? "This change must be made by hand."
505
+ };
506
+ }
507
+ if (response.ok) {
508
+ return {
509
+ diagnostics: body.diagnostics ?? [],
510
+ kind: "ok",
511
+ label: body.fileName ?? body.exportName ?? ""
512
+ };
513
+ }
514
+ const token = body.error;
515
+ const message = token === void 0 ? `policy scaffold failed (${String(response.status)})` : FAILURE_MESSAGES[token] ?? token;
516
+ return {
517
+ kind: "error",
518
+ message
519
+ };
520
+ };
521
+
522
+ const EMPTY_FORM = {
523
+ exportName: "",
524
+ filePath: "",
525
+ policyName: "",
526
+ policies: "",
527
+ tableName: ""
528
+ };
529
+ const PolicyScaffolder = () => {
530
+ const t = useT();
531
+ const [mode, setMode] = useState(null);
532
+ const [busy, setBusy] = useState(false);
533
+ const [result, setResult] = useState(null);
534
+ const [form, setForm] = useState(EMPTY_FORM);
535
+ const onFieldChange = (event) => {
536
+ const field = event.target.name;
537
+ const {
538
+ value
539
+ } = event.target;
540
+ setForm((previous) => {
541
+ return {
542
+ ...previous,
543
+ [field]: value
544
+ };
545
+ });
546
+ };
547
+ const reset = () => {
548
+ setMode(null);
549
+ setForm(EMPTY_FORM);
550
+ };
551
+ const submit = async (request) => {
552
+ setBusy(true);
553
+ setResult(null);
554
+ try {
555
+ const outcome = await applyPolicyScaffold(request);
556
+ setResult(outcome);
557
+ if (outcome.kind === "ok") {
558
+ reset();
559
+ }
560
+ } catch (error) {
561
+ setResult({
562
+ kind: "error",
563
+ message: error instanceof Error ? error.message : String(error)
564
+ });
565
+ } finally {
566
+ setBusy(false);
567
+ }
568
+ };
569
+ const onSubmitScaffold = () => {
570
+ fireAndForget(submit({
571
+ kind: "scaffoldPolicy",
572
+ name: form.policyName.trim(),
573
+ table: form.tableName.trim()
574
+ }));
575
+ };
576
+ const onSubmitWire = () => {
577
+ fireAndForget(submit({
578
+ exportName: form.exportName.trim(),
579
+ filePath: form.filePath.trim(),
580
+ kind: "wireRls",
581
+ policies: form.policies.trim()
582
+ }));
583
+ };
584
+ const openScaffold = () => {
585
+ setResult(null);
586
+ setMode("scaffold");
587
+ };
588
+ const openWire = () => {
589
+ setResult(null);
590
+ setMode("wire");
591
+ };
592
+ return /* @__PURE__ */ jsxDEV(Card, {
593
+ "data-testid": "policy-scaffolder",
594
+ size: "sm",
595
+ children: [/* @__PURE__ */ jsxDEV(CardHeader, {
596
+ children: [/* @__PURE__ */ jsxDEV(CardTitle, {
597
+ children: t("Scaffold access rules")
598
+ }, void 0, false), /* @__PURE__ */ jsxDEV(CardDescription, {
599
+ children: t("Writes a new deny-by-default policy file under lunora/, or wires one into a procedure, then reruns codegen. Local dev only.")
600
+ }, void 0, false)]
601
+ }, void 0, true), /* @__PURE__ */ jsxDEV(CardContent, {
602
+ children: [/* @__PURE__ */ jsxDEV("div", {
603
+ className: "flex flex-wrap gap-2",
604
+ children: [/* @__PURE__ */ jsxDEV(Button, {
605
+ "data-testid": "policy-scaffolder-new",
606
+ onClick: openScaffold,
607
+ size: "xs",
608
+ type: "button",
609
+ variant: mode === "scaffold" ? "default" : "outline",
610
+ children: t("New policy file")
611
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Button, {
612
+ "data-testid": "policy-scaffolder-wire",
613
+ onClick: openWire,
614
+ size: "xs",
615
+ type: "button",
616
+ variant: mode === "wire" ? "default" : "outline",
617
+ children: t("Wire into a procedure")
618
+ }, void 0, false)]
619
+ }, void 0, true), mode === "scaffold" && /* @__PURE__ */ jsxDEV("div", {
620
+ className: "mt-3 flex flex-wrap items-end gap-2",
621
+ "data-testid": "policy-scaffolder-new-form",
622
+ children: [/* @__PURE__ */ jsxDEV("div", {
623
+ className: "flex flex-col gap-1 text-xs",
624
+ children: [/* @__PURE__ */ jsxDEV(Label, {
625
+ htmlFor: "policy-scaffolder-name",
626
+ children: t("Policy name")
627
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
628
+ "data-testid": "policy-scaffolder-name",
629
+ id: "policy-scaffolder-name",
630
+ name: "policyName",
631
+ onChange: onFieldChange,
632
+ value: form.policyName
633
+ }, void 0, false)]
634
+ }, void 0, true), /* @__PURE__ */ jsxDEV("div", {
635
+ className: "flex flex-col gap-1 text-xs",
636
+ children: [/* @__PURE__ */ jsxDEV(Label, {
637
+ htmlFor: "policy-scaffolder-table",
638
+ children: t("Table name")
639
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
640
+ "data-testid": "policy-scaffolder-table",
641
+ id: "policy-scaffolder-table",
642
+ name: "tableName",
643
+ onChange: onFieldChange,
644
+ value: form.tableName
645
+ }, void 0, false)]
646
+ }, void 0, true), /* @__PURE__ */ jsxDEV(Button, {
647
+ "data-testid": "policy-scaffolder-create",
648
+ disabled: busy || form.policyName.trim() === "" || form.tableName.trim() === "",
649
+ onClick: onSubmitScaffold,
650
+ size: "xs",
651
+ type: "button",
652
+ children: busy ? t("Creating…") : t("Create policy file")
653
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Button, {
654
+ onClick: reset,
655
+ size: "xs",
656
+ type: "button",
657
+ variant: "ghost",
658
+ children: t("Cancel")
659
+ }, void 0, false)]
660
+ }, void 0, true), mode === "wire" && /* @__PURE__ */ jsxDEV("div", {
661
+ className: "mt-3 flex flex-wrap items-end gap-2",
662
+ "data-testid": "policy-scaffolder-wire-form",
663
+ children: [/* @__PURE__ */ jsxDEV("div", {
664
+ className: "flex flex-col gap-1 text-xs",
665
+ children: [/* @__PURE__ */ jsxDEV(Label, {
666
+ htmlFor: "policy-scaffolder-file",
667
+ children: t("Procedure file path")
668
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
669
+ "data-testid": "policy-scaffolder-file",
670
+ id: "policy-scaffolder-file",
671
+ name: "filePath",
672
+ onChange: onFieldChange,
673
+ value: form.filePath
674
+ }, void 0, false)]
675
+ }, void 0, true), /* @__PURE__ */ jsxDEV("div", {
676
+ className: "flex flex-col gap-1 text-xs",
677
+ children: [/* @__PURE__ */ jsxDEV(Label, {
678
+ htmlFor: "policy-scaffolder-export",
679
+ children: t("Exported procedure")
680
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
681
+ "data-testid": "policy-scaffolder-export",
682
+ id: "policy-scaffolder-export",
683
+ name: "exportName",
684
+ onChange: onFieldChange,
685
+ value: form.exportName
686
+ }, void 0, false)]
687
+ }, void 0, true), /* @__PURE__ */ jsxDEV("div", {
688
+ className: "flex flex-col gap-1 text-xs",
689
+ children: [/* @__PURE__ */ jsxDEV(Label, {
690
+ htmlFor: "policy-scaffolder-policies",
691
+ children: t("Policy set identifier")
692
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Input, {
693
+ "data-testid": "policy-scaffolder-policies",
694
+ id: "policy-scaffolder-policies",
695
+ name: "policies",
696
+ onChange: onFieldChange,
697
+ value: form.policies
698
+ }, void 0, false)]
699
+ }, void 0, true), /* @__PURE__ */ jsxDEV(Button, {
700
+ "data-testid": "policy-scaffolder-apply",
701
+ disabled: busy || form.filePath.trim() === "" || form.exportName.trim() === "" || form.policies.trim() === "",
702
+ onClick: onSubmitWire,
703
+ size: "xs",
704
+ type: "button",
705
+ children: busy ? t("Wiring…") : t("Wire RLS")
706
+ }, void 0, false), /* @__PURE__ */ jsxDEV(Button, {
707
+ onClick: reset,
708
+ size: "xs",
709
+ type: "button",
710
+ variant: "ghost",
711
+ children: t("Cancel")
712
+ }, void 0, false)]
713
+ }, void 0, true), result?.kind === "error" && /* @__PURE__ */ jsxDEV("p", {
714
+ className: "mt-3 text-sm text-destructive",
715
+ "data-testid": "policy-scaffolder-error",
716
+ role: "alert",
717
+ children: result.message
718
+ }, void 0, false), result?.kind === "needs-manual-edit" && /* @__PURE__ */ jsxDEV("p", {
719
+ className: "mt-3 text-sm text-amber-600 dark:text-amber-500",
720
+ "data-testid": "policy-scaffolder-manual",
721
+ role: "alert",
722
+ children: t("This change must be made by hand — the scaffolder only adds new, deny-by-default rules.")
723
+ }, void 0, false), result?.kind === "ok" && /* @__PURE__ */ jsxDEV("div", {
724
+ className: "mt-3 flex flex-col gap-1",
725
+ "data-testid": "policy-scaffolder-ok",
726
+ children: [/* @__PURE__ */ jsxDEV("p", {
727
+ className: "text-sm text-emerald-600 dark:text-emerald-500",
728
+ children: t("Scaffolded {label} and reran codegen. Fill in the `when` predicates before relying on it.", {
729
+ label: result.label
730
+ })
731
+ }, void 0, false), result.diagnostics.length > 0 && /* @__PURE__ */ jsxDEV("ul", {
732
+ className: "flex flex-col gap-0.5",
733
+ "data-testid": "policy-scaffolder-diagnostics",
734
+ children: result.diagnostics.map((diagnostic) => /* @__PURE__ */ jsxDEV("li", {
735
+ className: "font-mono text-[11px] text-destructive",
736
+ children: diagnostic
737
+ }, diagnostic, false))
738
+ }, void 0, false)]
739
+ }, void 0, true)]
740
+ }, void 0, true)]
741
+ }, void 0, true);
742
+ };
743
+
744
+ const PermissionsPanel = ({
745
+ functions,
746
+ runAsIdentity = false,
747
+ schemaEditable = false
748
+ } = {}) => {
749
+ const t = useT();
750
+ const [prefill, setPrefill] = useState(void 0);
751
+ const onProbe = (_table, _operation, procedure) => {
752
+ if (procedure !== "") {
753
+ setPrefill((previous) => {
754
+ return {
755
+ functionPath: procedure,
756
+ nonce: (previous?.nonce ?? 0) + 1
757
+ };
758
+ });
759
+ }
760
+ };
761
+ return /* @__PURE__ */ jsxDEV("div", {
762
+ className: "flex flex-col gap-8",
763
+ "data-testid": "lunora-permissions",
764
+ children: [/* @__PURE__ */ jsxDEV("section", {
765
+ className: "flex flex-col gap-3",
766
+ children: [/* @__PURE__ */ jsxDEV("header", {
767
+ className: "border-b border-border px-4 py-3",
768
+ children: /* @__PURE__ */ jsxDEV("span", {
769
+ className: "font-mono text-[11px] tracking-wide text-muted-foreground uppercase",
770
+ children: t("Permissions")
771
+ }, void 0, false)
772
+ }, void 0, false), /* @__PURE__ */ jsxDEV(PermissionsMatrix, {
773
+ onProbe
774
+ }, void 0, false)]
775
+ }, void 0, true), /* @__PURE__ */ jsxDEV("section", {
776
+ className: "flex flex-col gap-3",
777
+ children: /* @__PURE__ */ jsxDEV(Card, {
778
+ className: "gap-0 py-0",
779
+ children: [/* @__PURE__ */ jsxDEV("header", {
780
+ className: "border-b border-border px-4 py-3",
781
+ children: /* @__PURE__ */ jsxDEV("span", {
782
+ className: "font-mono text-[11px] tracking-wide text-muted-foreground uppercase",
783
+ children: t("Probe")
784
+ }, void 0, false)
785
+ }, void 0, false), /* @__PURE__ */ jsxDEV(CardContent, {
786
+ className: "px-4 py-4",
787
+ children: [/* @__PURE__ */ jsxDEV("p", {
788
+ className: "mb-3 text-sm text-muted-foreground",
789
+ children: t("Pick a function and an identity, then run it to see the access outcome.")
790
+ }, void 0, false), /* @__PURE__ */ jsxDEV(PermissionsPlayground, {
791
+ functions,
792
+ prefill,
793
+ runAsIdentity
794
+ }, void 0, false)]
795
+ }, void 0, true)]
796
+ }, void 0, true)
797
+ }, void 0, false), schemaEditable && /* @__PURE__ */ jsxDEV("section", {
798
+ className: "flex flex-col gap-2",
799
+ children: /* @__PURE__ */ jsxDEV(PolicyScaffolder, {}, void 0, false)
800
+ }, void 0, false)]
801
+ }, void 0, true);
802
+ };
803
+
804
+ export { PermissionsPanel };