okengine 0.14.1 → 0.16.0

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 (285) hide show
  1. package/package.json +3 -4
  2. package/site/content/docs/elements/ai.mdx +2 -1
  3. package/site/content/docs/elements/channel.mdx +1 -0
  4. package/site/content/docs/elements/clock.mdx +3 -0
  5. package/site/content/docs/elements/flow.mdx +9 -0
  6. package/site/content/docs/elements/gate.mdx +80 -22
  7. package/site/content/docs/elements/signal.mdx +4 -4
  8. package/site/content/docs/elements/store.mdx +84 -27
  9. package/site/content/docs/elements/vault.mdx +4 -0
  10. package/site/content/docs/get-started/basic-usage.mdx +22 -13
  11. package/site/content/docs/get-started/index.mdx +5 -0
  12. package/site/content/docs/get-started/introduction.mdx +11 -3
  13. package/site/content/docs/get-started/meta.json +1 -1
  14. package/site/content/docs/get-started/project-structure.mdx +932 -0
  15. package/site/content/docs/reference/cli.md +13 -10
  16. package/site/content/docs/reference/client.mdx +28 -21
  17. package/site/content/docs/reference/fx.mdx +29 -18
  18. package/site/content/docs/reference/security.md +2 -2
  19. package/src/auth/api-key-sql.test.ts +47 -0
  20. package/src/auth/api-key-sql.ts +254 -0
  21. package/src/auth/api-keys.ts +273 -14
  22. package/src/auth/auth.test.ts +56 -0
  23. package/src/auth/bindings.ts +14 -5
  24. package/src/auth/config.ts +5 -5
  25. package/src/auth/cross-plane.ts +10 -3
  26. package/src/auth/index.ts +25 -1
  27. package/src/auth/schema.ts +8 -0
  28. package/src/cli/ai-setup/ai-setup.test.ts +62 -0
  29. package/src/cli/ai-setup/apply.ts +32 -1
  30. package/src/cli/attach-host-console.test.ts +40 -2
  31. package/src/cli/attach-host-console.ts +9 -0
  32. package/src/cli/build.test.ts +4 -2
  33. package/src/cli/db-seed.ts +48 -15
  34. package/src/cli/db.test.ts +104 -0
  35. package/src/cli/db.ts +122 -10
  36. package/src/cli/dev-controls.test.ts +20 -0
  37. package/src/cli/dev-controls.ts +13 -0
  38. package/src/cli/dev.test.ts +15 -4
  39. package/src/cli/dev.ts +102 -29
  40. package/src/cli/ensure-drizzle-config.ts +2 -1
  41. package/src/cli/tui/DevLive.tsx +12 -2
  42. package/src/cli/tui/keys.test.ts +2 -1
  43. package/src/cli/tui/keys.ts +3 -2
  44. package/src/compiler/aot.test.ts +1 -1
  45. package/src/compiler/effects-infer.ts +17 -2
  46. package/src/compiler/extract.test.ts +409 -33
  47. package/src/compiler/extract.ts +318 -67
  48. package/src/compiler/flow-path.test.ts +96 -0
  49. package/src/compiler/flow-path.ts +173 -0
  50. package/src/compiler/generate-adopt.test.ts +112 -3
  51. package/src/compiler/generate-adopt.ts +280 -26
  52. package/src/compiler/schema-from-ast.ts +609 -0
  53. package/src/console/server/access.test.ts +116 -8
  54. package/src/console/server/access.ts +70 -23
  55. package/src/console/server/app.ts +1 -0
  56. package/src/console/server/bind.ts +2 -2
  57. package/src/console/server/console.test.ts +3 -0
  58. package/src/console/server/dev-identities.test.ts +127 -0
  59. package/src/console/server/dev-identities.ts +229 -0
  60. package/src/console/server/flows-invoke.test.ts +21 -4
  61. package/src/console/server/flows.ts +233 -98
  62. package/src/console/server/gates.test.ts +24 -0
  63. package/src/console/server/gates.ts +8 -21
  64. package/src/console/server/index.ts +1 -0
  65. package/src/console/server/invoke-user-flow.test.ts +3 -1
  66. package/src/console/server/invoke-user-flow.ts +17 -2
  67. package/src/console/server/iso-at.test.ts +9 -0
  68. package/src/console/server/iso-at.ts +11 -0
  69. package/src/console/server/live.test.ts +26 -1
  70. package/src/console/server/live.ts +4 -2
  71. package/src/console/server/operator-db.ts +2 -0
  72. package/src/console/server/security-headers.ts +6 -3
  73. package/src/console/server/spa-proxy.test.ts +1 -0
  74. package/src/console/server/sql-catalog.ts +147 -7
  75. package/src/console/server/sql-rls.test.ts +16 -0
  76. package/src/console/server/state.ts +37 -55
  77. package/src/console/server/store.test.ts +7 -4
  78. package/src/console/server/store.ts +142 -58
  79. package/src/console/ui-next/dist/assets/access-page-CAGHrA9H.js +4 -0
  80. package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +1 -0
  81. package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
  82. package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
  83. package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
  84. package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
  85. package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
  86. package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
  87. package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
  88. package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
  89. package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
  90. package/src/console/ui-next/dist/assets/flows-page-B-OUtiAu.js +1 -0
  91. package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-Awq7gYdu.js} +1 -1
  92. package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
  93. package/src/console/ui-next/dist/assets/index-CGoZkILK.js +66 -0
  94. package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
  95. package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
  96. package/src/console/ui-next/dist/assets/observability-page-BDyXalNR.js +4 -0
  97. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
  98. package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
  99. package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
  100. package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
  101. package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
  102. package/src/console/ui-next/dist/assets/{skeleton-CL_X0GCj.js → skeleton-D-czQJT6.js} +1 -1
  103. package/src/console/ui-next/dist/assets/store-page-Xh8Kn3rx.js +41 -0
  104. package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
  105. package/src/console/ui-next/dist/assets/tree-expand-toggle-DkOXA12R.js +54 -0
  106. package/src/console/ui-next/dist/assets/units-page-Dpk40kOQ.js +1 -0
  107. package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-uk4WVboC.js} +1 -1
  108. package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
  109. package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
  110. package/src/console/ui-next/dist/assets/vault-page-B1dB9Ft0.js +2 -0
  111. package/src/console/ui-next/dist/index.html +12 -10
  112. package/src/console/ui-next/index.html +3 -3
  113. package/src/console/ui-next/src/client.ts +202 -7
  114. package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
  115. package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
  116. package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
  117. package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
  118. package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
  119. package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
  120. package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
  121. package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
  122. package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
  123. package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
  124. package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
  125. package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
  126. package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
  127. package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
  128. package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
  129. package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
  130. package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
  131. package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
  132. package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
  133. package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
  134. package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
  135. package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
  136. package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
  137. package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
  138. package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
  139. package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
  140. package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
  141. package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
  142. package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +19 -1
  143. package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
  144. package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
  145. package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
  146. package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
  147. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
  148. package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
  149. package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
  150. package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
  151. package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
  152. package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
  153. package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
  154. package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
  155. package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
  156. package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
  157. package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
  158. package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
  159. package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
  160. package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
  161. package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
  162. package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
  163. package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
  164. package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
  165. package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
  166. package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
  167. package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
  168. package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
  169. package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
  170. package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
  171. package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
  172. package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
  173. package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
  174. package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
  175. package/src/console/ui-next/src/lib/document-meta.ts +2 -1
  176. package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
  177. package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
  178. package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
  179. package/src/console/ui-next/src/lib/shortcut.ts +3 -0
  180. package/src/console/ui-next/src/router.tsx +10 -1
  181. package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
  182. package/src/console/ui-next/ui-next-seed-store.ts +43 -15
  183. package/src/console/xss.gate.test.ts +1 -0
  184. package/src/docker/compose-health.test.ts +27 -0
  185. package/src/docker/compose-health.ts +19 -3
  186. package/src/docker/docker.test.ts +3 -0
  187. package/src/docker/recipes/pgdog.ts +2 -0
  188. package/src/drivers/clock-postgres.ts +13 -9
  189. package/src/drivers/instances-postgres.ts +4 -6
  190. package/src/drivers/journal-postgres.ts +13 -9
  191. package/src/drivers/pg-rls.test.ts +87 -0
  192. package/src/drivers/pg-rls.ts +233 -7
  193. package/src/drivers/pglite.ts +26 -3
  194. package/src/drivers/postgres.test.ts +23 -0
  195. package/src/drivers/postgres.ts +133 -9
  196. package/src/drivers/types.ts +10 -0
  197. package/src/elements/clock/declare.ts +26 -1
  198. package/src/elements/gate/boot.ts +5 -5
  199. package/src/elements/gate/declare.ts +38 -4
  200. package/src/elements/gate/flatten.ts +1 -1
  201. package/src/elements/gate/runtime.ts +1 -1
  202. package/src/elements/store/cache.ts +1 -1
  203. package/src/elements/store/declare.ts +9 -6
  204. package/src/elements/store/emit-drizzle.ts +44 -8
  205. package/src/elements/store/resource.test.ts +1 -1
  206. package/src/elements/store/rls-identity.test.ts +95 -0
  207. package/src/elements/store/rls-identity.ts +116 -0
  208. package/src/elements/store/runtime.ts +18 -0
  209. package/src/elements/store/schema-decl.test.ts +37 -0
  210. package/src/elements/store/schema-decl.ts +260 -41
  211. package/src/elements/store/seed.ts +1 -1
  212. package/src/elements/store/sql-condition.test.ts +12 -0
  213. package/src/elements/store/sql-condition.ts +26 -2
  214. package/src/elements/store/sql-rls-isolation.test.ts +165 -0
  215. package/src/elements/store/sql-rls-stamp.test.ts +117 -0
  216. package/src/elements/store/sql-select.types.test.ts +71 -0
  217. package/src/elements/store/sql-session.ts +135 -35
  218. package/src/elements/store/upsert-app.test.ts +1 -1
  219. package/src/elements/store.ts +17 -0
  220. package/src/http.ts +20 -3
  221. package/src/index.ts +11 -1
  222. package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
  223. package/src/kernel/adopt-routes.ts +30 -2
  224. package/src/kernel/api-key-host-persist.test.ts +143 -0
  225. package/src/kernel/app.ts +145 -16
  226. package/src/kernel/auth-resolve.ts +10 -5
  227. package/src/kernel/auto-cache.test.ts +8 -8
  228. package/src/kernel/auto-registry.test.ts +49 -9
  229. package/src/kernel/boot.test.ts +5 -5
  230. package/src/kernel/boot.ts +2 -2
  231. package/src/kernel/effects-stamping.test.ts +3 -3
  232. package/src/kernel/element-registries.ts +12 -5
  233. package/src/kernel/errors.ts +24 -0
  234. package/src/kernel/flow-units.ts +64 -0
  235. package/src/kernel/flow.ts +29 -17
  236. package/src/kernel/fx-auth-keys.test.ts +88 -0
  237. package/src/kernel/fx-auth-keys.ts +198 -0
  238. package/src/kernel/fx.test.ts +17 -1
  239. package/src/kernel/fx.ts +59 -18
  240. package/src/kernel/horizontal-child.ts +4 -4
  241. package/src/kernel/http-path-pending.ts +20 -0
  242. package/src/kernel/http-resource.ts +4 -0
  243. package/src/kernel/http-stream.test.ts +4 -4
  244. package/src/kernel/pipeline-api-key.test.ts +192 -7
  245. package/src/kernel/pipeline.test.ts +2 -2
  246. package/src/kernel/pipeline.ts +4 -2
  247. package/src/kernel/plugin-elements.test.ts +1 -1
  248. package/src/kernel/plugin-needs.test.ts +1 -1
  249. package/src/kernel/ready.test.ts +2 -2
  250. package/src/kernel/resource-mount.test.ts +14 -0
  251. package/src/kernel/run-duration.test.ts +2 -2
  252. package/src/kernel/stamp-http.test.ts +79 -0
  253. package/src/kernel/stamp-http.ts +75 -0
  254. package/src/kernel/triggers.ts +52 -27
  255. package/src/manifest/types.ts +22 -3
  256. package/src/release/build-lib.ts +1 -0
  257. package/src/release/measure.ts +2 -2
  258. package/src/runtime/json-code-block.test.ts +2 -2
  259. package/src/runtime/serve.test.ts +4 -4
  260. package/src/term.test.ts +52 -4
  261. package/src/term.ts +125 -17
  262. package/src/test/reset-element-registries.ts +8 -3
  263. package/src/cli/ask-seed.test.ts +0 -96
  264. package/src/cli/ask-seed.ts +0 -82
  265. package/src/console/ui-next/dist/assets/cache-glyph-BanhLsEY.js +0 -1
  266. package/src/console/ui-next/dist/assets/call-pii-button-bqkxMrJH.js +0 -1
  267. package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
  268. package/src/console/ui-next/dist/assets/confirm-sheet-DDCRmG62.js +0 -1
  269. package/src/console/ui-next/dist/assets/explorer-empty-HjTnVQoR.js +0 -1
  270. package/src/console/ui-next/dist/assets/flows-page-DxDsOd4f.js +0 -1
  271. package/src/console/ui-next/dist/assets/http-method-CJCBYL2j.js +0 -1
  272. package/src/console/ui-next/dist/assets/index-Ce6WKWKM.js +0 -66
  273. package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
  274. package/src/console/ui-next/dist/assets/link-DF7SZ9Ek.js +0 -1
  275. package/src/console/ui-next/dist/assets/observability-page-BEZDzyYh.js +0 -4
  276. package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
  277. package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
  278. package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
  279. package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
  280. package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
  281. package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
  282. package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
  283. package/src/console/ui-next/dist/assets/trace-detail-sheet-DFLFfUUX.js +0 -2
  284. package/src/console/ui-next/dist/assets/units-page-C0gW6Kdo.js +0 -1
  285. package/src/console/ui-next/dist/assets/vault-page-DuKzqwzW.js +0 -2
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Access page URL search — selected key + sheet action.
3
+ */
4
+
5
+ import { useNavigate, useSearch } from "@tanstack/react-router";
6
+ import { useCallback } from "react";
7
+
8
+ /** Sheet opened from the URL. */
9
+ export type AccessAction = "create" | "edit" | "refresh" | "revoke" | "rotate";
10
+
11
+ /** Search params for `/access`. */
12
+ export interface AccessSearch {
13
+ readonly q?: string;
14
+ readonly key?: string;
15
+ readonly action?: AccessAction;
16
+ }
17
+
18
+ /**
19
+ * Validate Access search params from the router.
20
+ *
21
+ * @param search - Raw search object
22
+ */
23
+ export function validateAccessSearch(search: Record<string, unknown>): AccessSearch {
24
+ const q = typeof search.q === "string" && search.q.length > 0 ? search.q : undefined;
25
+ const key = typeof search.key === "string" && search.key.length > 0 ? search.key : undefined;
26
+ const action =
27
+ search.action === "create" ||
28
+ search.action === "edit" ||
29
+ search.action === "refresh" ||
30
+ search.action === "revoke" ||
31
+ search.action === "rotate"
32
+ ? search.action
33
+ : undefined;
34
+ return {
35
+ ...(q !== undefined ? { q } : {}),
36
+ ...(key !== undefined ? { key } : {}),
37
+ ...(action !== undefined ? { action } : {}),
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Read + write the Access page selection from the URL.
43
+ */
44
+ export function useAccessSelection(): {
45
+ readonly query: string;
46
+ readonly selectedKey: string | null;
47
+ readonly action: AccessAction | null;
48
+ readonly setQuery: (q: string) => void;
49
+ readonly setSelectedKey: (key: string | null) => void;
50
+ readonly setAction: (action: AccessAction | null) => void;
51
+ } {
52
+ const search = useSearch({ strict: false }) as AccessSearch;
53
+ const navigate = useNavigate();
54
+
55
+ const query = typeof search.q === "string" ? search.q : "";
56
+ const selectedKey = typeof search.key === "string" ? search.key : null;
57
+ const action = search.action ?? null;
58
+
59
+ const patch = useCallback(
60
+ (next: Partial<AccessSearch>) => {
61
+ void navigate({
62
+ to: ".",
63
+ search: (prev: Record<string, unknown>) => validateAccessSearch({ ...prev, ...next }),
64
+ replace: true,
65
+ });
66
+ },
67
+ [navigate],
68
+ );
69
+
70
+ return {
71
+ query,
72
+ selectedKey,
73
+ action,
74
+ setQuery: (q) => {
75
+ patch({ q: q.length > 0 ? q : undefined });
76
+ },
77
+ setSelectedKey: (key) => {
78
+ patch({ key: key ?? undefined, action: undefined });
79
+ },
80
+ setAction: (next) => {
81
+ patch({ action: next ?? undefined });
82
+ },
83
+ };
84
+ }
@@ -18,6 +18,7 @@ describe("sanitizeReturnTo", () => {
18
18
  expect(sanitizeReturnTo("/vault?name=STRIPE_KEY&action=rotate")).toBe(
19
19
  "/vault?name=STRIPE_KEY&action=rotate",
20
20
  );
21
+ expect(sanitizeReturnTo("/access?key=key_1")).toBe("/access?key=key_1");
21
22
  expect(sanitizeReturnTo("/observability?window=7d")).toBe("/observability?window=7d");
22
23
  expect(sanitizeReturnTo("/monitoring?window=7d")).toBe("/observability?window=7d");
23
24
  });
@@ -107,6 +108,10 @@ describe("afterAuthLocation", () => {
107
108
  to: "/vault",
108
109
  search: { name: "STRIPE_KEY", action: "rotate-master" },
109
110
  });
111
+ expect(afterAuthLocation("/access?key=key_1")).toEqual({
112
+ to: "/access",
113
+ search: { key: "key_1" },
114
+ });
110
115
  expect(afterAuthLocation("/observability?window=7d&run=r1")).toEqual({
111
116
  to: "/observability",
112
117
  search: { window: "7d", run: "r1" },
@@ -2,7 +2,7 @@
2
2
  * Post-auth return path — keep the operator on the module they were in
3
3
  * after a session expires, instead of always landing on `/overview`.
4
4
  *
5
- * Only `/overview`, `/flows`, `/store`, `/vault`, and `/observability` (plus their
5
+ * Only `/overview`, `/flows`, `/store`, `/vault`, `/access`, and `/observability` (plus their
6
6
  * search) are legal. `/monitoring` is rewritten to `/observability`. Anything else
7
7
  * is dropped so `?next=` cannot be an open redirect.
8
8
  */
@@ -15,12 +15,20 @@ import {
15
15
  type ObservabilitySearch,
16
16
  } from "../observability/state/observability-selection.ts";
17
17
  import { validateVaultSearch, type VaultSearch } from "../vault/state/vault-selection.ts";
18
+ import { validateAccessSearch, type AccessSearch } from "../access/state/access-selection.ts";
18
19
 
19
20
  /** Default shell module when no safe return path is present. */
20
21
  export const DEFAULT_AFTER_AUTH = "/overview" as const;
21
22
 
22
23
  /** Shell pathnames that may be restored after login. */
23
- const SHELL_PATHS = new Set(["/overview", "/flows", "/store", "/vault", "/observability"]);
24
+ const SHELL_PATHS = new Set([
25
+ "/overview",
26
+ "/flows",
27
+ "/store",
28
+ "/vault",
29
+ "/access",
30
+ "/observability",
31
+ ]);
24
32
 
25
33
  /** Retired live module — rewritten to {@link CANONICAL_OBSERVABILITY}. */
26
34
  const LEGACY_MONITORING = "/monitoring";
@@ -40,6 +48,7 @@ export type AfterAuthLocation =
40
48
  | { readonly to: "/flows"; readonly search: UnitsSearch }
41
49
  | { readonly to: "/store"; readonly search: StoreSearch }
42
50
  | { readonly to: "/vault"; readonly search: VaultSearch }
51
+ | { readonly to: "/access"; readonly search: AccessSearch }
43
52
  | { readonly to: "/observability"; readonly search: ObservabilitySearch };
44
53
 
45
54
  /**
@@ -111,6 +120,9 @@ export function afterAuthLocation(value: unknown): AfterAuthLocation {
111
120
  if (url.pathname === "/vault") {
112
121
  return { to: "/vault", search: validateVaultSearch(raw) };
113
122
  }
123
+ if (url.pathname === "/access") {
124
+ return { to: "/access", search: validateAccessSearch(raw) };
125
+ }
114
126
  if (url.pathname === CANONICAL_OBSERVABILITY) {
115
127
  return { to: "/observability", search: validateObservabilitySearch(raw) };
116
128
  }
@@ -123,6 +135,7 @@ export type AfterAuthNavigate = {
123
135
  (opts: { to: "/flows"; search: UnitsSearch }): unknown;
124
136
  (opts: { to: "/store"; search: StoreSearch }): unknown;
125
137
  (opts: { to: "/vault"; search: VaultSearch }): unknown;
138
+ (opts: { to: "/access"; search: AccessSearch }): unknown;
126
139
  (opts: { to: "/observability"; search: ObservabilitySearch }): unknown;
127
140
  };
128
141
 
@@ -144,6 +157,9 @@ export function goAfterAuth(navigate: AfterAuthNavigate, value: unknown): void {
144
157
  case "/vault":
145
158
  void navigate({ to: "/vault", search: dest.search });
146
159
  return;
160
+ case "/access":
161
+ void navigate({ to: "/access", search: dest.search });
162
+ return;
147
163
  case "/observability":
148
164
  void navigate({ to: "/observability", search: dest.search });
149
165
  return;
@@ -18,6 +18,25 @@ import type { ElementHugeIcon } from "@/lib/element-icons.ts";
18
18
  /** Known HTTP methods from Manifest `HttpTrigger.method`. */
19
19
  export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "QUERY";
20
20
 
21
+ /**
22
+ * Whether the verb carries a JSON request body on the wire.
23
+ *
24
+ * Matches the HTTP compiler (`GET` / `HEAD` / `DELETE` are path-or-query
25
+ * only) and the typed client (`GET` / `HEAD` leftover fields → query).
26
+ *
27
+ * @param method - HTTP method string (case-insensitive) or null
28
+ */
29
+ export function httpMethodHasBody(method: string | null | undefined): boolean {
30
+ switch ((method ?? "").toUpperCase()) {
31
+ case "GET":
32
+ case "HEAD":
33
+ case "DELETE":
34
+ return false;
35
+ default:
36
+ return true;
37
+ }
38
+ }
39
+
21
40
  /**
22
41
  * Units-tree display rank: GET → POST → QUERY → PATCH/PUT → DELETE.
23
42
  *
@@ -8,6 +8,7 @@ import { ELEMENT_ICONS } from "@/lib/element-icons.ts";
8
8
  import { EDGE_STROKE } from "../graph/flow-graph-theme.ts";
9
9
  import {
10
10
  httpMethodBadgeClass,
11
+ httpMethodHasBody,
11
12
  httpMethodIcon,
12
13
  httpMethodRailClass,
13
14
  httpMethodSortRank,
@@ -110,6 +111,23 @@ describe("effectKindIcon", () => {
110
111
  });
111
112
  });
112
113
 
114
+ describe("httpMethodHasBody", () => {
115
+ test("GET, HEAD, and DELETE are path/query only", () => {
116
+ expect(httpMethodHasBody("GET")).toBe(false);
117
+ expect(httpMethodHasBody("HEAD")).toBe(false);
118
+ expect(httpMethodHasBody("DELETE")).toBe(false);
119
+ expect(httpMethodHasBody("get")).toBe(false);
120
+ });
121
+
122
+ test("POST, PUT, PATCH, QUERY, and OPTIONS carry JSON", () => {
123
+ expect(httpMethodHasBody("POST")).toBe(true);
124
+ expect(httpMethodHasBody("PUT")).toBe(true);
125
+ expect(httpMethodHasBody("PATCH")).toBe(true);
126
+ expect(httpMethodHasBody("QUERY")).toBe(true);
127
+ expect(httpMethodHasBody("OPTIONS")).toBe(true);
128
+ });
129
+ });
130
+
113
131
  describe("httpMethodBadgeClass", () => {
114
132
  test("assigns distinct accents per method", () => {
115
133
  expect(httpMethodBadgeClass("GET")).toContain("emerald");
@@ -352,7 +370,7 @@ describe("Sheet Replay path", () => {
352
370
  ok: true as const,
353
371
  rootId: run.id,
354
372
  dryRun: false,
355
- at: 1,
373
+ at: "2026-08-21T09:36:51.525Z",
356
374
  flow: run.flow,
357
375
  },
358
376
  error: null,
@@ -15,6 +15,7 @@ import {
15
15
  EXPLORER_STRIP_TOKEN_IDLE_CLASS,
16
16
  EXPLORER_TOOLBAR_CLASS,
17
17
  } from "@/components/explorer/explorer-chrome.ts";
18
+ import { AgentDisclosure } from "@/components/agents/agent-disclosure.tsx";
18
19
  import { ExplorerSearch } from "@/components/explorer/explorer-search.tsx";
19
20
  import {
20
21
  Empty,
@@ -236,10 +237,16 @@ export function TracesPane({
236
237
  ) : null}
237
238
  </div>
238
239
 
239
- {runs.length > 0 && advancedOpen ? (
240
- <div id="traces-advanced-panel">
240
+ {runs.length > 0 ? (
241
+ <AgentDisclosure
242
+ id="traces-advanced-panel"
243
+ role="region"
244
+ aria-label="Advanced filters"
245
+ open={advancedOpen}
246
+ className="shrink-0"
247
+ >
241
248
  <AdvancedFilters query={filters.advanced} runs={runs} onChange={setAdvanced} />
242
- </div>
249
+ </AgentDisclosure>
243
250
  ) : null}
244
251
 
245
252
  <div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
@@ -6,7 +6,7 @@ import { authLabelClassName } from "@/components/auth-card";
6
6
  import { ConsoleChrome } from "@/components/console-chrome";
7
7
 
8
8
  /**
9
- * 404 for any path that is not `/`, `/overview`, `/flows`, `/store`, `/vault`, or `/observability`.
9
+ * 404 for any path that is not `/`, `/overview`, `/flows`, `/store`, `/vault`, `/access`, or `/observability`.
10
10
  */
11
11
  export function NotFoundPage() {
12
12
  return (
@@ -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
- }, [store.ref, store.facet, child.name, tenant, tenantReady, limit, indexQuery, topK, piiMasked]);
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 { isSqlCatalogChild, storeChildLabel } from "../lib/sql-catalog.ts";
28
- import { STORE_FACET_SPECS } from "../lib/store-tree.ts";
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 showStatus = Boolean(drift || lagMs !== null || store.contentAddressed || piiCount > 0);
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 className={className} role={role} data-slot={dataSlot} data-drifted={dataDrifted}>
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>