okengine 0.13.0 → 0.15.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.
- package/manifest.v1.schema.json +7 -1
- package/package.json +4 -5
- package/site/content/docs/ai/mcp.mdx +27 -2
- package/site/content/docs/elements/ai.mdx +59 -11
- package/site/content/docs/elements/channel.mdx +1 -0
- package/site/content/docs/elements/clock.mdx +30 -12
- package/site/content/docs/elements/flow.mdx +9 -3
- package/site/content/docs/elements/gate.mdx +25 -22
- package/site/content/docs/elements/signal.mdx +71 -25
- package/site/content/docs/elements/store.mdx +87 -28
- package/site/content/docs/elements/vault.mdx +1 -0
- package/site/content/docs/get-started/basic-usage.mdx +22 -13
- package/site/content/docs/get-started/index.mdx +5 -0
- package/site/content/docs/get-started/installation.mdx +2 -2
- package/site/content/docs/get-started/introduction.mdx +11 -3
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/project-structure.mdx +932 -0
- package/site/content/docs/reference/cli.md +13 -10
- package/site/content/docs/reference/client.mdx +25 -21
- package/site/content/docs/reference/fx.mdx +27 -22
- package/site/content/docs/reference/security.md +1 -1
- package/src/auth/bindings.ts +14 -5
- package/src/auth/cross-plane.ts +10 -3
- package/src/cli/ai-setup/ai-setup.test.ts +62 -0
- package/src/cli/ai-setup/apply.ts +32 -1
- package/src/cli/ai-setup/recommend.test.ts +25 -0
- package/src/cli/ai-setup/recommend.ts +8 -3
- package/src/cli/attach-host-console.test.ts +3 -2
- package/src/cli/attach-host-console.ts +3 -0
- package/src/cli/build.test.ts +4 -2
- package/src/cli/db-seed.ts +48 -15
- package/src/cli/db.test.ts +104 -0
- package/src/cli/db.ts +122 -10
- package/src/cli/dev-controls.test.ts +20 -0
- package/src/cli/dev-controls.ts +13 -0
- package/src/cli/dev.test.ts +15 -4
- package/src/cli/dev.ts +102 -29
- package/src/cli/ensure-drizzle-config.ts +2 -1
- package/src/cli/tui/DevLive.tsx +12 -2
- package/src/cli/tui/keys.test.ts +2 -1
- package/src/cli/tui/keys.ts +3 -2
- package/src/compiler/aot.test.ts +1 -1
- package/src/compiler/effects-infer.ts +58 -3
- package/src/compiler/extract.test.ts +458 -29
- package/src/compiler/extract.ts +387 -69
- package/src/compiler/flow-path.test.ts +96 -0
- package/src/compiler/flow-path.ts +173 -0
- package/src/compiler/generate-adopt.test.ts +112 -3
- package/src/compiler/generate-adopt.ts +280 -26
- package/src/compiler/response.ts +45 -1
- package/src/compiler/schema-from-ast.ts +609 -0
- package/src/console/server/ai.ts +5 -2
- package/src/console/server/bind.ts +2 -2
- package/src/console/server/console.test.ts +3 -0
- package/src/console/server/flows.ts +121 -40
- package/src/console/server/invoke-user-flow.test.ts +3 -1
- package/src/console/server/invoke-user-flow.ts +17 -2
- package/src/console/server/iso-at.test.ts +9 -0
- package/src/console/server/iso-at.ts +11 -0
- package/src/console/server/security-headers.ts +6 -3
- package/src/console/server/serve.ts +17 -2
- package/src/console/server/spa-proxy.test.ts +1 -0
- package/src/console/server/sql-catalog.ts +147 -7
- package/src/console/server/sql-rls.test.ts +16 -0
- package/src/console/server/state.ts +8 -0
- package/src/console/server/store.test.ts +7 -4
- package/src/console/server/store.ts +142 -58
- package/src/console/ui-next/dist/assets/agent-disclosure-C0X1fbWF.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-F1FI122b.js +1 -0
- package/src/console/ui-next/dist/assets/{call-pii-button-bqkxMrJH.js → call-pii-button-DEDCl_j3.js} +1 -1
- package/src/console/ui-next/dist/assets/collapsible-LPqGvfoz.js +1 -0
- package/src/console/ui-next/dist/assets/{confirm-sheet-DDCRmG62.js → confirm-sheet-3ptDzXbz.js} +1 -1
- package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
- package/src/console/ui-next/dist/assets/{explorer-empty-HjTnVQoR.js → explorer-empty-BvYrygyO.js} +1 -1
- package/src/console/ui-next/dist/assets/flows-page-Dluo1Bd7.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-CS_O8L-r.js} +1 -1
- package/src/console/ui-next/dist/assets/http-method--sWDdXSB.js +1 -0
- package/src/console/ui-next/dist/assets/index-Ca3HZMVq.js +66 -0
- package/src/console/ui-next/dist/assets/index-UtTDRQpS.css +2 -0
- package/src/console/ui-next/dist/assets/{link-DF7SZ9Ek.js → link-COeyggt-.js} +1 -1
- package/src/console/ui-next/dist/assets/observability-page-HK9-BO8z.js +4 -0
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
- package/src/console/ui-next/dist/assets/replica-lag-DBIFFf7d.js +18 -0
- package/src/console/ui-next/dist/assets/sheet-form-Dnwa5oOR.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-CoqwEIi0.js +1 -0
- package/src/console/ui-next/dist/assets/skeleton-BHcfDYcb.js +1 -0
- package/src/console/ui-next/dist/assets/store-page-BSohH3wM.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-CFIGRnnA.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-BgLl34w7.js +54 -0
- package/src/console/ui-next/dist/assets/units-page-B0cFE76A.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-BLbZhzyF.js} +1 -1
- package/src/console/ui-next/dist/assets/vault-page-B1SbYe10.js +2 -0
- package/src/console/ui-next/dist/index.html +6 -6
- package/src/console/ui-next/seed-invoke-host.ts +2 -0
- package/src/console/ui-next/src/client.ts +35 -7
- package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
- package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
- package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
- package/src/console/ui-next/src/features/flows/graph/build-flow-graph.test.ts +18 -0
- package/src/console/ui-next/src/features/flows/graph/build-flow-graph.ts +14 -1
- package/src/console/ui-next/src/features/flows/graph/neighborhood.test.ts +17 -0
- package/src/console/ui-next/src/features/flows/graph/neighborhood.ts +16 -3
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +3 -1
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +24 -0
- package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +9 -3
- package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +29 -2
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
- package/src/console/ui-next/src/features/observability/lib/ask-count.test.ts +25 -0
- package/src/console/ui-next/src/features/observability/lib/ask-count.ts +4 -1
- package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
- package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
- package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
- package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
- package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
- package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
- package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
- package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
- package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
- package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
- package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
- package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
- package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
- package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
- package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
- package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
- package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
- package/src/console/ui-next/src/features/units/detail/effects-summary.tsx +12 -4
- package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
- package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
- package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
- package/src/docker/compose-health.test.ts +27 -0
- package/src/docker/compose-health.ts +19 -3
- package/src/docker/docker.test.ts +4 -1
- package/src/docker/dockerfile.ts +1 -1
- package/src/docker/recipes/pgdog.ts +2 -0
- package/src/drivers/ai-anthropic.ts +5 -0
- package/src/drivers/ai-ollama.ts +49 -30
- package/src/drivers/ai-openai-compatible.ts +57 -46
- package/src/drivers/ai-providers.test.ts +3 -0
- package/src/drivers/bun-native-completeness.test.ts +7 -9
- package/src/drivers/clock-postgres.ts +13 -9
- package/src/drivers/instances-postgres.ts +4 -6
- package/src/drivers/journal-postgres.ts +13 -9
- package/src/drivers/pg-rls.test.ts +87 -0
- package/src/drivers/pg-rls.ts +233 -7
- package/src/drivers/pglite.ts +26 -3
- package/src/drivers/postgres.test.ts +23 -0
- package/src/drivers/postgres.ts +133 -9
- package/src/drivers/redis.ts +11 -4
- package/src/drivers/signal-redis.ts +24 -14
- package/src/drivers/signal-types.ts +2 -1
- package/src/drivers/types.ts +10 -0
- package/src/elements/ai/declare.ts +109 -0
- package/src/elements/ai/errors.test.ts +5 -1
- package/src/elements/ai/errors.ts +30 -2
- package/src/elements/ai/eval.ts +4 -6
- package/src/elements/ai/mcp-client.test.ts +206 -0
- package/src/elements/ai/mcp-client.ts +362 -0
- package/src/elements/ai/mcp-http.ts +159 -0
- package/src/elements/ai/mcp-mock.ts +134 -0
- package/src/elements/ai/mcp-protocol.ts +234 -0
- package/src/elements/ai/mcp-stdio.test.ts +50 -0
- package/src/elements/ai/mcp-stdio.ts +212 -0
- package/src/elements/ai/mcp-transport.ts +70 -0
- package/src/elements/ai/runtime.ts +159 -29
- package/src/elements/ai.test.ts +139 -0
- package/src/elements/ai.ts +16 -0
- package/src/elements/clock/declare.ts +26 -1
- package/src/elements/clock/health.test.ts +43 -0
- package/src/elements/clock/runtime.ts +55 -2
- package/src/elements/clock/schedule.ts +71 -142
- package/src/elements/clock.test.ts +9 -40
- package/src/elements/clock.ts +6 -1
- package/src/elements/gate/boot.ts +5 -5
- package/src/elements/gate/declare.ts +36 -4
- package/src/elements/gate/flatten.ts +1 -1
- package/src/elements/gate/runtime.ts +1 -1
- package/src/elements/index.ts +2 -0
- package/src/elements/signal/declare.ts +2 -1
- package/src/elements/signal/runtime.ts +16 -1
- package/src/elements/signal.ts +1 -0
- package/src/elements/store/cache.test.ts +2 -0
- package/src/elements/store/cache.ts +3 -3
- package/src/elements/store/declare.ts +9 -6
- package/src/elements/store/emit-drizzle.ts +44 -8
- package/src/elements/store/resource.test.ts +1 -1
- package/src/elements/store/rls-identity.test.ts +95 -0
- package/src/elements/store/rls-identity.ts +116 -0
- package/src/elements/store/runtime.ts +4 -0
- package/src/elements/store/schema-decl.test.ts +37 -0
- package/src/elements/store/schema-decl.ts +161 -0
- package/src/elements/store/seed.ts +1 -1
- package/src/elements/store/sql-condition.test.ts +12 -0
- package/src/elements/store/sql-condition.ts +26 -2
- package/src/elements/store/sql-rls-isolation.test.ts +165 -0
- package/src/elements/store/sql-rls-stamp.test.ts +117 -0
- package/src/elements/store/sql-session.ts +81 -3
- package/src/elements/store/upsert-app.test.ts +1 -1
- package/src/elements/store.ts +14 -0
- package/src/elements/vault/builtin-adapter.ts +17 -0
- package/src/full.ts +2 -0
- package/src/http.ts +20 -3
- package/src/index.ts +14 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
- package/src/kernel/adopt-routes.ts +30 -2
- package/src/kernel/app.ts +227 -77
- package/src/kernel/auto-cache.test.ts +8 -8
- package/src/kernel/auto-registry.test.ts +54 -9
- package/src/kernel/boot-bind/ai.ts +24 -0
- package/src/kernel/boot-bind/clock.ts +2 -0
- package/src/kernel/boot.test.ts +4 -4
- package/src/kernel/boot.ts +5 -3
- package/src/kernel/effects-stamping.test.ts +3 -3
- package/src/kernel/element-registries.ts +15 -5
- package/src/kernel/errors.ts +24 -0
- package/src/kernel/flow-units.ts +64 -0
- package/src/kernel/flow.ts +29 -17
- package/src/kernel/fx-dead-letters.test.ts +77 -0
- package/src/kernel/fx.test.ts +22 -0
- package/src/kernel/fx.ts +135 -6
- package/src/kernel/horizontal-child.ts +4 -4
- package/src/kernel/http-path-pending.ts +20 -0
- package/src/kernel/http-resource.ts +4 -0
- package/src/kernel/http-stream.test.ts +174 -0
- package/src/kernel/index.ts +2 -0
- package/src/kernel/pipeline.test.ts +2 -2
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/plugin-needs.test.ts +1 -1
- package/src/kernel/ready.test.ts +2 -2
- package/src/kernel/resource-mount.test.ts +14 -0
- package/src/kernel/run-duration.test.ts +2 -2
- package/src/kernel/stamp-http.test.ts +79 -0
- package/src/kernel/stamp-http.ts +75 -0
- package/src/kernel/triggers.ts +52 -27
- package/src/manifest/mcp-ref.ts +88 -0
- package/src/manifest/types.ts +41 -2
- package/src/manifest/validate.test.ts +20 -0
- package/src/mcp/docs-server.ts +1 -1
- package/src/mcp/server.ts +1 -1
- package/src/plugins/compression.test.ts +21 -0
- package/src/plugins/compression.ts +1 -0
- package/src/release/measure.ts +2 -2
- package/src/runtime/bun.ts +41 -4
- package/src/runtime/json-code-block.test.ts +2 -2
- package/src/runtime/serve.test.ts +4 -4
- package/src/term.test.ts +52 -4
- package/src/term.ts +125 -17
- package/src/test/reset-element-registries.ts +10 -3
- package/src/cli/ask-seed.test.ts +0 -96
- package/src/cli/ask-seed.ts +0 -82
- package/src/console/ui-next/dist/assets/cache-glyph-CLPBqZeb.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-Dg8CTE29.js +0 -1
- package/src/console/ui-next/dist/assets/http-method-BJ92Z_ke.js +0 -1
- package/src/console/ui-next/dist/assets/index-Bp-R7jtM.js +0 -66
- package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
- package/src/console/ui-next/dist/assets/observability-page-DAnpEaq1.js +0 -4
- package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
- package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
- package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
- package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
- package/src/console/ui-next/dist/assets/skeleton-CL_X0GCj.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
- package/src/console/ui-next/dist/assets/trace-detail-sheet-D16lWQMt.js +0 -2
- package/src/console/ui-next/dist/assets/units-page-BtQ0bqMe.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-Ca-MvcmJ.js +0 -2
|
@@ -55,6 +55,7 @@ import { ToolbarTip } from "@/components/ui/toolbar-tip.tsx";
|
|
|
55
55
|
import { buildStoreGridModel, type StoreGridRow } from "../lib/grid-model.ts";
|
|
56
56
|
import { parseIndexQuery } from "../lib/index-query.ts";
|
|
57
57
|
import { childCatalogKind, groupSqlChildren, isSqlCatalogChild } from "../lib/sql-catalog.ts";
|
|
58
|
+
import { QueryGateMenu } from "../query/query-gate-menu.tsx";
|
|
58
59
|
import { TouchedByLists } from "./touched-by-section.tsx";
|
|
59
60
|
|
|
60
61
|
/** Browse page sizes offered in the Rows select. */
|
|
@@ -116,6 +117,10 @@ export function BrowseSection({
|
|
|
116
117
|
const [indexOpen, setIndexOpen] = useState(false);
|
|
117
118
|
const [triggerOpen, setTriggerOpen] = useState(false);
|
|
118
119
|
const [insertOpen, setInsertOpen] = useState(false);
|
|
120
|
+
const [rlsAs, setRlsAs] = useState<{ asGate: string | null; asUserId: string | null }>({
|
|
121
|
+
asGate: null,
|
|
122
|
+
asUserId: null,
|
|
123
|
+
});
|
|
119
124
|
|
|
120
125
|
const debouncedIndexText = useDebouncedValue(indexText, INDEX_SEARCH_DEBOUNCE_MS);
|
|
121
126
|
const indexQuery = useMemo(() => parseIndexQuery(debouncedIndexText), [debouncedIndexText]);
|
|
@@ -133,8 +138,25 @@ export function BrowseSection({
|
|
|
133
138
|
: {}),
|
|
134
139
|
...(store.facet === "index" && indexQuery.kind === "text" ? { q: indexQuery.q, topK } : {}),
|
|
135
140
|
...(store.facet === "sql" && !piiMasked ? { revealPii: true } : {}),
|
|
141
|
+
...(store.facet === "sql" && !isSqlCatalogChild(child) && rlsAs.asGate
|
|
142
|
+
? { asGate: rlsAs.asGate }
|
|
143
|
+
: {}),
|
|
144
|
+
...(store.facet === "sql" && !isSqlCatalogChild(child) && rlsAs.asUserId
|
|
145
|
+
? { asUserId: rlsAs.asUserId }
|
|
146
|
+
: {}),
|
|
136
147
|
};
|
|
137
|
-
}, [
|
|
148
|
+
}, [
|
|
149
|
+
store.ref,
|
|
150
|
+
store.facet,
|
|
151
|
+
child,
|
|
152
|
+
tenant,
|
|
153
|
+
tenantReady,
|
|
154
|
+
limit,
|
|
155
|
+
indexQuery,
|
|
156
|
+
topK,
|
|
157
|
+
piiMasked,
|
|
158
|
+
rlsAs,
|
|
159
|
+
]);
|
|
138
160
|
|
|
139
161
|
const browse = useStoreQuery(queryInput, tenantReady);
|
|
140
162
|
const deleteMutation = useStoreDelete();
|
|
@@ -191,6 +213,8 @@ export function BrowseSection({
|
|
|
191
213
|
c.key === "with_check" ||
|
|
192
214
|
c.key === "command" ||
|
|
193
215
|
c.key === "permissive",
|
|
216
|
+
...(c.key === "origin" ? { label: "Origin" } : {}),
|
|
217
|
+
...(c.key === "code" ? { label: "Code" } : {}),
|
|
194
218
|
})),
|
|
195
219
|
};
|
|
196
220
|
}
|
|
@@ -269,6 +293,9 @@ export function BrowseSection({
|
|
|
269
293
|
|
|
270
294
|
const toolbarExtras = (
|
|
271
295
|
<>
|
|
296
|
+
{store.facet === "sql" && !isSqlCatalogChild(child) ? (
|
|
297
|
+
<QueryGateMenu manifest={manifest} value={rlsAs} onChange={setRlsAs} />
|
|
298
|
+
) : null}
|
|
272
299
|
<Tooltip>
|
|
273
300
|
<TooltipTrigger
|
|
274
301
|
render={(props) => (
|
|
@@ -576,9 +603,26 @@ export function BrowseSection({
|
|
|
576
603
|
}
|
|
577
604
|
insertOpen={insertOpen}
|
|
578
605
|
onInsertOpenChange={setInsertOpen}
|
|
606
|
+
asGate={rlsAs.asGate}
|
|
607
|
+
asUserId={rlsAs.asUserId}
|
|
579
608
|
onDeleteRows={
|
|
580
609
|
authReadOnly || extensionCatalog ? undefined : (rows) => setDeleting(rows)
|
|
581
610
|
}
|
|
611
|
+
onViewportContextMenu={
|
|
612
|
+
store.ref === "sql:oke_console"
|
|
613
|
+
? undefined
|
|
614
|
+
: policyCatalog
|
|
615
|
+
? () => setPolicyOpen(true)
|
|
616
|
+
: functionCatalog
|
|
617
|
+
? () => setFunctionOpen(true)
|
|
618
|
+
: indexCatalog
|
|
619
|
+
? () => setIndexOpen(true)
|
|
620
|
+
: triggerCatalog
|
|
621
|
+
? () => setTriggerOpen(true)
|
|
622
|
+
: store.facet === "sql" && !isSqlCatalogChild(child)
|
|
623
|
+
? () => setInsertOpen(true)
|
|
624
|
+
: undefined
|
|
625
|
+
}
|
|
582
626
|
/>
|
|
583
627
|
</div>
|
|
584
628
|
) : null}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
CheckmarkCircle02Icon,
|
|
10
10
|
Folder01Icon,
|
|
11
11
|
SecurityCheckIcon,
|
|
12
|
+
ShieldEnergyIcon,
|
|
13
|
+
ShieldOff,
|
|
12
14
|
Timer01Icon,
|
|
13
15
|
} from "@hugeicons/core-free-icons";
|
|
14
16
|
import { HugeiconsIcon } from "@hugeicons/react";
|
|
@@ -24,8 +26,13 @@ import { formatDuration } from "@/features/flows/traces/format-duration.ts";
|
|
|
24
26
|
import { cn } from "@/lib/utils";
|
|
25
27
|
import { filesDriverLabel, filesDriverOrigin } from "../lib/files-origin.ts";
|
|
26
28
|
import { latestReplicaLagFromRuns } from "../lib/replica-lag.ts";
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
+
import {
|
|
30
|
+
isSqlCatalogChild,
|
|
31
|
+
storeChildLabel,
|
|
32
|
+
storeChildRlsPolicyCount,
|
|
33
|
+
storeChildShowsRls,
|
|
34
|
+
} from "../lib/sql-catalog.ts";
|
|
35
|
+
import { resourceHeaderChildRef, STORE_FACET_SPECS } from "../lib/store-tree.ts";
|
|
29
36
|
import { BrowseSection } from "./browse-section.tsx";
|
|
30
37
|
|
|
31
38
|
/** Props for {@link ResourcePanel}. */
|
|
@@ -77,7 +84,12 @@ export function ResourcePanel({
|
|
|
77
84
|
const lagTone = lagMs != null ? durationTone(lagMs) : null;
|
|
78
85
|
|
|
79
86
|
const files = store.facet === "files";
|
|
80
|
-
const
|
|
87
|
+
const showsRls = storeChildShowsRls(child);
|
|
88
|
+
const rlsOn = child.rls === true;
|
|
89
|
+
const rlsCount = storeChildRlsPolicyCount(child, manifest, store.name);
|
|
90
|
+
const showStatus = Boolean(
|
|
91
|
+
drift || lagMs !== null || store.contentAddressed || piiCount > 0 || showsRls,
|
|
92
|
+
);
|
|
81
93
|
|
|
82
94
|
return (
|
|
83
95
|
<div className="flex h-full min-h-0 flex-col overflow-hidden" data-slot="resource-panel">
|
|
@@ -124,18 +136,7 @@ export function ResourcePanel({
|
|
|
124
136
|
<CopyInlineButton value={child.effectRef} label="Copy effect ref" />
|
|
125
137
|
</>
|
|
126
138
|
) : (
|
|
127
|
-
|
|
128
|
-
<span className="shrink-0 font-mono text-[11px] leading-none text-muted-foreground">
|
|
129
|
-
{store.ref}
|
|
130
|
-
</span>
|
|
131
|
-
<span aria-hidden className="text-border">
|
|
132
|
-
·
|
|
133
|
-
</span>
|
|
134
|
-
<code className="min-w-0 truncate font-mono text-[11px] leading-none text-foreground/70">
|
|
135
|
-
{child.effectRef}
|
|
136
|
-
</code>
|
|
137
|
-
<CopyInlineButton value={child.effectRef} label="Copy effect ref" />
|
|
138
|
-
</>
|
|
139
|
+
<ResourceRefSubtitle storeRef={store.ref} effectRef={child.effectRef} />
|
|
139
140
|
)}
|
|
140
141
|
</>
|
|
141
142
|
}
|
|
@@ -154,6 +155,30 @@ export function ResourcePanel({
|
|
|
154
155
|
</StatusChip>
|
|
155
156
|
) : null}
|
|
156
157
|
{store.contentAddressed ? <StatusChip>content-addressed</StatusChip> : null}
|
|
158
|
+
{showsRls ? (
|
|
159
|
+
<StatusChip
|
|
160
|
+
tone={rlsOn ? "emerald" : "neutral"}
|
|
161
|
+
icon={
|
|
162
|
+
<HugeiconsIcon
|
|
163
|
+
icon={rlsOn ? ShieldEnergyIcon : ShieldOff}
|
|
164
|
+
className="size-3"
|
|
165
|
+
aria-hidden
|
|
166
|
+
/>
|
|
167
|
+
}
|
|
168
|
+
title={
|
|
169
|
+
rlsOn
|
|
170
|
+
? rlsCount > 0
|
|
171
|
+
? `Row-level security enabled · ${rlsCount} ${rlsCount === 1 ? "policy" : "policies"}`
|
|
172
|
+
: "Row-level security enabled"
|
|
173
|
+
: "Row-level security disabled"
|
|
174
|
+
}
|
|
175
|
+
role="status"
|
|
176
|
+
data-slot="resource-rls"
|
|
177
|
+
data-rls={rlsOn ? "on" : "off"}
|
|
178
|
+
>
|
|
179
|
+
{rlsOn && rlsCount > 0 ? `${rlsCount} RLS` : "RLS"}
|
|
180
|
+
</StatusChip>
|
|
181
|
+
) : null}
|
|
157
182
|
{piiCount > 0 ? (
|
|
158
183
|
<StatusChip
|
|
159
184
|
tone={piiMasked ? "sky" : "amber"}
|
|
@@ -233,6 +258,34 @@ export function ResourcePanel({
|
|
|
233
258
|
);
|
|
234
259
|
}
|
|
235
260
|
|
|
261
|
+
function ResourceRefSubtitle({
|
|
262
|
+
storeRef,
|
|
263
|
+
effectRef,
|
|
264
|
+
}: {
|
|
265
|
+
readonly storeRef: string;
|
|
266
|
+
readonly effectRef: string;
|
|
267
|
+
}): JSX.Element {
|
|
268
|
+
const childRef = resourceHeaderChildRef(storeRef, effectRef);
|
|
269
|
+
return (
|
|
270
|
+
<>
|
|
271
|
+
<span className="shrink-0 font-mono text-[11px] leading-none text-muted-foreground">
|
|
272
|
+
{storeRef}
|
|
273
|
+
</span>
|
|
274
|
+
{childRef ? (
|
|
275
|
+
<>
|
|
276
|
+
<span aria-hidden className="text-border">
|
|
277
|
+
·
|
|
278
|
+
</span>
|
|
279
|
+
<code className="min-w-0 truncate font-mono text-[11px] leading-none text-foreground/70">
|
|
280
|
+
{childRef}
|
|
281
|
+
</code>
|
|
282
|
+
</>
|
|
283
|
+
) : null}
|
|
284
|
+
<CopyInlineButton value={effectRef} label="Copy effect ref" />
|
|
285
|
+
</>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
236
289
|
type ChipTone = "neutral" | "emerald" | "amber" | "sky";
|
|
237
290
|
|
|
238
291
|
const CHIP_TONES: Record<ChipTone, string> = {
|
|
@@ -251,6 +304,7 @@ interface StatusChipProps {
|
|
|
251
304
|
readonly role?: string;
|
|
252
305
|
readonly "data-slot"?: string;
|
|
253
306
|
readonly "data-drifted"?: string;
|
|
307
|
+
readonly "data-rls"?: string;
|
|
254
308
|
readonly pressed?: boolean;
|
|
255
309
|
readonly onClick?: () => void;
|
|
256
310
|
readonly ariaLabel?: string;
|
|
@@ -270,6 +324,7 @@ function StatusChip({
|
|
|
270
324
|
role,
|
|
271
325
|
"data-slot": dataSlot,
|
|
272
326
|
"data-drifted": dataDrifted,
|
|
327
|
+
"data-rls": dataRls,
|
|
273
328
|
pressed,
|
|
274
329
|
onClick,
|
|
275
330
|
ariaLabel,
|
|
@@ -290,13 +345,20 @@ function StatusChip({
|
|
|
290
345
|
aria-label={ariaLabel}
|
|
291
346
|
data-slot={dataSlot}
|
|
292
347
|
data-drifted={dataDrifted}
|
|
348
|
+
data-rls={dataRls}
|
|
293
349
|
onClick={onClick}
|
|
294
350
|
>
|
|
295
351
|
{icon}
|
|
296
352
|
{children}
|
|
297
353
|
</button>
|
|
298
354
|
) : (
|
|
299
|
-
<span
|
|
355
|
+
<span
|
|
356
|
+
className={className}
|
|
357
|
+
role={role}
|
|
358
|
+
data-slot={dataSlot}
|
|
359
|
+
data-drifted={dataDrifted}
|
|
360
|
+
data-rls={dataRls}
|
|
361
|
+
>
|
|
300
362
|
{icon}
|
|
301
363
|
{children}
|
|
302
364
|
</span>
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Store row detail Sheet — Fields / JSON tabs
|
|
2
|
+
* Store row detail Sheet — Fields / JSON tabs, in-sheet edit, delete.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { useEffect, useMemo, useState, type JSX } from "react";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
Cancel01Icon,
|
|
8
|
+
Delete02Icon,
|
|
9
|
+
PencilEdit01Icon,
|
|
10
|
+
Tick02Icon,
|
|
11
|
+
} from "@hugeicons/core-free-icons";
|
|
7
12
|
import { HugeiconsIcon } from "@hugeicons/react";
|
|
8
13
|
import { HighlightedJson } from "@/components/highlighted-json";
|
|
9
14
|
import { Button } from "@/components/ui/button";
|
|
15
|
+
import { Input } from "@/components/ui/input";
|
|
16
|
+
import { SHEET_CONTROL, SheetError } from "@/components/ui/sheet-form.tsx";
|
|
10
17
|
import {
|
|
11
18
|
Sheet,
|
|
12
19
|
SheetContent,
|
|
@@ -15,9 +22,15 @@ import {
|
|
|
15
22
|
SheetTitle,
|
|
16
23
|
} from "@/components/ui/sheet";
|
|
17
24
|
import { cn } from "@/lib/utils";
|
|
25
|
+
import { useStoreEdit } from "../data/use-store-edit.ts";
|
|
18
26
|
import { RevealCell } from "./reveal-cell.tsx";
|
|
19
|
-
import {
|
|
20
|
-
|
|
27
|
+
import {
|
|
28
|
+
formatGridCell,
|
|
29
|
+
type StoreGridColumn,
|
|
30
|
+
type StoreGridModel,
|
|
31
|
+
type StoreGridRow,
|
|
32
|
+
} from "../lib/grid-model.ts";
|
|
33
|
+
import { isStorePiiMask, parseStoreCellDraft, sanitizeStorePatch } from "../lib/patch.ts";
|
|
21
34
|
import { isRtlText } from "../lib/rtl.ts";
|
|
22
35
|
|
|
23
36
|
/** Props for {@link StoreRowDetailSheet}. */
|
|
@@ -31,12 +44,13 @@ export interface StoreRowDetailSheetProps {
|
|
|
31
44
|
readonly childName: string;
|
|
32
45
|
readonly tenant?: string | null;
|
|
33
46
|
readonly masked?: boolean;
|
|
34
|
-
readonly
|
|
47
|
+
readonly asGate?: string | null;
|
|
48
|
+
readonly asUserId?: string | null;
|
|
35
49
|
readonly onDeleteRow?: (row: StoreGridRow) => void;
|
|
36
50
|
}
|
|
37
51
|
|
|
38
52
|
/**
|
|
39
|
-
* Right-side detail for one grid row — Fields (Reveal/edit) and full JSON.
|
|
53
|
+
* Right-side detail for one grid row — Fields (Reveal / edit) and full JSON.
|
|
40
54
|
*
|
|
41
55
|
* @param props - Selected row + facet + mutation callbacks
|
|
42
56
|
*/
|
|
@@ -50,13 +64,28 @@ export function StoreRowDetailSheet({
|
|
|
50
64
|
childName,
|
|
51
65
|
tenant,
|
|
52
66
|
masked = false,
|
|
53
|
-
|
|
67
|
+
asGate = null,
|
|
68
|
+
asUserId = null,
|
|
54
69
|
onDeleteRow,
|
|
55
70
|
}: StoreRowDetailSheetProps): JSX.Element {
|
|
71
|
+
const edit = useStoreEdit();
|
|
56
72
|
const [tab, setTab] = useState<"fields" | "json">("fields");
|
|
73
|
+
const [editing, setEditing] = useState(false);
|
|
74
|
+
const [draft, setDraft] = useState<Record<string, string>>({});
|
|
75
|
+
const [error, setError] = useState<string | null>(null);
|
|
76
|
+
|
|
77
|
+
const editableColumns = useMemo(
|
|
78
|
+
() => model.columns.filter((col) => col.editable),
|
|
79
|
+
[model.columns],
|
|
80
|
+
);
|
|
81
|
+
const canEdit =
|
|
82
|
+
model.editable && (facet === "sql" || facet === "kv") && editableColumns.length > 0;
|
|
57
83
|
|
|
58
84
|
useEffect(() => {
|
|
59
|
-
if (open)
|
|
85
|
+
if (!open) return;
|
|
86
|
+
setTab("fields");
|
|
87
|
+
setEditing(false);
|
|
88
|
+
setError(null);
|
|
60
89
|
}, [open, row]);
|
|
61
90
|
|
|
62
91
|
const json = useMemo(() => {
|
|
@@ -70,7 +99,45 @@ export function StoreRowDetailSheet({
|
|
|
70
99
|
|
|
71
100
|
if (!row) return <></>;
|
|
72
101
|
|
|
73
|
-
const
|
|
102
|
+
const startEdit = (): void => {
|
|
103
|
+
const next: Record<string, string> = {};
|
|
104
|
+
for (const col of editableColumns) {
|
|
105
|
+
next[col.key] = draftFromCell(col.type, row.cells[col.key]);
|
|
106
|
+
}
|
|
107
|
+
setDraft(next);
|
|
108
|
+
setError(null);
|
|
109
|
+
setEditing(true);
|
|
110
|
+
setTab("fields");
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const save = async (): Promise<void> => {
|
|
114
|
+
setError(null);
|
|
115
|
+
const patch: Record<string, unknown> = {};
|
|
116
|
+
for (const col of editableColumns) {
|
|
117
|
+
const text = draft[col.key] ?? "";
|
|
118
|
+
if (col.pii && text.trim() === "") continue;
|
|
119
|
+
if (text === draftFromCell(col.type, row.cells[col.key])) continue;
|
|
120
|
+
patch[col.key] = parseStoreCellDraft(col.type, text);
|
|
121
|
+
}
|
|
122
|
+
if (Object.keys(patch).length === 0) {
|
|
123
|
+
setEditing(false);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
await edit.mutateAsync({
|
|
128
|
+
ref: storeRef,
|
|
129
|
+
...(facet === "sql" ? { child: childName, id: row.id } : { key: row.id }),
|
|
130
|
+
...(tenant ? { tenant } : {}),
|
|
131
|
+
...(asGate ? { asGate } : {}),
|
|
132
|
+
...(asUserId ? { asUserId } : {}),
|
|
133
|
+
patch: sanitizeStorePatch(patch),
|
|
134
|
+
commit: true,
|
|
135
|
+
});
|
|
136
|
+
setEditing(false);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
139
|
+
}
|
|
140
|
+
};
|
|
74
141
|
|
|
75
142
|
return (
|
|
76
143
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
@@ -79,11 +146,12 @@ export function StoreRowDetailSheet({
|
|
|
79
146
|
showOverlay={false}
|
|
80
147
|
className="gap-0 p-0 sm:max-w-md"
|
|
81
148
|
data-slot="store-row-detail-sheet"
|
|
149
|
+
data-editing={editing ? "true" : "false"}
|
|
82
150
|
>
|
|
83
151
|
<SheetHeader className="gap-2 border-b border-border/50">
|
|
84
152
|
<div className="flex flex-col gap-0.5 pr-8">
|
|
85
153
|
<SheetTitle className="text-sm">
|
|
86
|
-
Row <span className="font-mono">{row.id}</span>
|
|
154
|
+
{editing ? "Edit" : "Row"} <span className="font-mono">{row.id}</span>
|
|
87
155
|
</SheetTitle>
|
|
88
156
|
<SheetDescription className="font-mono text-[11px]">
|
|
89
157
|
{storeRef}
|
|
@@ -106,6 +174,7 @@ export function StoreRowDetailSheet({
|
|
|
106
174
|
{model.columns.map((col) => {
|
|
107
175
|
const value = row.cells[col.key];
|
|
108
176
|
const maskedPii = col.pii && masked && isStorePiiMask(value);
|
|
177
|
+
const fieldEditable = editing && col.editable;
|
|
109
178
|
return (
|
|
110
179
|
<li key={col.key} className="flex items-baseline gap-3 py-1.5">
|
|
111
180
|
<span className="flex w-32 shrink-0 items-center gap-1.5">
|
|
@@ -130,7 +199,30 @@ export function StoreRowDetailSheet({
|
|
|
130
199
|
</span>
|
|
131
200
|
) : null}
|
|
132
201
|
</span>
|
|
133
|
-
{
|
|
202
|
+
{fieldEditable ? (
|
|
203
|
+
col.type === "json" ? (
|
|
204
|
+
<textarea
|
|
205
|
+
value={draft[col.key] ?? ""}
|
|
206
|
+
onChange={(event) =>
|
|
207
|
+
setDraft((prev) => ({ ...prev, [col.key]: event.target.value }))
|
|
208
|
+
}
|
|
209
|
+
aria-label={col.key}
|
|
210
|
+
rows={3}
|
|
211
|
+
className="min-h-16 min-w-0 flex-1 rounded-none border-0 bg-transparent font-mono text-[11px] outline-none"
|
|
212
|
+
/>
|
|
213
|
+
) : (
|
|
214
|
+
<Input
|
|
215
|
+
value={draft[col.key] ?? ""}
|
|
216
|
+
onChange={(event) =>
|
|
217
|
+
setDraft((prev) => ({ ...prev, [col.key]: event.target.value }))
|
|
218
|
+
}
|
|
219
|
+
type={col.type === "integer" || col.type === "number" ? "number" : "text"}
|
|
220
|
+
aria-label={col.key}
|
|
221
|
+
flat
|
|
222
|
+
className={cn(SHEET_CONTROL, "min-w-0 flex-1 px-0 font-mono")}
|
|
223
|
+
/>
|
|
224
|
+
)
|
|
225
|
+
) : maskedPii ? (
|
|
134
226
|
<RevealCell
|
|
135
227
|
refName={storeRef}
|
|
136
228
|
child={childName}
|
|
@@ -162,13 +254,42 @@ export function StoreRowDetailSheet({
|
|
|
162
254
|
)}
|
|
163
255
|
</div>
|
|
164
256
|
|
|
257
|
+
{error ? <SheetError slot="row-detail-error">{error}</SheetError> : null}
|
|
258
|
+
|
|
165
259
|
<div className="mt-auto flex items-center justify-between gap-2 border-t border-border/50 px-4 py-2.5">
|
|
166
|
-
{
|
|
260
|
+
{editing ? (
|
|
261
|
+
<div className="flex items-center gap-2">
|
|
262
|
+
<Button
|
|
263
|
+
type="button"
|
|
264
|
+
variant="outline"
|
|
265
|
+
size="sm"
|
|
266
|
+
onClick={() => {
|
|
267
|
+
setEditing(false);
|
|
268
|
+
setError(null);
|
|
269
|
+
}}
|
|
270
|
+
disabled={edit.isPending}
|
|
271
|
+
data-slot="row-detail-cancel"
|
|
272
|
+
>
|
|
273
|
+
<HugeiconsIcon icon={Cancel01Icon} className="size-3.5" aria-hidden />
|
|
274
|
+
Cancel
|
|
275
|
+
</Button>
|
|
276
|
+
<Button
|
|
277
|
+
type="button"
|
|
278
|
+
size="sm"
|
|
279
|
+
onClick={() => void save()}
|
|
280
|
+
disabled={edit.isPending}
|
|
281
|
+
data-slot="row-detail-save"
|
|
282
|
+
>
|
|
283
|
+
<HugeiconsIcon icon={Tick02Icon} className="size-3.5" aria-hidden />
|
|
284
|
+
{edit.isPending ? "Saving…" : "Save"}
|
|
285
|
+
</Button>
|
|
286
|
+
</div>
|
|
287
|
+
) : canEdit ? (
|
|
167
288
|
<Button
|
|
168
289
|
type="button"
|
|
169
290
|
variant="outline"
|
|
170
291
|
size="sm"
|
|
171
|
-
onClick={
|
|
292
|
+
onClick={startEdit}
|
|
172
293
|
data-slot="row-detail-edit"
|
|
173
294
|
>
|
|
174
295
|
<HugeiconsIcon icon={PencilEdit01Icon} className="size-3.5" aria-hidden />
|
|
@@ -177,7 +298,7 @@ export function StoreRowDetailSheet({
|
|
|
177
298
|
) : (
|
|
178
299
|
<span />
|
|
179
300
|
)}
|
|
180
|
-
{onDeleteRow ? (
|
|
301
|
+
{!editing && onDeleteRow ? (
|
|
181
302
|
<Button
|
|
182
303
|
type="button"
|
|
183
304
|
variant="destructive"
|
|
@@ -195,6 +316,26 @@ export function StoreRowDetailSheet({
|
|
|
195
316
|
);
|
|
196
317
|
}
|
|
197
318
|
|
|
319
|
+
/** Seed / compare an edit draft without turning `null` into the display em dash. */
|
|
320
|
+
function draftFromCell(type: StoreGridColumn["type"], raw: unknown): string {
|
|
321
|
+
if (raw === null || raw === undefined) return "";
|
|
322
|
+
if (isStorePiiMask(raw)) return "";
|
|
323
|
+
if (type === "json") {
|
|
324
|
+
try {
|
|
325
|
+
return JSON.stringify(raw, null, 2);
|
|
326
|
+
} catch {
|
|
327
|
+
return "";
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (typeof raw === "string") return raw;
|
|
331
|
+
if (typeof raw === "number" || typeof raw === "boolean") return String(raw);
|
|
332
|
+
try {
|
|
333
|
+
return JSON.stringify(raw);
|
|
334
|
+
} catch {
|
|
335
|
+
return "";
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
198
339
|
function TabButton({
|
|
199
340
|
active,
|
|
200
341
|
onClick,
|