okengine 0.2.6 → 0.2.8

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 (584) hide show
  1. package/AGENTS.md +24 -23
  2. package/README.md +52 -346
  3. package/docs/spec/console.md +165 -142
  4. package/docs/spec/example.md +490 -312
  5. package/docs/spec/four-applications.md +495 -314
  6. package/docs/spec/unified-theory.md +184 -175
  7. package/package.json +45 -37
  8. package/spec/manifest.v1.schema.json +40 -9
  9. package/src/auth/api-keys.ts +2 -5
  10. package/src/auth/attenuation.ts +2 -6
  11. package/src/auth/auth.test.ts +53 -31
  12. package/src/auth/invoke-as.ts +1 -3
  13. package/src/auth/operator.ts +8 -26
  14. package/src/auth/planes.ts +2 -7
  15. package/src/auth/plugin.ts +4 -7
  16. package/src/auth/roles.ts +1 -5
  17. package/src/auth/sessions.ts +10 -39
  18. package/src/cli/args.ts +1 -5
  19. package/src/cli/ask-dev-mode.ts +40 -0
  20. package/src/cli/build.ts +1 -2
  21. package/src/cli/client-add.test.ts +8 -15
  22. package/src/cli/client-add.ts +5 -14
  23. package/src/cli/completion.test.ts +1 -5
  24. package/src/cli/completion.ts +4 -17
  25. package/src/cli/db-auto-push.test.ts +64 -0
  26. package/src/cli/db-auto-push.ts +72 -0
  27. package/src/cli/db.test.ts +189 -0
  28. package/src/cli/db.ts +386 -0
  29. package/src/cli/dev-app-runner.ts +2 -8
  30. package/src/cli/dev-db-push.test.ts +163 -0
  31. package/src/cli/dev-mode.test.ts +45 -0
  32. package/src/cli/dev-mode.ts +78 -0
  33. package/src/cli/dev.test.ts +148 -21
  34. package/src/cli/dev.ts +208 -134
  35. package/src/cli/doc-drift.test.ts +3 -2
  36. package/src/cli/doc-drift.ts +21 -50
  37. package/src/cli/doc-staleness.test.ts +69 -0
  38. package/src/cli/docker-cli.test.ts +7 -8
  39. package/src/cli/docker.ts +1 -3
  40. package/src/cli/doctor-diff-examples.ts +1 -3
  41. package/src/cli/doctor-diff.test.ts +1 -5
  42. package/src/cli/doctor-diff.ts +17 -46
  43. package/src/cli/doctor-pii.ts +2 -7
  44. package/src/cli/doctor.test.ts +26 -4
  45. package/src/cli/doctor.ts +35 -11
  46. package/src/cli/eval.ts +4 -14
  47. package/src/cli/gates-list.ts +1 -4
  48. package/src/cli/hero-meta.test.ts +17 -19
  49. package/src/cli/hero-meta.ts +20 -30
  50. package/src/cli/images.ts +3 -8
  51. package/src/cli/index.ts +11 -2
  52. package/src/cli/load-config.images.test.ts +14 -14
  53. package/src/cli/load-config.ts +41 -47
  54. package/src/cli/mcp-from-console.ts +1 -4
  55. package/src/cli/mode.ts +45 -0
  56. package/src/cli/ports.test.ts +4 -13
  57. package/src/cli/ports.ts +1 -3
  58. package/src/cli/privacy-erase.ts +1 -3
  59. package/src/cli/registry.ts +75 -25
  60. package/src/cli/schema.ts +3 -10
  61. package/src/cli/stack.ts +2 -7
  62. package/src/cli/start.ts +4 -15
  63. package/src/cli/upgrade.ts +1 -4
  64. package/src/client/budget-entry.ts +1 -6
  65. package/src/client/create.test.ts +6 -12
  66. package/src/client/create.ts +2 -8
  67. package/src/client/errors.ts +1 -4
  68. package/src/client/index.ts +1 -6
  69. package/src/client/notes-contract.test.ts +4 -13
  70. package/src/client/transport.ts +9 -30
  71. package/src/client/types.ts +8 -29
  72. package/src/compiler/aot.test.ts +2 -9
  73. package/src/compiler/aot.ts +1 -4
  74. package/src/compiler/differential.test.ts +3 -11
  75. package/src/compiler/dynamic.ts +1 -4
  76. package/src/compiler/effects-infer.ts +8 -34
  77. package/src/compiler/emit.ts +1 -3
  78. package/src/compiler/extract.test.ts +96 -30
  79. package/src/compiler/extract.ts +362 -156
  80. package/src/compiler/fixtures/skyport/oke.config.ts +1 -1
  81. package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -3
  82. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -3
  83. package/src/compiler/fixtures/skyport/src/flows/support/index.ts +4 -1
  84. package/src/compiler/fixtures/skyport.expected.json +24 -83
  85. package/src/compiler/fixtures/triggers/five-triggers.ts +1 -2
  86. package/src/compiler/http-parse.ts +2 -9
  87. package/src/compiler/index.ts +1 -5
  88. package/src/compiler/response.ts +4 -7
  89. package/src/compiler/sucrose.ts +11 -31
  90. package/src/config/define-config.test.ts +57 -34
  91. package/src/config/index.ts +181 -37
  92. package/src/config/resolve-driver.test.ts +28 -17
  93. package/src/console/budget.test.ts +1 -4
  94. package/src/console/index.ts +1 -4
  95. package/src/console/server/access.test.ts +15 -43
  96. package/src/console/server/access.ts +19 -49
  97. package/src/console/server/ai.test.ts +3 -11
  98. package/src/console/server/ai.ts +23 -46
  99. package/src/console/server/app.ts +14 -45
  100. package/src/console/server/auth-rate.test.ts +4 -12
  101. package/src/console/server/bind.ts +1 -5
  102. package/src/console/server/channels.test.ts +2 -6
  103. package/src/console/server/channels.ts +10 -35
  104. package/src/console/server/claim.ts +2 -8
  105. package/src/console/server/clock.test.ts +2 -6
  106. package/src/console/server/clock.ts +8 -26
  107. package/src/console/server/console.test.ts +1 -3
  108. package/src/console/server/diff.test.ts +5 -17
  109. package/src/console/server/diff.ts +10 -31
  110. package/src/console/server/dry-run.audit.test.ts +2 -8
  111. package/src/console/server/flows.ts +64 -233
  112. package/src/console/server/gates.test.ts +14 -50
  113. package/src/console/server/gates.ts +13 -48
  114. package/src/console/server/index.ts +2 -9
  115. package/src/console/server/live.ts +3 -12
  116. package/src/console/server/operator-db.test.ts +4 -15
  117. package/src/console/server/operator-db.ts +13 -32
  118. package/src/console/server/plugin.ts +50 -53
  119. package/src/console/server/plugins.test.ts +2 -6
  120. package/src/console/server/plugins.ts +17 -56
  121. package/src/console/server/runs-pii.ts +2 -7
  122. package/src/console/server/security.gate.test.ts +18 -40
  123. package/src/console/server/serve.ts +10 -25
  124. package/src/console/server/signals.test.ts +1 -3
  125. package/src/console/server/signals.ts +17 -40
  126. package/src/console/server/state.ts +21 -59
  127. package/src/console/server/store.test.ts +20 -22
  128. package/src/console/server/store.ts +31 -76
  129. package/src/console/server/vault.test.ts +12 -29
  130. package/src/console/server/vault.ts +23 -60
  131. package/src/console/ui/access/AccessA11yView.tsx +6 -21
  132. package/src/console/ui/access/a11y.test.tsx +3 -12
  133. package/src/console/ui/access/acknowledgement.test.ts +1 -4
  134. package/src/console/ui/access/acknowledgement.ts +1 -3
  135. package/src/console/ui/access/blast-radius.ts +1 -3
  136. package/src/console/ui/access/confirmation.test.ts +1 -5
  137. package/src/console/ui/access/confirmation.ts +1 -4
  138. package/src/console/ui/access/fixture.ts +1 -5
  139. package/src/console/ui/access/grantable.test.ts +1 -3
  140. package/src/console/ui/access/grantable.ts +2 -7
  141. package/src/console/ui/access/index.ts +2 -9
  142. package/src/console/ui/access/provenance.test.ts +1 -3
  143. package/src/console/ui/access/provenance.ts +2 -6
  144. package/src/console/ui/access/search.test.ts +1 -5
  145. package/src/console/ui/access/search.ts +2 -6
  146. package/src/console/ui/ai/AiA11yView.tsx +6 -17
  147. package/src/console/ui/ai/a11y.test.tsx +2 -5
  148. package/src/console/ui/ai/fixture.ts +2 -7
  149. package/src/console/ui/ai/format.ts +1 -3
  150. package/src/console/ui/ai/group.test.ts +1 -3
  151. package/src/console/ui/ai/group.ts +4 -12
  152. package/src/console/ui/ai/index.ts +2 -9
  153. package/src/console/ui/ai/promotion.test.ts +2 -7
  154. package/src/console/ui/ai/promotion.ts +4 -13
  155. package/src/console/ui/ai/search.test.ts +2 -11
  156. package/src/console/ui/ai/search.ts +3 -13
  157. package/src/console/ui/ai/types.ts +1 -8
  158. package/src/console/ui/architecture/ArchitectureA11yView.tsx +8 -29
  159. package/src/console/ui/architecture/a11y.test.tsx +1 -2
  160. package/src/console/ui/architecture/declared.ts +1 -5
  161. package/src/console/ui/architecture/index.ts +7 -35
  162. package/src/console/ui/architecture/layout.ts +1 -3
  163. package/src/console/ui/architecture/pathologies.ts +10 -19
  164. package/src/console/ui/architecture/search.ts +6 -15
  165. package/src/console/ui/architecture/traffic.ts +4 -14
  166. package/src/console/ui/architecture/types.ts +2 -10
  167. package/src/console/ui/architecture/view.test.ts +7 -19
  168. package/src/console/ui/architecture/view.ts +9 -36
  169. package/src/console/ui/channels/ChannelsA11yView.tsx +4 -13
  170. package/src/console/ui/channels/a11y.test.tsx +1 -2
  171. package/src/console/ui/channels/confirmation.test.ts +1 -4
  172. package/src/console/ui/channels/confirmation.ts +1 -4
  173. package/src/console/ui/channels/findings.ts +1 -5
  174. package/src/console/ui/channels/index.ts +2 -8
  175. package/src/console/ui/channels/locale.test.ts +1 -3
  176. package/src/console/ui/channels/locale.ts +2 -8
  177. package/src/console/ui/channels/search.ts +4 -13
  178. package/src/console/ui/channels/taxonomy.ts +1 -3
  179. package/src/console/ui/clock/ClockA11yView.tsx +9 -26
  180. package/src/console/ui/clock/a11y.test.tsx +2 -5
  181. package/src/console/ui/clock/confirmation.test.ts +1 -3
  182. package/src/console/ui/clock/confirmation.ts +1 -4
  183. package/src/console/ui/clock/findings.ts +1 -4
  184. package/src/console/ui/clock/health.ts +2 -7
  185. package/src/console/ui/clock/index.ts +2 -10
  186. package/src/console/ui/clock/search.test.ts +1 -3
  187. package/src/console/ui/clock/search.ts +2 -6
  188. package/src/console/ui/clock/timeline.test.ts +1 -3
  189. package/src/console/ui/clock/waiting-on.test.ts +1 -5
  190. package/src/console/ui/clock/waiting-on.ts +2 -7
  191. package/src/console/ui/diff/DiffA11yView.tsx +8 -17
  192. package/src/console/ui/diff/a11y.test.tsx +2 -5
  193. package/src/console/ui/diff/group.test.ts +3 -13
  194. package/src/console/ui/diff/group.ts +2 -8
  195. package/src/console/ui/diff/index.ts +2 -8
  196. package/src/console/ui/diff/search.test.ts +1 -5
  197. package/src/console/ui/diff/search.ts +4 -16
  198. package/src/console/ui/diff/summary.ts +1 -2
  199. package/src/console/ui/dist/assets/{index-Dy4jht9P.js → index-BWo8R7NR.js} +2 -2
  200. package/src/console/ui/dist/assets/{panel-access-Dd37LU2c.js → panel-access-C0J2D-a2.js} +1 -1
  201. package/src/console/ui/dist/assets/{panel-ai-CC7LR6-J.js → panel-ai-D_m6WQI8.js} +1 -1
  202. package/src/console/ui/dist/assets/{panel-architecture-B5b3iKCz.js → panel-architecture-CKnXFyUx.js} +1 -1
  203. package/src/console/ui/dist/assets/{panel-channels-CeNjTKXp.js → panel-channels-BOmQ-onL.js} +1 -1
  204. package/src/console/ui/dist/assets/{panel-clock-DgFTLoHV.js → panel-clock-giAq0Ccv.js} +1 -1
  205. package/src/console/ui/dist/assets/{panel-diff-DxehccqB.js → panel-diff-cdonmH8c.js} +1 -1
  206. package/src/console/ui/dist/assets/{panel-flows-BtrVn-Eg.js → panel-flows-DlCU5zjA.js} +2 -2
  207. package/src/console/ui/dist/assets/{panel-gates-Z9MKRGdH.js → panel-gates-XclZxWD5.js} +1 -1
  208. package/src/console/ui/dist/assets/{panel-overview-Dt_AeXgd.js → panel-overview-BznEOTnb.js} +1 -1
  209. package/src/console/ui/dist/assets/{panel-plugins-DWd0TowH.js → panel-plugins-CcGM1g64.js} +1 -1
  210. package/src/console/ui/dist/assets/{panel-runs-DGstFHeq.js → panel-runs-CGWNHLR4.js} +1 -1
  211. package/src/console/ui/dist/assets/{panel-signals-DzEa2Fnt.js → panel-signals-CNywkdak.js} +1 -1
  212. package/src/console/ui/dist/assets/{panel-store-BJkbNgxx.js → panel-store-KmTbFHMH.js} +1 -1
  213. package/src/console/ui/dist/assets/{panel-traces-BaRVO3gM.js → panel-traces-DBLx2ilD.js} +1 -1
  214. package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +1 -0
  215. package/src/console/ui/dist/index.html +1 -1
  216. package/src/console/ui/flows/FlowsA11yView.tsx +7 -22
  217. package/src/console/ui/flows/a11y.test.tsx +2 -8
  218. package/src/console/ui/flows/buffer.test.ts +3 -11
  219. package/src/console/ui/flows/buffer.ts +4 -11
  220. package/src/console/ui/flows/confirmation.ts +1 -3
  221. package/src/console/ui/flows/contract.test.ts +3 -7
  222. package/src/console/ui/flows/contract.ts +12 -36
  223. package/src/console/ui/flows/graph.test.ts +3 -11
  224. package/src/console/ui/flows/graph.ts +5 -15
  225. package/src/console/ui/flows/index.ts +1 -6
  226. package/src/console/ui/flows/save-as-test.ts +1 -3
  227. package/src/console/ui/flows/search.ts +8 -25
  228. package/src/console/ui/flows/tiers.ts +2 -9
  229. package/src/console/ui/gates/GatesA11yView.tsx +4 -13
  230. package/src/console/ui/gates/a11y.test.tsx +2 -5
  231. package/src/console/ui/gates/audit.ts +1 -3
  232. package/src/console/ui/gates/denial.test.ts +1 -3
  233. package/src/console/ui/gates/denial.ts +4 -12
  234. package/src/console/ui/gates/findings.ts +1 -3
  235. package/src/console/ui/gates/group.test.ts +3 -3
  236. package/src/console/ui/gates/group.ts +3 -11
  237. package/src/console/ui/gates/index.ts +2 -10
  238. package/src/console/ui/gates/search.ts +3 -10
  239. package/src/console/ui/overview/OverviewA11yView.tsx +9 -27
  240. package/src/console/ui/overview/a11y.test.tsx +1 -2
  241. package/src/console/ui/overview/busiest.ts +1 -3
  242. package/src/console/ui/overview/compose.ts +1 -13
  243. package/src/console/ui/overview/cost.ts +34 -38
  244. package/src/console/ui/overview/fixture.ts +21 -26
  245. package/src/console/ui/overview/golden.ts +4 -14
  246. package/src/console/ui/overview/index.ts +2 -9
  247. package/src/console/ui/overview/rank.test.ts +3 -10
  248. package/src/console/ui/overview/rank.ts +2 -7
  249. package/src/console/ui/overview/slo.test.ts +5 -9
  250. package/src/console/ui/overview/slo.ts +6 -23
  251. package/src/console/ui/overview/verdict.ts +3 -10
  252. package/src/console/ui/plugins/PluginsA11yView.tsx +8 -18
  253. package/src/console/ui/plugins/a11y.test.tsx +1 -2
  254. package/src/console/ui/plugins/fixture.ts +2 -4
  255. package/src/console/ui/plugins/group.test.ts +1 -3
  256. package/src/console/ui/plugins/group.ts +1 -6
  257. package/src/console/ui/plugins/index.ts +3 -13
  258. package/src/console/ui/plugins/search.ts +7 -19
  259. package/src/console/ui/runs/RunsA11yView.tsx +5 -20
  260. package/src/console/ui/runs/a11y.test.tsx +2 -5
  261. package/src/console/ui/runs/explain.ts +2 -6
  262. package/src/console/ui/runs/group.ts +3 -12
  263. package/src/console/ui/runs/histogram.test.ts +2 -11
  264. package/src/console/ui/runs/histogram.ts +1 -4
  265. package/src/console/ui/runs/index.ts +4 -18
  266. package/src/console/ui/runs/project.ts +2 -6
  267. package/src/console/ui/runs/query.test.ts +1 -3
  268. package/src/console/ui/runs/query.ts +12 -35
  269. package/src/console/ui/runs/search.ts +5 -19
  270. package/src/console/ui/runs/trace-link.test.ts +2 -6
  271. package/src/console/ui/runs/trace-link.ts +4 -16
  272. package/src/console/ui/runs/types.ts +1 -3
  273. package/src/console/ui/shell/App.tsx +2 -10
  274. package/src/console/ui/shell/client.ts +22 -115
  275. package/src/console/ui/shell/components/ui.tsx +1 -4
  276. package/src/console/ui/shell/layout/Shell.tsx +2 -4
  277. package/src/console/ui/shell/main.tsx +31 -96
  278. package/src/console/ui/shell/panels/access/AccessPanel.tsx +34 -98
  279. package/src/console/ui/shell/panels/ai/AiPanel.tsx +35 -106
  280. package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +18 -66
  281. package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +20 -57
  282. package/src/console/ui/shell/panels/clock/ClockPanel.tsx +22 -54
  283. package/src/console/ui/shell/panels/diff/DiffPanel.tsx +20 -61
  284. package/src/console/ui/shell/panels/flows/ContractEditor.tsx +6 -23
  285. package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +13 -28
  286. package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +9 -29
  287. package/src/console/ui/shell/panels/gates/GatesPanel.tsx +24 -80
  288. package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +20 -58
  289. package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +16 -41
  290. package/src/console/ui/shell/panels/runs/RunsPanel.tsx +31 -99
  291. package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +38 -101
  292. package/src/console/ui/shell/panels/store/StorePanel.tsx +27 -66
  293. package/src/console/ui/shell/panels/traces/TracesPanel.tsx +28 -89
  294. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +22 -53
  295. package/src/console/ui/shell/setup/Wizard.tsx +3 -5
  296. package/src/console/ui/shell/styles.css +1 -2
  297. package/src/console/ui/signals/SignalsA11yView.tsx +9 -33
  298. package/src/console/ui/signals/a11y.test.tsx +2 -5
  299. package/src/console/ui/signals/confirmation.test.ts +1 -5
  300. package/src/console/ui/signals/confirmation.ts +2 -6
  301. package/src/console/ui/signals/dry-run.ts +1 -3
  302. package/src/console/ui/signals/durable.ts +1 -3
  303. package/src/console/ui/signals/findings.ts +1 -3
  304. package/src/console/ui/signals/group.test.ts +2 -8
  305. package/src/console/ui/signals/group.ts +2 -10
  306. package/src/console/ui/signals/index.ts +1 -4
  307. package/src/console/ui/signals/monitor.ts +2 -7
  308. package/src/console/ui/signals/schema-form.test.ts +3 -14
  309. package/src/console/ui/signals/schema-form.ts +1 -4
  310. package/src/console/ui/signals/search.ts +4 -13
  311. package/src/console/ui/store/StoreA11yView.tsx +3 -9
  312. package/src/console/ui/store/a11y.test.tsx +2 -5
  313. package/src/console/ui/store/confirmation.test.ts +1 -3
  314. package/src/console/ui/store/confirmation.ts +1 -4
  315. package/src/console/ui/store/dry-run.ts +2 -5
  316. package/src/console/ui/store/fixture.ts +1 -2
  317. package/src/console/ui/store/group.test.ts +1 -6
  318. package/src/console/ui/store/group.ts +1 -4
  319. package/src/console/ui/store/index.ts +1 -5
  320. package/src/console/ui/store/search.ts +2 -6
  321. package/src/console/ui/store/will-not-fire.ts +1 -2
  322. package/src/console/ui/traces/TracesA11yView.tsx +10 -29
  323. package/src/console/ui/traces/a11y.test.tsx +1 -2
  324. package/src/console/ui/traces/chain.test.ts +5 -19
  325. package/src/console/ui/traces/chain.ts +1 -3
  326. package/src/console/ui/traces/critical-path.test.ts +1 -3
  327. package/src/console/ui/traces/critical-path.ts +2 -6
  328. package/src/console/ui/traces/filter.test.ts +2 -6
  329. package/src/console/ui/traces/filter.ts +5 -13
  330. package/src/console/ui/traces/fold.test.ts +4 -12
  331. package/src/console/ui/traces/fold.ts +1 -3
  332. package/src/console/ui/traces/mini.ts +1 -3
  333. package/src/console/ui/traces/replay.test.ts +1 -3
  334. package/src/console/ui/traces/replay.ts +1 -3
  335. package/src/console/ui/traces/sampling.ts +1 -4
  336. package/src/console/ui/traces/search.ts +4 -17
  337. package/src/console/ui/traces/tier.ts +1 -3
  338. package/src/console/ui/vault/VaultA11yView.tsx +5 -17
  339. package/src/console/ui/vault/a11y.test.tsx +1 -2
  340. package/src/console/ui/vault/blast-radius.ts +1 -3
  341. package/src/console/ui/vault/confirmation.test.ts +1 -5
  342. package/src/console/ui/vault/confirmation.ts +1 -4
  343. package/src/console/ui/vault/dormant.test.ts +1 -4
  344. package/src/console/ui/vault/export-safe.test.ts +1 -5
  345. package/src/console/ui/vault/export-safe.ts +1 -4
  346. package/src/console/ui/vault/fixture.ts +3 -3
  347. package/src/console/ui/vault/group.ts +1 -2
  348. package/src/console/ui/vault/index.ts +2 -10
  349. package/src/console/ui/vault/search.ts +2 -6
  350. package/src/console/ui/vault/types.ts +1 -1
  351. package/src/console/xss.gate.test.ts +1 -3
  352. package/src/docker/compose.ts +112 -20
  353. package/src/docker/derive.ts +5 -19
  354. package/src/docker/docker.test.ts +15 -15
  355. package/src/docker/dockerfile.integration.test.ts +12 -21
  356. package/src/docker/helpers.ts +5 -4
  357. package/src/docker/index.ts +4 -11
  358. package/src/docker/pin.ts +1 -9
  359. package/src/docker/recipes/index.ts +5 -6
  360. package/src/docker/recipes/mailpit.ts +22 -0
  361. package/src/docker/recipes/postgres.ts +17 -6
  362. package/src/docker/recipes/redis.ts +6 -1
  363. package/src/docker/recipes/rustfs.ts +33 -0
  364. package/src/docker/stack-id.test.ts +33 -0
  365. package/src/docker/stack-id.ts +25 -13
  366. package/src/docker/stack.integration.test.ts +6 -21
  367. package/src/docker/types.ts +13 -4
  368. package/src/drivers/ai-anthropic.ts +5 -14
  369. package/src/drivers/ai-mock.ts +2 -5
  370. package/src/drivers/ai-openai-compatible.ts +4 -10
  371. package/src/drivers/ai-providers.test.ts +6 -13
  372. package/src/drivers/bun-native-completeness.test.ts +6 -15
  373. package/src/drivers/channel-console.ts +6 -7
  374. package/src/drivers/channel-fcm.ts +20 -30
  375. package/src/drivers/channel-resend.ts +1 -3
  376. package/src/drivers/channel-smtp.ts +2 -6
  377. package/src/drivers/channel-types.ts +2 -12
  378. package/src/drivers/channel-unifonic.ts +2 -6
  379. package/src/drivers/channel-wa-cloud.ts +2 -6
  380. package/src/drivers/channel-webpush.ts +21 -60
  381. package/src/drivers/conformance.test.ts +2 -10
  382. package/src/drivers/conformance.ts +8 -21
  383. package/src/drivers/fs.ts +2 -8
  384. package/src/drivers/index.ts +12 -56
  385. package/src/drivers/memory.ts +14 -41
  386. package/src/drivers/pgvector.ts +7 -11
  387. package/src/drivers/postgres.ts +7 -13
  388. package/src/drivers/redis.ts +13 -37
  389. package/src/drivers/s3.ts +5 -17
  390. package/src/drivers/signal-engine.ts +12 -49
  391. package/src/drivers/signal-memory.ts +2 -8
  392. package/src/drivers/signal-nats.ts +4 -9
  393. package/src/drivers/signal-postgres.ts +62 -129
  394. package/src/drivers/signal-redis.ts +7 -21
  395. package/src/drivers/signal-types.ts +1 -4
  396. package/src/drivers/sqlite.ts +3 -12
  397. package/src/drivers/types.ts +4 -23
  398. package/src/drivers/vault-env.ts +2 -6
  399. package/src/drivers/vault-infisical.ts +1 -5
  400. package/src/drivers/vault-managed.ts +1 -5
  401. package/src/drivers/vault-memory.ts +1 -5
  402. package/src/drivers/vault-openbao.ts +1 -5
  403. package/src/drivers/vault-sops.ts +10 -35
  404. package/src/drivers/vault-types.ts +1 -7
  405. package/src/elements/ai/declare.ts +4 -14
  406. package/src/elements/ai/eval.ts +2 -6
  407. package/src/elements/ai/pii.ts +1 -3
  408. package/src/elements/ai/runtime.ts +12 -50
  409. package/src/elements/ai/schema.ts +4 -15
  410. package/src/elements/ai.test.ts +13 -24
  411. package/src/elements/ai.ts +1 -5
  412. package/src/elements/channel/consent.ts +3 -12
  413. package/src/elements/channel/costs.ts +4 -15
  414. package/src/elements/channel/declare.ts +1 -4
  415. package/src/elements/channel/email-auth.ts +1 -4
  416. package/src/elements/channel/locale.ts +2 -10
  417. package/src/elements/channel/mask.ts +1 -3
  418. package/src/elements/channel/mime.ts +2 -12
  419. package/src/elements/channel/outcomes.test.ts +1 -5
  420. package/src/elements/channel/outcomes.ts +5 -15
  421. package/src/elements/channel/receipts.ts +2 -7
  422. package/src/elements/channel/runtime.ts +28 -74
  423. package/src/elements/channel/suppression.ts +8 -14
  424. package/src/elements/channel.test.ts +8 -31
  425. package/src/elements/channel.ts +3 -14
  426. package/src/elements/clock/actions.ts +6 -18
  427. package/src/elements/clock/declare.ts +1 -3
  428. package/src/elements/clock/dst.ts +1 -5
  429. package/src/elements/clock/durable.ts +4 -16
  430. package/src/elements/clock/health.ts +2 -7
  431. package/src/elements/clock/leader.ts +2 -9
  432. package/src/elements/clock/reconcile.ts +4 -15
  433. package/src/elements/clock/runtime.ts +2 -5
  434. package/src/elements/clock/schedule.ts +4 -20
  435. package/src/elements/clock.test.ts +2 -7
  436. package/src/elements/clock.ts +5 -23
  437. package/src/elements/gate/permissions.ts +1 -4
  438. package/src/elements/gate/runtime.ts +7 -28
  439. package/src/elements/gate.ts +1 -5
  440. package/src/elements/index.ts +9 -7
  441. package/src/elements/signal/declare.ts +2 -7
  442. package/src/elements/signal/reconcile.test.ts +1 -4
  443. package/src/elements/signal/runtime.ts +1 -3
  444. package/src/elements/signal.test.ts +3 -15
  445. package/src/elements/signal.ts +2 -8
  446. package/src/elements/store/cache.test.ts +46 -0
  447. package/src/elements/store/cache.ts +3 -11
  448. package/src/elements/store/classify.ts +5 -22
  449. package/src/elements/store/declare.ts +8 -17
  450. package/src/elements/store/domain-ddl.test.ts +86 -0
  451. package/src/elements/store/emit-drizzle.ts +363 -0
  452. package/src/elements/store/files-policy.test.ts +1 -5
  453. package/src/elements/store/files-policy.ts +4 -12
  454. package/src/elements/store/load-plugin-tables.ts +103 -0
  455. package/src/elements/store/missing-relation.test.ts +83 -0
  456. package/src/elements/store/missing-relation.ts +38 -0
  457. package/src/elements/store/replica.ts +1 -4
  458. package/src/elements/store/runtime.ts +25 -28
  459. package/src/elements/store/schema-decl.test.ts +227 -0
  460. package/src/elements/store/schema-decl.ts +516 -0
  461. package/src/elements/store/sql-condition.ts +11 -21
  462. package/src/elements/store/sql-session.test.ts +50 -0
  463. package/src/elements/store/sql-session.ts +60 -63
  464. package/src/elements/store/table.ts +35 -19
  465. package/src/elements/store.test.ts +17 -39
  466. package/src/elements/store.ts +37 -31
  467. package/src/elements/vault/chain.ts +34 -0
  468. package/src/elements/vault/declare.ts +22 -32
  469. package/src/elements/vault/fingerprint.ts +2 -7
  470. package/src/elements/vault/redact.ts +2 -6
  471. package/src/elements/vault/runtime.ts +18 -32
  472. package/src/elements/vault.test.ts +12 -28
  473. package/src/elements/vault.ts +8 -12
  474. package/src/index.ts +5 -2
  475. package/src/kernel/adopt-routes.ts +15 -29
  476. package/src/kernel/app.ts +34 -94
  477. package/src/kernel/auth-resolve.ts +2 -9
  478. package/src/kernel/boot-bind/ai.ts +1 -4
  479. package/src/kernel/boot-bind/channel.ts +2 -8
  480. package/src/kernel/boot-bind/clock.ts +2 -6
  481. package/src/kernel/boot-bind/gate.ts +2 -8
  482. package/src/kernel/boot-bind/runs.ts +1 -3
  483. package/src/kernel/boot-bind/signal.ts +3 -10
  484. package/src/kernel/boot-bind/store.test.ts +17 -17
  485. package/src/kernel/boot-bind/store.ts +44 -50
  486. package/src/kernel/boot-bind/vault.ts +2 -8
  487. package/src/kernel/boot.test.ts +4 -9
  488. package/src/kernel/boot.ts +29 -76
  489. package/src/kernel/budget.test.ts +1 -3
  490. package/src/kernel/capability.ts +3 -10
  491. package/src/kernel/dry-run.test.ts +1 -4
  492. package/src/kernel/dry-run.ts +3 -11
  493. package/src/kernel/effects.test.ts +1 -5
  494. package/src/kernel/effects.ts +4 -11
  495. package/src/kernel/errors.registry.test.ts +2 -8
  496. package/src/kernel/errors.ts +23 -32
  497. package/src/kernel/flow.test.ts +12 -25
  498. package/src/kernel/flow.ts +5 -25
  499. package/src/kernel/fx.test.ts +15 -44
  500. package/src/kernel/fx.ts +27 -79
  501. package/src/kernel/hook-timing.test.ts +3 -6
  502. package/src/kernel/hook-timing.ts +5 -14
  503. package/src/kernel/hooks.test.ts +10 -10
  504. package/src/kernel/hooks.ts +3 -12
  505. package/src/kernel/index.ts +4 -17
  506. package/src/kernel/journal.ts +7 -31
  507. package/src/kernel/on.ts +1 -6
  508. package/src/kernel/pipeline.test.ts +10 -19
  509. package/src/kernel/pipeline.ts +5 -17
  510. package/src/kernel/plug.ts +4 -11
  511. package/src/kernel/plugin/conflicts.test.ts +13 -25
  512. package/src/kernel/plugin/decorate.test.ts +8 -13
  513. package/src/kernel/plugin/scoping.test.ts +4 -11
  514. package/src/kernel/plugin.ts +48 -24
  515. package/src/kernel/registry.ts +80 -29
  516. package/src/kernel/router.test.ts +15 -13
  517. package/src/kernel/router.ts +9 -29
  518. package/src/kernel/routing-budget.test.ts +1 -3
  519. package/src/kernel/run-telemetry.ts +1 -3
  520. package/src/kernel/triggers.ts +4 -21
  521. package/src/manifest/diff.test.ts +26 -91
  522. package/src/manifest/diff.ts +95 -225
  523. package/src/manifest/index.ts +2 -9
  524. package/src/manifest/types.ts +29 -5
  525. package/src/manifest/undeclared.test.ts +4 -12
  526. package/src/manifest/undeclared.ts +1 -2
  527. package/src/manifest/validate.test.ts +2 -6
  528. package/src/manifest/validate.ts +4 -14
  529. package/src/mcp/authorization.ts +8 -27
  530. package/src/mcp/confirmation.ts +3 -10
  531. package/src/mcp/data.ts +2 -6
  532. package/src/mcp/docs-index.ts +4 -7
  533. package/src/mcp/docs-mcp.test.ts +3 -12
  534. package/src/mcp/docs-server.ts +7 -27
  535. package/src/mcp/docs-tools.ts +2 -6
  536. package/src/mcp/index.ts +1 -6
  537. package/src/mcp/injection.gate.test.ts +4 -18
  538. package/src/mcp/mcp.test.ts +18 -49
  539. package/src/mcp/protocol.ts +3 -10
  540. package/src/mcp/server.ts +11 -52
  541. package/src/mcp/session.ts +4 -14
  542. package/src/mcp/tools.ts +14 -50
  543. package/src/plugins/catalogue.test.ts +3 -7
  544. package/src/plugins/catalogue.ts +1 -3
  545. package/src/plugins/node-import-scan.ts +2 -5
  546. package/src/plugins/supply-chain.ts +5 -17
  547. package/src/release/exports.ts +3 -8
  548. package/src/release/measure.exports.test.ts +1 -3
  549. package/src/release/measure.ts +15 -43
  550. package/src/runs/bench.test.ts +1 -3
  551. package/src/runs/collect.ts +8 -25
  552. package/src/runs/drivers/clickhouse.ts +2 -9
  553. package/src/runs/drivers/files.ts +9 -31
  554. package/src/runs/drivers/memory.ts +5 -22
  555. package/src/runs/drivers/postgres.ts +8 -20
  556. package/src/runs/duckdb.ts +2 -8
  557. package/src/runs/index.ts +3 -14
  558. package/src/runs/outlier.ts +3 -10
  559. package/src/runs/parquet.ts +8 -26
  560. package/src/runs/privacy.ts +2 -8
  561. package/src/runs/runs.test.ts +3 -9
  562. package/src/runs/runtime.ts +7 -32
  563. package/src/runs/shred.ts +10 -29
  564. package/src/runs/types.ts +1 -4
  565. package/src/runtime/bun.ts +4 -15
  566. package/src/runtime/cold-start.bench.ts +1 -3
  567. package/src/runtime/dev-request-log.test.ts +1 -4
  568. package/src/runtime/dev-request-log.ts +1 -4
  569. package/src/runtime/index.ts +1 -6
  570. package/src/runtime/primitives.ts +5 -4
  571. package/src/runtime/security.ts +3 -13
  572. package/src/runtime/serve.test.ts +4 -7
  573. package/src/runtime/types.ts +1 -4
  574. package/src/runtime/web-standard.ts +3 -15
  575. package/src/term.test.ts +4 -6
  576. package/src/term.ts +49 -98
  577. package/src/test/create-test-app.test.ts +4 -14
  578. package/src/test/create-test-app.ts +13 -31
  579. package/src/test/provisions.integration.test.ts +3 -11
  580. package/src/upgrade/codemods.test.ts +25 -6
  581. package/src/upgrade/codemods.ts +116 -3
  582. package/src/validation/standard-schema.test.ts +2 -5
  583. package/src/validation/standard-schema.ts +13 -33
  584. package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +0 -1
@@ -15,6 +15,7 @@ import type {
15
15
  AiPrompt,
16
16
  Channel,
17
17
  Clock,
18
+ DeclaredColumn,
18
19
  Effects,
19
20
  Flow,
20
21
  Gate,
@@ -66,6 +67,11 @@ interface ProjectScope {
66
67
  bindings: Map<string, InferBinding>;
67
68
  signals: Record<string, Signal>;
68
69
  stores: Record<string, Store>;
70
+ /** Binding or table name → declared columns from `store.schema.table`. */
71
+ schemaTables: Map<
72
+ string,
73
+ { readonly name: string; readonly columns: Record<string, DeclaredColumn> }
74
+ >;
69
75
  clocks: Record<string, Clock>;
70
76
  gates: Record<string, Gate>;
71
77
  /** Local binding name → gate manifest id (policy name or rate expression). */
@@ -89,9 +95,7 @@ interface ProjectScope {
89
95
  *
90
96
  * @param options - Root directory and/or explicit files
91
97
  */
92
- export async function extractManifest(
93
- options: ExtractManifestOptions = {},
94
- ): Promise<Manifest> {
98
+ export async function extractManifest(options: ExtractManifestOptions = {}): Promise<Manifest> {
95
99
  const files = options.files
96
100
  ? [...options.files]
97
101
  : await readSources(options.rootDir ?? ".", options.glob ?? "**/*.{ts,tsx}");
@@ -101,6 +105,7 @@ export async function extractManifest(
101
105
  bindings: new Map(),
102
106
  signals: {},
103
107
  stores: {},
108
+ schemaTables: new Map(),
104
109
  clocks: {},
105
110
  gates: {},
106
111
  gateIds: new Map(),
@@ -191,10 +196,7 @@ export async function extractFromSources(
191
196
  return extractManifest({ files, app });
192
197
  }
193
198
 
194
- async function readSources(
195
- rootDir: string,
196
- pattern: string,
197
- ): Promise<SourceFile[]> {
199
+ async function readSources(rootDir: string, pattern: string): Promise<SourceFile[]> {
198
200
  // Bun.Glob (global) — bare `import … from "bun"` is rejected by JSR.
199
201
  const glob = new Bun.Glob(pattern);
200
202
  const files: SourceFile[] = [];
@@ -273,13 +275,304 @@ function finalizeRefs(scope: ProjectScope): void {
273
275
  (id) => scope.flowExports.get(id) ?? scope.bindings.get(id)?.ref ?? id,
274
276
  );
275
277
  }
278
+
279
+ // Merge declared columns into store.tables (handles declare-after-sql order).
280
+ for (const store of Object.values(scope.stores)) {
281
+ if (!store.tables) continue;
282
+ for (const [name, table] of Object.entries(store.tables)) {
283
+ const declared = [...scope.schemaTables.values()].find((t) => t.name === name);
284
+ if (!declared) continue;
285
+ const existing = table.columns ?? {};
286
+ if (Object.keys(existing).length === 0) {
287
+ store.tables[name] = { ...table, columns: declared.columns };
288
+ }
289
+ }
290
+ }
291
+
292
+ // Orphan abstract tables (declared but not wired via store.sql schema):
293
+ // attach under the first sql store, or create a synthetic "app" sql store.
294
+ const attachedNames = new Set<string>();
295
+ for (const store of Object.values(scope.stores)) {
296
+ if (!store.tables) continue;
297
+ for (const name of Object.keys(store.tables)) attachedNames.add(name);
298
+ }
299
+ const orphanByName = new Map<string, { name: string; columns: Record<string, DeclaredColumn> }>();
300
+ for (const t of scope.schemaTables.values()) {
301
+ if (!attachedNames.has(t.name)) orphanByName.set(t.name, t);
302
+ }
303
+ if (orphanByName.size > 0) {
304
+ let target = Object.entries(scope.stores).find(([, s]) => s.facet === "sql")?.[1];
305
+ if (!target) {
306
+ scope.stores.app = scope.stores.app ?? { facet: "sql" };
307
+ target = scope.stores.app!;
308
+ }
309
+ target.tables = target.tables ?? {};
310
+ for (const t of orphanByName.values()) {
311
+ if (target.tables[t.name]) continue;
312
+ target.tables[t.name] = { columns: t.columns };
313
+ }
314
+ }
276
315
  }
277
316
 
278
- function visitDeclarationCall(
279
- call: CallExpression,
280
- program: AstNode,
317
+ function collectSchemaTable(call: CallExpression, program: AstNode, scope: ProjectScope): void {
318
+ const tableName = stringArg(call.arguments[0]);
319
+ if (!tableName) return;
320
+ const colsNode = objectArg(call.arguments[1]);
321
+ const columns = colsNode ? parseDeclaredColumns(colsNode) : {};
322
+ const entry = { name: tableName, columns };
323
+ scope.schemaTables.set(tableName, entry);
324
+ const bindingName = enclosingConstName(call, program);
325
+ if (bindingName) scope.schemaTables.set(bindingName, entry);
326
+ }
327
+
328
+ function attachSchemaOption(
329
+ optsNode: AstNode | undefined,
330
+ store: Store,
281
331
  scope: ProjectScope,
282
332
  ): void {
333
+ const opts = objectArg(optsNode);
334
+ if (!opts) return;
335
+ const schemaNode = objectProp(opts, "schema");
336
+ if (!schemaNode) return;
337
+
338
+ // schema: { notes } or schema: { notes: store.schema.table(...) }
339
+ if (schemaNode.type === "ObjectExpression") {
340
+ store.tables = store.tables ?? {};
341
+ for (const prop of objectProperties(schemaNode)) {
342
+ const key = propKey(prop);
343
+ const value = (prop as AstNode & { value?: AstNode }).value;
344
+ if (!key || !value) continue;
345
+
346
+ if (value.type === "Identifier") {
347
+ const id = (value as Identifier).name;
348
+ const declared = scope.schemaTables.get(id) ?? scope.schemaTables.get(key);
349
+ if (declared) {
350
+ store.tables[declared.name] = { columns: declared.columns };
351
+ } else {
352
+ store.tables[key] = store.tables[key] ?? {};
353
+ }
354
+ continue;
355
+ }
356
+
357
+ if (value.type === "CallExpression") {
358
+ const inline = parseInlineSchemaTable(value as CallExpression);
359
+ if (inline) {
360
+ store.tables[inline.name] = { columns: inline.columns };
361
+ scope.schemaTables.set(inline.name, inline);
362
+ }
363
+ }
364
+ }
365
+ return;
366
+ }
367
+
368
+ // schema: notesModule (identifier — unknown shape; skip columns)
369
+ if (schemaNode.type === "Identifier") {
370
+ // Namespace import — table names unknown at extract time.
371
+ }
372
+ }
373
+
374
+ function parseInlineSchemaTable(
375
+ call: CallExpression,
376
+ ): { name: string; columns: Record<string, DeclaredColumn> } | undefined {
377
+ const callee = call.callee;
378
+ if (callee.type !== "MemberExpression") return undefined;
379
+ const member = callee as AstNode & { object: AstNode; property: AstNode };
380
+ if (identifierName(member.property) !== "table") return undefined;
381
+ if (member.object.type !== "MemberExpression") return undefined;
382
+ const inner = member.object as AstNode & { object: AstNode; property: AstNode };
383
+ if (identifierName(inner.object) !== "store" || identifierName(inner.property) !== "schema") {
384
+ return undefined;
385
+ }
386
+ const tableName = stringArg(call.arguments[0]);
387
+ if (!tableName) return undefined;
388
+ const colsNode = objectArg(call.arguments[1]);
389
+ return {
390
+ name: tableName,
391
+ columns: colsNode ? parseDeclaredColumns(colsNode) : {},
392
+ };
393
+ }
394
+
395
+ function parseDeclaredColumns(obj: AstNode): Record<string, DeclaredColumn> {
396
+ const out: Record<string, DeclaredColumn> = {};
397
+ for (const prop of objectProperties(obj)) {
398
+ const key = propKey(prop);
399
+ const value = (prop as AstNode & { value?: AstNode }).value;
400
+ if (!key || !value) continue;
401
+ const col = parseFieldChain(value, key);
402
+ if (col) out[key] = col;
403
+ }
404
+ return out;
405
+ }
406
+
407
+ /**
408
+ * Walk `field.text().notNull().pii()` (and similar) into a DeclaredColumn.
409
+ *
410
+ * @param node - AST node for the column value
411
+ * @param key - JS key (for default sqlName)
412
+ */
413
+ function parseFieldChain(node: AstNode, key: string): DeclaredColumn | undefined {
414
+ const chain: string[] = [];
415
+ let sqlType: "text" | "integer" | undefined;
416
+ let sqlName: string | undefined;
417
+ let defaultValue: string | number | boolean | null | undefined;
418
+ let hasDefault = false;
419
+ let cur: AstNode | undefined = node;
420
+
421
+ while (cur && cur.type === "CallExpression") {
422
+ const call = cur as CallExpression;
423
+ const callee = call.callee;
424
+ if (callee.type === "MemberExpression") {
425
+ const member = callee as AstNode & { object: AstNode; property: AstNode };
426
+ const method = identifierName(member.property);
427
+ if (method) {
428
+ chain.push(method);
429
+ if (method === "as") {
430
+ sqlName = stringArg(call.arguments[0]) ?? sqlName;
431
+ }
432
+ if (method === "default") {
433
+ const lit = call.arguments[0];
434
+ if (lit && lit.type === "Literal") {
435
+ const v = (lit as Literal).value;
436
+ if (
437
+ v === null ||
438
+ typeof v === "string" ||
439
+ typeof v === "number" ||
440
+ typeof v === "boolean"
441
+ ) {
442
+ defaultValue = v as string | number | boolean | null;
443
+ hasDefault = true;
444
+ }
445
+ }
446
+ }
447
+ }
448
+ cur = member.object;
449
+ continue;
450
+ }
451
+ if (callee.type === "Identifier" && (callee as Identifier).name === "field") {
452
+ // shouldn't happen — field.text() is MemberExpression
453
+ break;
454
+ }
455
+ break;
456
+ }
457
+
458
+ // Remaining should be field.text / field.integer
459
+ if (cur && cur.type === "MemberExpression") {
460
+ const member = cur as AstNode & { object: AstNode; property: AstNode };
461
+ if (identifierName(member.object) === "field") {
462
+ const t = identifierName(member.property);
463
+ if (t === "text" || t === "integer") sqlType = t;
464
+ }
465
+ }
466
+
467
+ // Also: field.text() ends as CallExpression with callee MemberExpression field.text
468
+ if (!sqlType && node.type === "CallExpression") {
469
+ // Re-walk to find field.text() / field.integer() as a call
470
+ let probe: AstNode | undefined = node;
471
+ while (probe && probe.type === "CallExpression") {
472
+ const call = probe as CallExpression;
473
+ const callee = call.callee;
474
+ if (callee.type === "MemberExpression") {
475
+ const member = callee as AstNode & { object: AstNode; property: AstNode };
476
+ if (
477
+ identifierName(member.object) === "field" &&
478
+ (identifierName(member.property) === "text" ||
479
+ identifierName(member.property) === "integer")
480
+ ) {
481
+ sqlType = identifierName(member.property) as "text" | "integer";
482
+ break;
483
+ }
484
+ probe = member.object;
485
+ continue;
486
+ }
487
+ break;
488
+ }
489
+ }
490
+
491
+ if (!sqlType) return undefined;
492
+
493
+ const methods = new Set(chain);
494
+ const col: DeclaredColumn = {
495
+ type: sqlType,
496
+ sqlName: sqlName ?? camelToSnakeKey(key),
497
+ nullable: !(methods.has("notNull") || methods.has("primaryKey")),
498
+ ...(methods.has("primaryKey") ? { primaryKey: true } : {}),
499
+ ...(methods.has("unique") ? { unique: true } : {}),
500
+ ...(hasDefault ? { default: defaultValue ?? null } : {}),
501
+ ...(methods.has("pii") ? { pii: true } : {}),
502
+ ...(methods.has("sensitive") ? { sensitive: true } : {}),
503
+ };
504
+ if (methods.has("retain")) {
505
+ // retain("7y") — find the call in the original chain by re-walk
506
+ let probe: AstNode | undefined = node;
507
+ while (probe && probe.type === "CallExpression") {
508
+ const call = probe as CallExpression;
509
+ const callee = call.callee;
510
+ if (callee.type === "MemberExpression") {
511
+ const member = callee as AstNode & { object: AstNode; property: AstNode };
512
+ if (identifierName(member.property) === "retain") {
513
+ const dur = stringArg(call.arguments[0]);
514
+ if (dur) col.retain = dur;
515
+ break;
516
+ }
517
+ probe = member.object;
518
+ continue;
519
+ }
520
+ break;
521
+ }
522
+ }
523
+ if (methods.has("references")) {
524
+ // .references(() => links.code) — record table.column when statically readable
525
+ let probe: AstNode | undefined = node;
526
+ while (probe && probe.type === "CallExpression") {
527
+ const call = probe as CallExpression;
528
+ const callee = call.callee;
529
+ if (callee.type === "MemberExpression") {
530
+ const member = callee as AstNode & { object: AstNode; property: AstNode };
531
+ if (identifierName(member.property) === "references") {
532
+ const ref = parseReferencesArg(call.arguments[0]);
533
+ if (ref) col.references = ref;
534
+ break;
535
+ }
536
+ probe = member.object;
537
+ continue;
538
+ }
539
+ break;
540
+ }
541
+ }
542
+ return col;
543
+ }
544
+
545
+ /**
546
+ * Parse `.references(() => table.column)` into a Manifest reference.
547
+ *
548
+ * @param arg - First argument to `.references(...)`
549
+ */
550
+ function parseReferencesArg(
551
+ arg: AstNode | undefined,
552
+ ): { table?: string; column?: string } | undefined {
553
+ if (!arg) return undefined;
554
+ // ArrowFunctionExpression: () => links.code
555
+ if (arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression") {
556
+ const fn = arg as AstNode & { body: AstNode };
557
+ const body = fn.body;
558
+ if (body.type === "MemberExpression") {
559
+ const member = body as AstNode & { object: AstNode; property: AstNode };
560
+ const table = identifierName(member.object);
561
+ const column = identifierName(member.property);
562
+ if (table || column) return { ...(table ? { table } : {}), ...(column ? { column } : {}) };
563
+ }
564
+ }
565
+ return {};
566
+ }
567
+
568
+ function camelToSnakeKey(key: string): string {
569
+ return key
570
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
571
+ .replace(/([A-Z])([A-Z][a-z])/g, "$1_$2")
572
+ .toLowerCase();
573
+ }
574
+
575
+ function visitDeclarationCall(call: CallExpression, program: AstNode, scope: ProjectScope): void {
283
576
  const callee = call.callee;
284
577
 
285
578
  // store.sql("name") / store.kv(...) / …
@@ -299,6 +592,9 @@ function visitDeclarationCall(
299
592
  const ref = `${facet}:${storeName}` as const;
300
593
  const bindingName = enclosingConstName(call, program);
301
594
  scope.stores[storeName] = scope.stores[storeName] ?? { facet };
595
+ if (facet === "sql") {
596
+ attachSchemaOption(call.arguments[1], scope.stores[storeName]!, scope);
597
+ }
302
598
  if (bindingName) {
303
599
  scope.bindings.set(bindingName, {
304
600
  kind: "store",
@@ -309,6 +605,14 @@ function visitDeclarationCall(
309
605
  }
310
606
  }
311
607
 
608
+ // store.schema.table("notes", { … })
609
+ if (prop === "table" && member.object.type === "MemberExpression") {
610
+ const inner = member.object as AstNode & { object: AstNode; property: AstNode };
611
+ if (identifierName(inner.object) === "store" && identifierName(inner.property) === "schema") {
612
+ collectSchemaTable(call, program, scope);
613
+ }
614
+ }
615
+
312
616
  if (obj === "gate" && prop === "policy") {
313
617
  const policyName = stringArg(call.arguments[0]);
314
618
  if (policyName) {
@@ -326,8 +630,7 @@ function visitDeclarationCall(
326
630
 
327
631
  if (obj === "gate" && prop === "rate") {
328
632
  const opts = objectArg(call.arguments[0]);
329
- const strategy = (stringProp(opts, "strategy") ??
330
- "sliding-window-counter") as RateStrategy;
633
+ const strategy = (stringProp(opts, "strategy") ?? "sliding-window-counter") as RateStrategy;
331
634
  const max = numberProp(opts, "max");
332
635
  const per = stringProp(opts, "per");
333
636
  const keyBy = stringProp(opts, "keyBy");
@@ -362,9 +665,7 @@ function visitDeclarationCall(
362
665
  const medium = stringProp(opts, "medium");
363
666
  const locales = stringArrayProp(opts, "locales");
364
667
  scope.channels[templateName] = {
365
- ...(medium
366
- ? { medium: medium as Channel["medium"] }
367
- : { medium: "email" }),
668
+ ...(medium ? { medium: medium as Channel["medium"] } : { medium: "email" }),
368
669
  ...(locales ? { locales } : {}),
369
670
  };
370
671
  const bindingName = enclosingConstName(call, program);
@@ -382,12 +683,8 @@ function visitDeclarationCall(
382
683
  const opts = objectArg(call.arguments[1]);
383
684
  if (modelName) {
384
685
  const model: AiModel = {
385
- ...(stringProp(opts, "provider")
386
- ? { provider: stringProp(opts, "provider") }
387
- : {}),
388
- ...(stringProp(opts, "tier")
389
- ? { tier: stringProp(opts, "tier") }
390
- : {}),
686
+ ...(stringProp(opts, "provider") ? { provider: stringProp(opts, "provider") } : {}),
687
+ ...(stringProp(opts, "tier") ? { tier: stringProp(opts, "tier") } : {}),
391
688
  };
392
689
  scope.ai.models = scope.ai.models ?? {};
393
690
  scope.ai.models[modelName] = model;
@@ -463,9 +760,7 @@ function visitDeclarationCall(
463
760
  ...(stringProp(opts, "description")
464
761
  ? { description: stringProp(opts, "description") }
465
762
  : {}),
466
- ...(stringProp(opts, "rotate")
467
- ? { rotate: stringProp(opts, "rotate") }
468
- : {}),
763
+ ...(stringProp(opts, "rotate") ? { rotate: stringProp(opts, "rotate") } : {}),
469
764
  };
470
765
  const bindingName = enclosingConstName(call, program);
471
766
  if (bindingName) {
@@ -512,9 +807,7 @@ function visitDeclarationCall(
512
807
  const opts = objectArg(call.arguments[1]);
513
808
  if (name && opts) {
514
809
  scope.clocks[name] = {
515
- ...(stringProp(opts, "every")
516
- ? { every: stringProp(opts, "every") }
517
- : {}),
810
+ ...(stringProp(opts, "every") ? { every: stringProp(opts, "every") } : {}),
518
811
  ...(stringProp(opts, "cron") ? { cron: stringProp(opts, "cron") } : {}),
519
812
  ...(boolProp(opts, "overridable") !== undefined
520
813
  ? { overridable: boolProp(opts, "overridable") }
@@ -547,9 +840,7 @@ function visitDeclarationCall(
547
840
  },
548
841
  }
549
842
  : {}),
550
- ...(stringProp(opts, "composes")
551
- ? { composes: stringProp(opts, "composes") }
552
- : {}),
843
+ ...(stringProp(opts, "composes") ? { composes: stringProp(opts, "composes") } : {}),
553
844
  ...(flows && flows.length > 0 ? { flows } : {}),
554
845
  };
555
846
  scope.journeys[name] = journey;
@@ -570,9 +861,7 @@ function visitDeclarationCall(
570
861
  ...(stringProp(opts, "description")
571
862
  ? { description: stringProp(opts, "description") }
572
863
  : {}),
573
- ...(stringProp(opts, "rotate")
574
- ? { rotate: stringProp(opts, "rotate") }
575
- : {}),
864
+ ...(stringProp(opts, "rotate") ? { rotate: stringProp(opts, "rotate") } : {}),
576
865
  };
577
866
  const bindingName = enclosingConstName(call, program);
578
867
  if (bindingName) {
@@ -601,20 +890,13 @@ function collectAgent(call: CallExpression, scope: ProjectScope): void {
601
890
  scope.ai.agents[agentName] = agent;
602
891
  }
603
892
 
604
- function collectConfig(
605
- opts: AstNode | undefined,
606
- scope: ProjectScope,
607
- ): void {
893
+ function collectConfig(opts: AstNode | undefined, scope: ProjectScope): void {
608
894
  if (!opts) return;
609
895
 
610
896
  const tenancy = objectProp(opts, "tenancy");
611
897
  if (tenancy) {
612
898
  const isolation = stringProp(tenancy, "isolation");
613
- if (
614
- isolation === "row" ||
615
- isolation === "schema" ||
616
- isolation === "database"
617
- ) {
899
+ if (isolation === "row" || isolation === "schema" || isolation === "database") {
618
900
  scope.tenancy = { isolation };
619
901
  }
620
902
  }
@@ -622,21 +904,15 @@ function collectConfig(
622
904
  const i18n = objectProp(opts, "i18n");
623
905
  if (i18n) {
624
906
  scope.i18n = {
625
- ...(stringArrayProp(i18n, "locales")
626
- ? { locales: stringArrayProp(i18n, "locales") }
627
- : {}),
628
- ...(stringProp(i18n, "default")
629
- ? { default: stringProp(i18n, "default") }
630
- : {}),
907
+ ...(stringArrayProp(i18n, "locales") ? { locales: stringArrayProp(i18n, "locales") } : {}),
908
+ ...(stringProp(i18n, "default") ? { default: stringProp(i18n, "default") } : {}),
631
909
  };
632
910
  const dir = objectProp(i18n, "dir");
633
911
  if (dir && dir.type === "ObjectExpression") {
634
912
  const dirMap: Record<string, "ltr" | "rtl"> = {};
635
913
  for (const prop of objectProperties(dir)) {
636
914
  const key = propKey(prop);
637
- const val = stringArg(
638
- (prop as AstNode & { value?: AstNode }).value,
639
- );
915
+ const val = stringArg((prop as AstNode & { value?: AstNode }).value);
640
916
  if (key && (val === "ltr" || val === "rtl")) dirMap[key] = val;
641
917
  }
642
918
  if (Object.keys(dirMap).length > 0) {
@@ -666,9 +942,7 @@ function collectConfig(
666
942
  if (drivers) {
667
943
  const prodArr = arrayProp(drivers, "prod");
668
944
  if (prodArr && prodArr.length > 0 && prodArr.every((el) => stringArg(el))) {
669
- scope.drivers.prod = prodArr
670
- .map((el) => stringArg(el)!)
671
- .filter((x) => x.length > 0);
945
+ scope.drivers.prod = prodArr.map((el) => stringArg(el)!).filter((x) => x.length > 0);
672
946
  } else {
673
947
  const prod = new Set<string>();
674
948
  collectDriverProtocols(drivers, "prod", prod);
@@ -677,11 +951,7 @@ function collectConfig(
677
951
  }
678
952
  }
679
953
 
680
- function collectDriverProtocols(
681
- node: AstNode,
682
- env: string,
683
- into: Set<string>,
684
- ): void {
954
+ function collectDriverProtocols(node: AstNode, env: string, into: Set<string>): void {
685
955
  if (node.type !== "ObjectExpression") return;
686
956
  for (const prop of objectProperties(node)) {
687
957
  const key = propKey(prop);
@@ -704,10 +974,7 @@ function collectDriverProtocols(
704
974
  // role: { dev, test, prod } or nested store: { sql: { prod } }
705
975
  const envVal = objectProp(value, env);
706
976
  if (envVal) {
707
- if (
708
- envVal.type === "Literal" &&
709
- typeof (envVal as Literal).value === "string"
710
- ) {
977
+ if (envVal.type === "Literal" && typeof (envVal as Literal).value === "string") {
711
978
  into.add((envVal as Literal).value as string);
712
979
  } else if (envVal.type === "ObjectExpression") {
713
980
  const driver = stringProp(envVal, "driver");
@@ -720,11 +987,7 @@ function collectDriverProtocols(
720
987
  }
721
988
  }
722
989
 
723
- function collectFlows(
724
- file: SourceFile,
725
- program: AstNode,
726
- scope: ProjectScope,
727
- ): void {
990
+ function collectFlows(file: SourceFile, program: AstNode, scope: ProjectScope): void {
728
991
  walk(program, (node) => {
729
992
  if (node.type !== "CallExpression") return;
730
993
  const call = node as CallExpression;
@@ -780,7 +1043,10 @@ function isOnFlowArgument(flowCall: CallExpression, program: AstNode): boolean {
780
1043
  }
781
1044
 
782
1045
  function unwrapFlowCall(node: AstNode): CallExpression | undefined {
783
- if (node.type === "CallExpression" && identifierName((node as CallExpression).callee) === "flow") {
1046
+ if (
1047
+ node.type === "CallExpression" &&
1048
+ identifierName((node as CallExpression).callee) === "flow"
1049
+ ) {
784
1050
  return node as CallExpression;
785
1051
  }
786
1052
  return undefined;
@@ -845,15 +1111,11 @@ function registerFlow(args: {
845
1111
  }
846
1112
  }
847
1113
 
848
- const trigger = args.triggerNode
849
- ? parseTrigger(args.triggerNode, args.scope)
850
- : undefined;
1114
+ const trigger = args.triggerNode ? parseTrigger(args.triggerNode, args.scope) : undefined;
851
1115
 
852
1116
  const gates = trigger?.gates;
853
1117
  const liveFromTrigger = trigger?.live;
854
- const manifestTrigger = trigger
855
- ? stripTriggerExtras(trigger.trigger)
856
- : undefined;
1118
+ const manifestTrigger = trigger ? stripTriggerExtras(trigger.trigger) : undefined;
857
1119
 
858
1120
  const flow: Flow = {};
859
1121
 
@@ -882,10 +1144,7 @@ function registerFlow(args: {
882
1144
  if (slo) flow.slo = slo;
883
1145
 
884
1146
  const stepsFromOpts = stringArrayProp(opts, "steps");
885
- const steps =
886
- stepsFromOpts && stepsFromOpts.length > 0
887
- ? stepsFromOpts
888
- : inferred.steps;
1147
+ const steps = stepsFromOpts && stepsFromOpts.length > 0 ? stepsFromOpts : inferred.steps;
889
1148
  if (steps.length > 0) flow.steps = steps;
890
1149
 
891
1150
  if (boolProp(opts, "nondeterministic") || inferred.nondeterministic) {
@@ -898,17 +1157,14 @@ function registerFlow(args: {
898
1157
  ...(numberProp(cost, "estimatePerCall") !== undefined
899
1158
  ? { estimatePerCall: numberProp(cost, "estimatePerCall") }
900
1159
  : {}),
901
- ...(numberProp(cost, "budget") !== undefined
902
- ? { budget: numberProp(cost, "budget") }
903
- : {}),
1160
+ ...(numberProp(cost, "budget") !== undefined ? { budget: numberProp(cost, "budget") } : {}),
904
1161
  };
905
1162
  } else if (inferred.nondeterministic) {
906
1163
  // Derive budget from the first ask prompt when present.
907
1164
  const ask = effects?.asks?.[0];
908
1165
  if (ask) {
909
1166
  const promptName = ask.split("@")[0]!;
910
- const promptBudget = args.scope.ai.prompts?.[promptName]?.budget
911
- ?.maxCostPerCall;
1167
+ const promptBudget = args.scope.ai.prompts?.[promptName]?.budget?.maxCostPerCall;
912
1168
  if (promptBudget !== undefined) {
913
1169
  flow.cost = {
914
1170
  estimatePerCall: Number((promptBudget * 0.55).toFixed(3)),
@@ -935,8 +1191,7 @@ function registerFlow(args: {
935
1191
  if (inferred.cacheIneligible) {
936
1192
  flow.cache = false;
937
1193
  } else {
938
- const cache = (opts &&
939
- objectProperties(opts).find((p) => propKey(p) === "cache")) as
1194
+ const cache = (opts && objectProperties(opts).find((p) => propKey(p) === "cache")) as
940
1195
  | AstNode
941
1196
  | undefined;
942
1197
  if (cache) {
@@ -1002,10 +1257,7 @@ function parseTrigger(node: AstNode, scope: ProjectScope): ParsedTrigger | undef
1002
1257
  return undefined;
1003
1258
  }
1004
1259
 
1005
- function parseHttpTrigger(
1006
- call: CallExpression,
1007
- scope: ProjectScope,
1008
- ): ParsedTrigger | undefined {
1260
+ function parseHttpTrigger(call: CallExpression, scope: ProjectScope): ParsedTrigger | undefined {
1009
1261
  // Walk the chain: http.METHOD(path).gate(...).live()
1010
1262
  let current: AstNode = call;
1011
1263
  let method: string | undefined;
@@ -1059,15 +1311,7 @@ function parseHttpTrigger(
1059
1311
 
1060
1312
  if (!method || !path) return undefined;
1061
1313
 
1062
- const httpMethods = [
1063
- "GET",
1064
- "POST",
1065
- "PUT",
1066
- "PATCH",
1067
- "DELETE",
1068
- "HEAD",
1069
- "OPTIONS",
1070
- ] as const;
1314
+ const httpMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] as const;
1071
1315
  if (!(httpMethods as readonly string[]).includes(method)) return undefined;
1072
1316
 
1073
1317
  return {
@@ -1082,9 +1326,7 @@ function parseHttpTrigger(
1082
1326
  };
1083
1327
  }
1084
1328
 
1085
- function parseCdcTrigger(
1086
- call: CallExpression,
1087
- ): Trigger["cdc"] | undefined {
1329
+ function parseCdcTrigger(call: CallExpression): Trigger["cdc"] | undefined {
1088
1330
  // *.changed("col") or *.changed()
1089
1331
  const callee = call.callee;
1090
1332
  if (callee.type !== "MemberExpression") return undefined;
@@ -1098,25 +1340,17 @@ function parseCdcTrigger(
1098
1340
  const inner = obj as CallExpression;
1099
1341
  const innerCallee = inner.callee;
1100
1342
  if (identifierName(innerCallee) === "table") {
1101
- const tableName =
1102
- stringArg(inner.arguments[0]) ?? identifierName(inner.arguments[0]);
1343
+ const tableName = stringArg(inner.arguments[0]) ?? identifierName(inner.arguments[0]);
1103
1344
  if (!tableName) return undefined;
1104
- return column
1105
- ? { table: tableName, column }
1106
- : { table: tableName };
1345
+ return column ? { table: tableName, column } : { table: tableName };
1107
1346
  }
1108
1347
  if (
1109
1348
  innerCallee.type === "MemberExpression" &&
1110
- identifierName(
1111
- (innerCallee as AstNode & { property: AstNode }).property,
1112
- ) === "table"
1349
+ identifierName((innerCallee as AstNode & { property: AstNode }).property) === "table"
1113
1350
  ) {
1114
- const tableName =
1115
- stringArg(inner.arguments[0]) ?? identifierName(inner.arguments[0]);
1351
+ const tableName = stringArg(inner.arguments[0]) ?? identifierName(inner.arguments[0]);
1116
1352
  if (!tableName) return undefined;
1117
- return column
1118
- ? { table: tableName, column }
1119
- : { table: tableName };
1353
+ return column ? { table: tableName, column } : { table: tableName };
1120
1354
  }
1121
1355
  }
1122
1356
  return undefined;
@@ -1146,9 +1380,7 @@ function parseEffectsObject(node: AstNode | undefined): Effects | undefined {
1146
1380
  return effects;
1147
1381
  }
1148
1382
 
1149
- function parseErrors(
1150
- node: AstNode | undefined,
1151
- ): Flow["errors"] | undefined {
1383
+ function parseErrors(node: AstNode | undefined): Flow["errors"] | undefined {
1152
1384
  if (!node) return undefined;
1153
1385
  if (node.type === "ObjectExpression") {
1154
1386
  const names: string[] = [];
@@ -1187,10 +1419,7 @@ function parseSlo(node: AstNode | undefined): Slo | undefined {
1187
1419
  };
1188
1420
  }
1189
1421
 
1190
- function schemaProp(
1191
- obj: AstNode | undefined,
1192
- key: string,
1193
- ): string | undefined {
1422
+ function schemaProp(obj: AstNode | undefined, key: string): string | undefined {
1194
1423
  const node = objectProp(obj, key);
1195
1424
  if (!node) return undefined;
1196
1425
  // Identifier / member schemas → opaque placeholder matching the spec excerpt.
@@ -1205,10 +1434,7 @@ function schemaProp(
1205
1434
 
1206
1435
  // ── AST helpers ────────────────────────────────────────────────────────────
1207
1436
 
1208
- function enclosingConstName(
1209
- call: CallExpression,
1210
- program: AstNode,
1211
- ): string | undefined {
1437
+ function enclosingConstName(call: CallExpression, program: AstNode): string | undefined {
1212
1438
  const targetStart = call.start;
1213
1439
  if (targetStart === undefined) return undefined;
1214
1440
  let found: string | undefined;
@@ -1239,10 +1465,7 @@ function objectArg(node: AstNode | undefined): AstNode | undefined {
1239
1465
  return undefined;
1240
1466
  }
1241
1467
 
1242
- function objectProp(
1243
- obj: AstNode | undefined,
1244
- key: string,
1245
- ): AstNode | undefined {
1468
+ function objectProp(obj: AstNode | undefined, key: string): AstNode | undefined {
1246
1469
  if (!obj || obj.type !== "ObjectExpression") return undefined;
1247
1470
  for (const prop of objectProperties(obj)) {
1248
1471
  if (propKey(prop) === key) {
@@ -1253,9 +1476,9 @@ function objectProp(
1253
1476
  }
1254
1477
 
1255
1478
  function objectProperties(obj: AstNode): AstNode[] {
1256
- return (
1257
- (obj as AstNode & { properties?: AstNode[] }).properties ?? []
1258
- ).filter((p) => p.type === "Property" || p.type === "ObjectProperty");
1479
+ return ((obj as AstNode & { properties?: AstNode[] }).properties ?? []).filter(
1480
+ (p) => p.type === "Property" || p.type === "ObjectProperty",
1481
+ );
1259
1482
  }
1260
1483
 
1261
1484
  function propKey(prop: AstNode): string | undefined {
@@ -1268,50 +1491,33 @@ function propKey(prop: AstNode): string | undefined {
1268
1491
  return undefined;
1269
1492
  }
1270
1493
 
1271
- function stringProp(
1272
- obj: AstNode | undefined,
1273
- key: string,
1274
- ): string | undefined {
1494
+ function stringProp(obj: AstNode | undefined, key: string): string | undefined {
1275
1495
  return stringArg(objectProp(obj, key));
1276
1496
  }
1277
1497
 
1278
- function numberProp(
1279
- obj: AstNode | undefined,
1280
- key: string,
1281
- ): number | undefined {
1498
+ function numberProp(obj: AstNode | undefined, key: string): number | undefined {
1282
1499
  const node = objectProp(obj, key);
1283
1500
  if (!node || node.type !== "Literal") return undefined;
1284
1501
  const v = (node as Literal).value;
1285
1502
  return typeof v === "number" ? v : undefined;
1286
1503
  }
1287
1504
 
1288
- function boolProp(
1289
- obj: AstNode | undefined,
1290
- key: string,
1291
- ): boolean | undefined {
1505
+ function boolProp(obj: AstNode | undefined, key: string): boolean | undefined {
1292
1506
  const node = objectProp(obj, key);
1293
1507
  if (!node || node.type !== "Literal") return undefined;
1294
1508
  const v = (node as Literal).value;
1295
1509
  return typeof v === "boolean" ? v : undefined;
1296
1510
  }
1297
1511
 
1298
- function stringArrayProp(
1299
- obj: AstNode | undefined,
1300
- key: string,
1301
- ): string[] | undefined {
1512
+ function stringArrayProp(obj: AstNode | undefined, key: string): string[] | undefined {
1302
1513
  const node = objectProp(obj, key);
1303
1514
  if (!node || node.type !== "ArrayExpression") return undefined;
1304
1515
  const els = (node as AstNode & { elements?: AstNode[] }).elements ?? [];
1305
- const out = els
1306
- .map((el) => stringArg(el))
1307
- .filter((x): x is string => typeof x === "string");
1516
+ const out = els.map((el) => stringArg(el)).filter((x): x is string => typeof x === "string");
1308
1517
  return out;
1309
1518
  }
1310
1519
 
1311
- function arrayProp(
1312
- obj: AstNode | undefined,
1313
- key: string,
1314
- ): AstNode[] | undefined {
1520
+ function arrayProp(obj: AstNode | undefined, key: string): AstNode[] | undefined {
1315
1521
  const node = objectProp(obj, key);
1316
1522
  if (!node || node.type !== "ArrayExpression") return undefined;
1317
1523
  return ((node as AstNode & { elements?: AstNode[] }).elements ?? []).filter(