okengine 0.14.1 → 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.
Files changed (205) 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 +3 -0
  6. package/site/content/docs/elements/gate.mdx +25 -22
  7. package/site/content/docs/elements/signal.mdx +4 -4
  8. package/site/content/docs/elements/store.mdx +86 -27
  9. package/site/content/docs/elements/vault.mdx +1 -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 +25 -21
  17. package/site/content/docs/reference/security.md +1 -1
  18. package/src/auth/bindings.ts +14 -5
  19. package/src/auth/cross-plane.ts +10 -3
  20. package/src/cli/ai-setup/ai-setup.test.ts +62 -0
  21. package/src/cli/ai-setup/apply.ts +32 -1
  22. package/src/cli/attach-host-console.test.ts +3 -2
  23. package/src/cli/attach-host-console.ts +3 -0
  24. package/src/cli/build.test.ts +4 -2
  25. package/src/cli/db-seed.ts +48 -15
  26. package/src/cli/db.test.ts +104 -0
  27. package/src/cli/db.ts +122 -10
  28. package/src/cli/dev-controls.test.ts +20 -0
  29. package/src/cli/dev-controls.ts +13 -0
  30. package/src/cli/dev.test.ts +15 -4
  31. package/src/cli/dev.ts +102 -29
  32. package/src/cli/ensure-drizzle-config.ts +2 -1
  33. package/src/cli/tui/DevLive.tsx +12 -2
  34. package/src/cli/tui/keys.test.ts +2 -1
  35. package/src/cli/tui/keys.ts +3 -2
  36. package/src/compiler/aot.test.ts +1 -1
  37. package/src/compiler/extract.test.ts +409 -33
  38. package/src/compiler/extract.ts +318 -67
  39. package/src/compiler/flow-path.test.ts +96 -0
  40. package/src/compiler/flow-path.ts +173 -0
  41. package/src/compiler/generate-adopt.test.ts +112 -3
  42. package/src/compiler/generate-adopt.ts +280 -26
  43. package/src/compiler/schema-from-ast.ts +609 -0
  44. package/src/console/server/bind.ts +2 -2
  45. package/src/console/server/console.test.ts +3 -0
  46. package/src/console/server/flows.ts +120 -40
  47. package/src/console/server/invoke-user-flow.test.ts +3 -1
  48. package/src/console/server/invoke-user-flow.ts +17 -2
  49. package/src/console/server/iso-at.test.ts +9 -0
  50. package/src/console/server/iso-at.ts +11 -0
  51. package/src/console/server/security-headers.ts +6 -3
  52. package/src/console/server/spa-proxy.test.ts +1 -0
  53. package/src/console/server/sql-catalog.ts +147 -7
  54. package/src/console/server/sql-rls.test.ts +16 -0
  55. package/src/console/server/state.ts +8 -0
  56. package/src/console/server/store.test.ts +7 -4
  57. package/src/console/server/store.ts +142 -58
  58. package/src/console/ui-next/dist/assets/agent-disclosure-C0X1fbWF.js +1 -0
  59. package/src/console/ui-next/dist/assets/{cache-glyph-BanhLsEY.js → cache-glyph-F1FI122b.js} +1 -1
  60. package/src/console/ui-next/dist/assets/{call-pii-button-bqkxMrJH.js → call-pii-button-DEDCl_j3.js} +1 -1
  61. package/src/console/ui-next/dist/assets/collapsible-LPqGvfoz.js +1 -0
  62. package/src/console/ui-next/dist/assets/{confirm-sheet-DDCRmG62.js → confirm-sheet-3ptDzXbz.js} +1 -1
  63. package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
  64. package/src/console/ui-next/dist/assets/{explorer-empty-HjTnVQoR.js → explorer-empty-BvYrygyO.js} +1 -1
  65. package/src/console/ui-next/dist/assets/flows-page-Dluo1Bd7.js +1 -0
  66. package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-CS_O8L-r.js} +1 -1
  67. package/src/console/ui-next/dist/assets/http-method--sWDdXSB.js +1 -0
  68. package/src/console/ui-next/dist/assets/index-Ca3HZMVq.js +66 -0
  69. package/src/console/ui-next/dist/assets/index-UtTDRQpS.css +2 -0
  70. package/src/console/ui-next/dist/assets/{link-DF7SZ9Ek.js → link-COeyggt-.js} +1 -1
  71. package/src/console/ui-next/dist/assets/{observability-page-BEZDzyYh.js → observability-page-HK9-BO8z.js} +2 -2
  72. package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
  73. package/src/console/ui-next/dist/assets/replica-lag-DBIFFf7d.js +18 -0
  74. package/src/console/ui-next/dist/assets/sheet-form-Dnwa5oOR.js +1 -0
  75. package/src/console/ui-next/dist/assets/shortcut-keys-CoqwEIi0.js +1 -0
  76. package/src/console/ui-next/dist/assets/skeleton-BHcfDYcb.js +1 -0
  77. package/src/console/ui-next/dist/assets/store-page-BSohH3wM.js +41 -0
  78. package/src/console/ui-next/dist/assets/trace-detail-sheet-CFIGRnnA.js +2 -0
  79. package/src/console/ui-next/dist/assets/tree-expand-toggle-BgLl34w7.js +54 -0
  80. package/src/console/ui-next/dist/assets/units-page-B0cFE76A.js +1 -0
  81. package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-BLbZhzyF.js} +1 -1
  82. package/src/console/ui-next/dist/assets/vault-page-B1SbYe10.js +2 -0
  83. package/src/console/ui-next/dist/index.html +6 -6
  84. package/src/console/ui-next/src/client.ts +35 -7
  85. package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
  86. package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
  87. package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
  88. package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
  89. package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +19 -1
  90. package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
  91. package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
  92. package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
  93. package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
  94. package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
  95. package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
  96. package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
  97. package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
  98. package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
  99. package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
  100. package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
  101. package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
  102. package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
  103. package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
  104. package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
  105. package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
  106. package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
  107. package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
  108. package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
  109. package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
  110. package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
  111. package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
  112. package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
  113. package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
  114. package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
  115. package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
  116. package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
  117. package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
  118. package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
  119. package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
  120. package/src/docker/compose-health.test.ts +27 -0
  121. package/src/docker/compose-health.ts +19 -3
  122. package/src/docker/docker.test.ts +3 -0
  123. package/src/docker/recipes/pgdog.ts +2 -0
  124. package/src/drivers/clock-postgres.ts +13 -9
  125. package/src/drivers/instances-postgres.ts +4 -6
  126. package/src/drivers/journal-postgres.ts +13 -9
  127. package/src/drivers/pg-rls.test.ts +87 -0
  128. package/src/drivers/pg-rls.ts +233 -7
  129. package/src/drivers/pglite.ts +26 -3
  130. package/src/drivers/postgres.test.ts +23 -0
  131. package/src/drivers/postgres.ts +133 -9
  132. package/src/drivers/types.ts +10 -0
  133. package/src/elements/clock/declare.ts +26 -1
  134. package/src/elements/gate/boot.ts +5 -5
  135. package/src/elements/gate/declare.ts +36 -4
  136. package/src/elements/gate/flatten.ts +1 -1
  137. package/src/elements/gate/runtime.ts +1 -1
  138. package/src/elements/store/declare.ts +9 -6
  139. package/src/elements/store/emit-drizzle.ts +44 -8
  140. package/src/elements/store/resource.test.ts +1 -1
  141. package/src/elements/store/rls-identity.test.ts +95 -0
  142. package/src/elements/store/rls-identity.ts +116 -0
  143. package/src/elements/store/runtime.ts +4 -0
  144. package/src/elements/store/schema-decl.test.ts +37 -0
  145. package/src/elements/store/schema-decl.ts +161 -0
  146. package/src/elements/store/seed.ts +1 -1
  147. package/src/elements/store/sql-condition.test.ts +12 -0
  148. package/src/elements/store/sql-condition.ts +26 -2
  149. package/src/elements/store/sql-rls-isolation.test.ts +165 -0
  150. package/src/elements/store/sql-rls-stamp.test.ts +117 -0
  151. package/src/elements/store/sql-session.ts +81 -3
  152. package/src/elements/store/upsert-app.test.ts +1 -1
  153. package/src/elements/store.ts +14 -0
  154. package/src/http.ts +20 -3
  155. package/src/index.ts +10 -1
  156. package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
  157. package/src/kernel/adopt-routes.ts +30 -2
  158. package/src/kernel/app.ts +131 -14
  159. package/src/kernel/auto-cache.test.ts +8 -8
  160. package/src/kernel/auto-registry.test.ts +49 -9
  161. package/src/kernel/boot.test.ts +4 -4
  162. package/src/kernel/boot.ts +2 -2
  163. package/src/kernel/effects-stamping.test.ts +3 -3
  164. package/src/kernel/element-registries.ts +12 -5
  165. package/src/kernel/errors.ts +24 -0
  166. package/src/kernel/flow-units.ts +64 -0
  167. package/src/kernel/flow.ts +29 -17
  168. package/src/kernel/fx.ts +23 -0
  169. package/src/kernel/horizontal-child.ts +4 -4
  170. package/src/kernel/http-path-pending.ts +20 -0
  171. package/src/kernel/http-resource.ts +4 -0
  172. package/src/kernel/http-stream.test.ts +4 -4
  173. package/src/kernel/pipeline.test.ts +2 -2
  174. package/src/kernel/plugin-elements.test.ts +1 -1
  175. package/src/kernel/plugin-needs.test.ts +1 -1
  176. package/src/kernel/ready.test.ts +2 -2
  177. package/src/kernel/resource-mount.test.ts +14 -0
  178. package/src/kernel/run-duration.test.ts +2 -2
  179. package/src/kernel/stamp-http.test.ts +79 -0
  180. package/src/kernel/stamp-http.ts +75 -0
  181. package/src/kernel/triggers.ts +52 -27
  182. package/src/manifest/types.ts +13 -0
  183. package/src/release/measure.ts +2 -2
  184. package/src/runtime/json-code-block.test.ts +2 -2
  185. package/src/runtime/serve.test.ts +4 -4
  186. package/src/term.test.ts +52 -4
  187. package/src/term.ts +125 -17
  188. package/src/test/reset-element-registries.ts +8 -3
  189. package/src/cli/ask-seed.test.ts +0 -96
  190. package/src/cli/ask-seed.ts +0 -82
  191. package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
  192. package/src/console/ui-next/dist/assets/flows-page-DxDsOd4f.js +0 -1
  193. package/src/console/ui-next/dist/assets/http-method-CJCBYL2j.js +0 -1
  194. package/src/console/ui-next/dist/assets/index-Ce6WKWKM.js +0 -66
  195. package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
  196. package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
  197. package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
  198. package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
  199. package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
  200. package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
  201. package/src/console/ui-next/dist/assets/skeleton-CL_X0GCj.js +0 -1
  202. package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
  203. package/src/console/ui-next/dist/assets/trace-detail-sheet-DFLFfUUX.js +0 -2
  204. package/src/console/ui-next/dist/assets/units-page-C0gW6Kdo.js +0 -1
  205. package/src/console/ui-next/dist/assets/vault-page-DuKzqwzW.js +0 -2
@@ -37,18 +37,18 @@
37
37
  } catch (e) {}
38
38
  })();
39
39
  </script>
40
- <script type="module" crossorigin src="/assets/index-Ce6WKWKM.js"></script>
40
+ <script type="module" crossorigin src="/assets/index-Ca3HZMVq.js"></script>
41
41
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
42
42
  <link rel="modulepreload" crossorigin href="/assets/react-dom-Bph1y7z7.js">
43
43
  <link rel="modulepreload" crossorigin href="/assets/xyflow-D7n4g6go.js">
44
- <link rel="modulepreload" crossorigin href="/assets/shortcut-keys-DO4IsVqv.js">
45
- <link rel="modulepreload" crossorigin href="/assets/link-DF7SZ9Ek.js">
46
- <link rel="modulepreload" crossorigin href="/assets/skeleton-CL_X0GCj.js">
47
- <link rel="modulepreload" crossorigin href="/assets/react-B1ML8gxg.js">
44
+ <link rel="modulepreload" crossorigin href="/assets/shortcut-keys-CoqwEIi0.js">
45
+ <link rel="modulepreload" crossorigin href="/assets/link-COeyggt-.js">
46
+ <link rel="modulepreload" crossorigin href="/assets/skeleton-BHcfDYcb.js">
47
+ <link rel="modulepreload" crossorigin href="/assets/react-D8E3mtu1.js">
48
48
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-oH4irX4C.js">
49
49
  <link rel="modulepreload" crossorigin href="/assets/shortcut-Cgzuv4k1.js">
50
50
  <link rel="stylesheet" crossorigin href="/assets/xyflow-DZ0Ws1xk.css">
51
- <link rel="stylesheet" crossorigin href="/assets/index-D4Ldtj79.css">
51
+ <link rel="stylesheet" crossorigin href="/assets/index-UtTDRQpS.css">
52
52
  </head>
53
53
  <body>
54
54
  <div id="root"></div>
@@ -446,7 +446,7 @@ export type TracesReplayResult = {
446
446
  readonly ok: true;
447
447
  readonly rootId: string;
448
448
  readonly dryRun: boolean;
449
- readonly at: number;
449
+ readonly at: string;
450
450
  readonly flow: string;
451
451
  };
452
452
 
@@ -506,6 +506,11 @@ export type FlowsInvokeResult = {
506
506
  readonly flowId: string;
507
507
  readonly asUserId: string;
508
508
  readonly asGate?: string | null;
509
+ readonly rls?: {
510
+ readonly gate: string | null;
511
+ readonly userId: string;
512
+ readonly applied: boolean;
513
+ };
509
514
  readonly trigger: string;
510
515
  readonly response: unknown;
511
516
  /** True when classified PII keys were redacted. */
@@ -674,7 +679,7 @@ export type ClockRunNowResult = {
674
679
  readonly ok: true;
675
680
  readonly name: string;
676
681
  readonly ran: boolean;
677
- readonly at: number;
682
+ readonly at: string;
678
683
  };
679
684
 
680
685
  /**
@@ -721,6 +726,8 @@ export type StoreListChild = {
721
726
  readonly columnDescriptions: Readonly<Record<string, string>>;
722
727
  /** Live RLS when the engine reported `pg_class.relrowsecurity`. */
723
728
  readonly rls?: boolean;
729
+ /** Live + declared RLS policy count when the child is a SQL table. */
730
+ readonly rlsPolicyCount?: number;
724
731
  };
725
732
 
726
733
  /** One store row from `GET /console/store` (matches server `StoreListOut`). */
@@ -772,6 +779,8 @@ export type StoreQueryInput = {
772
779
  readonly topK?: number;
773
780
  /** When true, SQL browse returns PII cleartext (audited). Default masks. */
774
781
  readonly revealPii?: boolean;
782
+ readonly asGate?: string;
783
+ readonly asUserId?: string;
775
784
  };
776
785
 
777
786
  /** Success payload from `QUERY /console/store/query` (matches server `StoreQueryOut`). */
@@ -793,6 +802,11 @@ export type StoreQueryResult = {
793
802
  }>;
794
803
  readonly masked: boolean;
795
804
  readonly routedRole?: "primary" | "replica";
805
+ readonly rls?: {
806
+ readonly gate: string | null;
807
+ readonly userId: string;
808
+ readonly applied: boolean;
809
+ };
796
810
  };
797
811
 
798
812
  /**
@@ -822,7 +836,7 @@ export type StoreRevealInput = {
822
836
  export type StoreRevealResult = {
823
837
  readonly ok: true;
824
838
  readonly value: unknown;
825
- readonly at: number;
839
+ readonly at: string;
826
840
  };
827
841
 
828
842
  /**
@@ -851,6 +865,8 @@ export type StoreEditInput = {
851
865
  readonly confirmation?: string;
852
866
  readonly reason?: string;
853
867
  readonly commit?: boolean;
868
+ readonly asGate?: string;
869
+ readonly asUserId?: string;
854
870
  };
855
871
 
856
872
  /** Success payload from `POST /console/store/edit` (matches server). */
@@ -863,7 +879,12 @@ export type StoreEditResult = {
863
879
  readonly kind: "send" | "ask";
864
880
  readonly resource: string;
865
881
  }>;
866
- readonly at: number;
882
+ readonly at: string;
883
+ readonly rls?: {
884
+ readonly gate: string | null;
885
+ readonly userId: string;
886
+ readonly applied: boolean;
887
+ };
867
888
  };
868
889
 
869
890
  /**
@@ -907,7 +928,7 @@ export type StoreDeleteInput = {
907
928
  export type StoreDeleteResult = {
908
929
  readonly ok: true;
909
930
  readonly deleted: number;
910
- readonly at: number;
931
+ readonly at: string;
911
932
  };
912
933
 
913
934
  /**
@@ -934,6 +955,7 @@ export type StoreSqlInput = {
934
955
  readonly revealPii?: boolean;
935
956
  /** View rows as this Gate (`oke.gate` on postgres / pglite). */
936
957
  readonly asGate?: string;
958
+ readonly asUserId?: string;
937
959
  };
938
960
 
939
961
  /** Success payload from `POST /console/store/sql` (matches server `StoreSqlOut`). */
@@ -942,7 +964,13 @@ export type StoreSqlResult = {
942
964
  readonly masked: boolean;
943
965
  readonly routedRole: "primary" | "replica";
944
966
  readonly asGate: string | null;
967
+ readonly asUserId: string | null;
945
968
  readonly gateApplied: boolean;
969
+ readonly rls?: {
970
+ readonly gate: string | null;
971
+ readonly userId: string;
972
+ readonly applied: boolean;
973
+ };
946
974
  };
947
975
 
948
976
  /**
@@ -1267,7 +1295,7 @@ export type VaultWriteResult = {
1267
1295
  readonly ok: true;
1268
1296
  readonly name: string;
1269
1297
  readonly fingerprint: string | null;
1270
- readonly at: number;
1298
+ readonly at: string;
1271
1299
  };
1272
1300
 
1273
1301
  /**
@@ -1331,7 +1359,7 @@ export type VaultRotateMasterResult = {
1331
1359
  readonly kekVersion: number;
1332
1360
  readonly remaining: number;
1333
1361
  readonly masterKey: string | null;
1334
- readonly at: number;
1362
+ readonly at: string;
1335
1363
  };
1336
1364
 
1337
1365
  /**
@@ -61,6 +61,8 @@ export function Table<T>({
61
61
  onInsertColumn,
62
62
  onDeleteColumn,
63
63
  onRowDoubleClick,
64
+ onRowContextMenu,
65
+ onViewportContextMenu,
64
66
  cellRange = null,
65
67
  onCellRangeChange,
66
68
  onCellActivate,
@@ -192,6 +194,13 @@ export function Table<T>({
192
194
  onPointerMove={cellSelect.onScrollerPointerMove}
193
195
  onPointerUp={cellSelect.onPointerUp}
194
196
  onPointerCancel={cellSelect.onPointerUp}
197
+ onContextMenu={(event) => {
198
+ if (!onViewportContextMenu) return;
199
+ const target = event.target;
200
+ if (target instanceof Element && target.closest("[data-slot='table-row']")) return;
201
+ event.preventDefault();
202
+ onViewportContextMenu();
203
+ }}
195
204
  className={cn("overflow-auto", onCellRangeChange ? "select-none" : undefined)}
196
205
  style={{ height }}
197
206
  >
@@ -273,12 +282,19 @@ export function Table<T>({
273
282
  ref={(el) => {
274
283
  rowRefs.current[entry.id] = el;
275
284
  }}
285
+ data-slot="table-row"
276
286
  data-selected={isSelected}
277
287
  style={{ height: rowHeight }}
278
288
  onPointerEnter={
279
289
  hasRowMenu ? () => activateRow(entry.id, vItem.index) : undefined
280
290
  }
281
291
  onPointerLeave={hasRowMenu ? deactivateRow : undefined}
292
+ onContextMenu={(event) => {
293
+ if (!onRowContextMenu) return;
294
+ event.preventDefault();
295
+ event.stopPropagation();
296
+ onRowContextMenu(entry.row, entry.id);
297
+ }}
282
298
  onDoubleClick={(e) => {
283
299
  if (!onRowDoubleClick) return;
284
300
  const target = e.target;
@@ -78,6 +78,10 @@ export interface TableProps<T> {
78
78
  readonly onDeleteColumn?: (columnKey: string, index: number) => void;
79
79
  /** Double-click a body row. */
80
80
  readonly onRowDoubleClick?: (row: T, id: string) => void;
81
+ /** Two-finger / right-click a body row (caller should `preventDefault`). */
82
+ readonly onRowContextMenu?: (row: T, id: string) => void;
83
+ /** Two-finger / right-click empty viewport (not a row). */
84
+ readonly onViewportContextMenu?: () => void;
81
85
  /** Controlled cell range (visible row index × column index). */
82
86
  readonly cellRange?: CellRange | null;
83
87
  /** Enables drag / Shift+click cell-range selection. */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Keyboard key — shadcn Kbd. Tooltip content uses the inverted ink.
2
+ * Keyboard key — shadcn Kbd. Buttons inherit ink; tooltips invert.
3
3
  */
4
4
 
5
5
  import type { ComponentProps, JSX } from "react";
@@ -15,7 +15,7 @@ function Kbd({ className, ...props }: ComponentProps<"kbd">): JSX.Element {
15
15
  <kbd
16
16
  data-slot="kbd"
17
17
  className={cn(
18
- "pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-none bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*='size-'])]:size-3",
18
+ "pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-none bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none in-[.bg-primary]:bg-primary-foreground/20 in-[.bg-primary]:text-primary-foreground in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 [&_svg:not([class*='size-'])]:size-3",
19
19
  className,
20
20
  )}
21
21
  {...props}
@@ -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">
@@ -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>